ar_mailer 1.5.0

Eric Hodel | Fri, 16 Oct 2009 00:02:05 GMT

ar_mailer is a two-phase delivery agent for ActionMailer. Even delivering email to the local machine may take too long when you have to send hundreds of messages. ar_mailer allows you to store messages into the database for later delivery by a separate process, ar_sendmail.

Changes

  • Added --pid-file option. See --help for details.
  • Minor improvements to --help, argument error output

Posted in , ,  | 2 comments

dnssd 1.3.1

Eric Hodel | Wed, 14 Oct 2009 03:05:58 GMT

dnssd version 1.3.1 has been released!

DNS Service Discovery (aka Bonjour, MDNS) API for Ruby. Implements browsing, resolving, registration and domain enumeration. Supports avahi's DNSSD compatibility layer for avahi 0.6.25 or newer.

Changes

  • 1 bug fix
    • Fix regexp for parsing the fullname. Patch by James Sadler.

Posted in ,  | no comments

Example domains and IPs

Eric Hodel | Fri, 02 Oct 2009 21:58:28 GMT

I'm always forgetting this information. Here's a handy reference for example domain names, example IPv4 addresses and example IPv6 addresses.

Example Domain names

From RFC 2606:

.test
For testing DNS related code
example
example.com
example.net
example.org
For documentation or as examples

IPv4 Addresses

From RFC 3330:

192.0.2.0/24 is used for documentation and example code.

198.18.0.0/15 is used for benchmark tests of network interconnect devices. See RFC 2544

IPv6 Addresses

From RFC 3849:

2001:DB8::/32 is used for documentation and examples.

Posted in  | no comments

multipart emails with multipart layouts and inline images in ActionMailer

Eric Hodel | Wed, 30 Sep 2009 20:14:44 GMT

While ActionMailer claims to support multipart emails, that support is severely limited.

If you have a multipart/alternative email and are using layouts, multipart layouts don't work, the text/html layout will be used for the text/plain alternative part.

You also can't embed an image to use via <img src="cid:blah"> (or CSS, etc.) in the text/html section of your multipart/alternative mail. (I want the image to be controlled via the img element and styles, not plopped into the middle of my HTML section.)

While this ticket was closed due to lack of a test, the fix involves the modification of a constant which wouldn't be tested anyhow. (It seems nobody bothered to inform TMail about this problem either.)

Enough griping!

I managed to find a solution to these two problems!

To take care of the first problem I overrode initialize_template_class in my mailer class and manually set the template format for the part:

  def initialize_template_class(assigns)
    template_format = assigns.delete :template_format

    template = super

    template.template_format = template_format if template_format

    template
  end

Then wrote a render_multipart method that manually chooses the template and layout to render:

  def render_multipart(template, options)
    content_type 'multipart/alternative'

    part :content_type => 'multipart/related' do |related|
      html_options = { :template_format => :html }.merge options

      related.part :content_type => 'text/html',
                   :body => render(:file => "#{template}.text.html.haml",
                                   :layout => 'notifier_mailer.text.html',
                                   :body => html_options)

      related.part(:content_type => 'image/png',
                   :headers => { 'Content-Id' => 'site_logo.png' }) do |image|
        image.body = File.read LOGO_PATH
        image.transfer_encoding = 'base64'
        image.content_disposition = nil
      end

    end

    plain_options = { :template_format => :plain }.merge options

    part :content_type => 'text/plain',
         :body => render(:file => "#{template}.text.plain.erb",
                         :layout => 'notifier_mailer.text.plain',
                         :body => plain_options)
  end

For Apple mail to properly display a multipart/alternative mail the top-level MIME type needs to be multipart/alternative. The text/html alternative part is wrapped inside a multipart/related part that also contains the embedded image. (When viewing the mail as text/plain the image attachment doesn't appear on the attachments bar. When viewing as text/html it does, but I don't think that there's a way to hide it from the attachments bar.)

Fixing this For Good™ seems to involve teaching ActionPack's views about how to read MIME extensions (reset.text.plain.erb and reset.text.html.erb) instead of filetype extensions (show.html.erb and show.xml.erb) like what is used by ActionController's views. The ticket seems to indicate somebody's trying to figure out how to do that.

To take care of the embedding of an image in an email for use in the HTML part I removed the restriction on Content-Id for a part header from the patch to TMail:

TMail::HeaderField::FNAME_TO_CLASS.delete 'content-id'

Then set the Content-Id for the image and unset the Content-Disposition (copied from multipart_render above):

related.part(:content_type => 'image/png',
             :headers => { 'Content-Id' => 'site_logo.png' }) do |image|
  image.body = File.read LOGO_PATH
  image.transfer_encoding = 'base64'
  image.content_disposition = nil
end

Leaving the Content-Disposition as inline causes the image to show up once at the bottom of the HTML content and once where the <img> is present.

From a mailer method this is called with the explicit template name (I was too lazy to intuit from caller):

  def reset(user, password)
    recipients user.email
    from       REPLY_TO
    subject    "Password reset"

    render_multipart 'reset', :password => password, :recipient => user
  end

In app/views/layouts/notifier_mailer.text.html.haml I have the following snippet to use the site logo:

%img.logo{ :src => 'cid:site_logo.png' }

Posted in  | no comments

imap_to_rss 1.1

Eric Hodel | Tue, 01 Sep 2009 22:47:00 GMT

imap_to_rss version 1.1 has been released!

IMAPToRSS turns messages on an IMAP server into RSS entries when the match a handler. Included handlers work for email from Amazon, HSBC and UPS. IMAPToRSS automatically loads handlers for any other mail.

Changes:

  • 1 major enhancement
    • Added iTunes handler
  • 1 bug fix
    • Fixed various bugs in the Amazon handler

Posted in ,  | no comments

rubygems-isit19 1.0

Eric Hodel | Thu, 20 Aug 2009 05:26:00 GMT

Lets you figure out if your gems and gems you install might work on 1.9. Uses isitruby19.com as its datastore. Be sure to update the website with your experiences!

Features

  • gem isit19 for checking your installed gems
  • gem install plugin that tells you if your installed gem works on 1.9

Synopsis

$ gem install ZenTest

ZenTest 4.1.4 might work, 100% say 4.1.3 works on 1.9
Update http://isitruby19.com/ZenTest with your experiences!

Successfully installed ZenTest-4.1.4
1 gem installed

Posted in ,  | no comments

Service Discovery Protocols

Eric Hodel | Wed, 19 Aug 2009 21:42:03 GMT

I’ve worked on two different service discovery libraries for ruby, one for SSPD and one for mDNS, and I’d like to share the differences between them with you.

Both SSDP and mDNS are designed to be used with self-configuring networks and to automatically discover and connect to other devices. Both use multicast to advertise and search.

SSPD

The Simple Service Discovery Protocol is used primarily by UPnP for network devices to discover each other. UPnP is designed for consumer electronics and appliances that need to communicate with each other automatically.

The description of SSPD fits on a twelve of pages when written in (relatively) plain English (section 1 in the UPNP Device Architecture). The protocol itself looks much like HTTP and can be implemented largely using simple text processing. (Strangely, SOAP is the higher-level communication layer for UPnP.)

Unfortunately there’s no way for multiple devices to share the same SSPD socket without being designed to work with each other. Since UPnP is focused on simple devices, this fits well with their design goals. Most UPnP devices are routers, media servers, game consoles, televisions, etc. If you built your own router and media server on the same host it will probably be impossible for both devices to show up on the network, nor will you be able to run a media server and media viewer on the same host without the two being designed to work with each other.

mDNS

Multicast DNS is used primarily by Apple and (basically) has a DNS server on each host. Hosts speak the DNS protocol to each other over multicast and have a central daemon that handles communication on the network much like the libc DNS resolver.

Even though there’s plenty of DNS clients around for code reuse, implementing a DNS client form scratch is a large undertaking (my SSDP implementation is around 750 lines with documentation, ruby’s Resolv DNS resolver is around 2200 lines and doesn’t include a server or multicast code). The entirety of the DNS system is spread across several RFCs, not including the documentation for mDNS itself.

Fortunately mDNS libraries provide a daemon for handling network communication and a client library you can use to register and browse for devices (the dnssd gem uses Apple’s DNS Service Discovery API). mDNS is focused on computers rather than devices, so multiple services can all coexist on the same host.

Unfortunately, due to the complexity of mDNS, there’s no single API for applications to use. Apple’s DNS-SD works across multiple platforms including windows. For unix-like operating systems there is also avahi which has a limited dnssd compatibility shim.

Posted in  | no comments

dnssd 1.3

Eric Hodel | Wed, 19 Aug 2009 04:31:00 GMT

DNS Service Discovery (aka Bonjour, MDNS) API for Ruby. Implements browsing, resolving, registration and domain enumeration. Supports avahi’s DNSSD compatibility layer for avahi 0.6.25 or newer.

Changes

  • 4 major enhancements
    • Added DNSSD::Service.get_property
    • Added DNSSD::Service#getaddrinfo
    • Added DNSSD::Service#add_record
    • Added DNSSD::Service#query_record
  • 5 minor enhancements
    • DNSSD::Reply#connect now uses DNSSD::Service#getaddrinfo (faster)
    • DNSSD::Service#register behaves properly when blockless
    • Broke up DNSSD::Reply into specific subclasses
    • Added sample/query_record.rb
    • sample/*.rb work with each other now for clarity of implementation
  • 2 bug fixes
    • Fix hierarchical domains like \.mac\.name.members.mac.com.
    • Fix compilation against avahi 0.6.25

Posted in ,  | no comments

On Ruby C Extensions

Eric Hodel | Tue, 18 Aug 2009 02:07:40 GMT

I’m not a great C coder, but I have implemented, cleaned up and read a few C extensions for Ruby and I’ve got some tips for writing C extensions

C is Bad

The C itself is your biggest enemy when writing C extensions. The more you write the harder it is for you to maintain and the harder it will be for anyone else to improve or fix your code in the future. The closer your C extension code is to the library’s API the better you can use Ruby to glue all the pieces together into a friendly interface.

Ruby is Good

With less C code and more Ruby code you end up a library that’s easier to refactor, adapt and extend. Generic data manipulation, simple math, convenience functions, etc. are all easier to write, test and debug when written in Ruby. There’s no need to recompile between changes, worry about compiler warnings, or fix type errors.

If you’re returning results from an operation, do as much of the work in Ruby as possible. If there are objects that can be created in Ruby create them in Ruby. If you get a struct sockaddr, unpack it on the Ruby side rather than calling the Socket methods from C using rb_funcall. If you’ve got an abstract representation of a flag bitfield, create the object on the Ruby side rather than calling rb_class_new_instance.

Check for what you need

mkmf.rb has loads of functions (dir_config, have_library, have_macro, have_func, have_type, have_struct_member, etc.) for determining whether or not you have everything you need to build your extension. When using these functions be sure to check the result and fail when what you need is missing.

Keep Up-to-date on the Ruby/C API

There’s tons of functions and macros for doing most of you need already built-in to Ruby. README.EXT has the overview, but there’s also ruby.h and intern.h which give you a list of functions you can use.

Use the friendly macros RSTRING_LEN, RSTRING_PTR and friends when playing with String, Array, etc. Use rb_define_alloc_func() with Data_Wrap_Struct(). Use RTEST and NIL_P for checking ruby results. Convert numbers with NUM2ULONG or NUM2INT, etc.

Play Nice with Threads

If you’re performing a blocking IO operation use rb_thread_wait_fd, rb_thread_fd_writable, etc. to keep other threads running. rb_thread_wait_for can be used for polling.

Don’t Repeat Yourself

While it is safe to repeatedly call rb_intern() if you have to invoke rb_funcall or rb_iv_set, it’s easier and prettier to use a static variable set from your Init function. Same for looking up classes using rb_path2class() or rb_define_class_under().

C is bad

It’s worth repeating. Writing C code is hard. Refactoring C code is hard. Debugging C code is hard. Do as little as possible in C and you’ll thank yourself down the road.

Posted in  | 2 comments

dnssd 1.2

Eric Hodel | Thu, 13 Aug 2009 05:45:00 GMT

DNS Service Discovery (aka Bonjour, MDNS) API for Ruby. Implements browsing, resolving, registration and domain enumeration.

Changes:

  • 4 major enhancements
    • DNSSD::Service is now directly instantiable
    • DNSSD.announce which registers a server socket you’ve created
    • DNSSD::Reply.connect which connects to a browsed service
    • Fix asynchronous service shutdown crash
  • 8 minor enhancements
    • DNSSD.resolve now optionally accepts a DNSSD::Reply from DNSSD.browse
    • Use rb_thread_wait_fd instead of custom rb_thread_select code
    • DNSSD::Reply#protocol and DNSSD::Reply#service_name
    • Added missing error classes
    • Added missing InterfaceUnicast constant
    • Improved Documentation
    • Use C constants in ext/dnssd/errors.c
    • Reduced C code in ext/dnssd/service.c for greater control. See DNSSD::Service
  • 4 bug fixes
    • Don’t invoke block on callback if none was provided
    • Remove ext/dnssd/dns_sd.h so the correct header is used
    • DNSSD::NoMemoryError is now raised instead of NoMemError
    • DNSSD::ReferenceUsedError is now correctly named DNSSD::RefusedError

Posted in  | no comments

Older posts: 1 2 3 ... 25