Technical Development

Rails URL Redirect

redirect_to :overwrite_params => {:controller=>’survey’, :action=>’handle_single_scan’}

Here’s a little trick I discovered recently.

I wanted to rewrite a url, forwarding a request to another controller, with all the other parameters intact. Rather than define each individual parameter, I found that rails has a built in API to preserve the existing URL with just a few fields specified.

For instance, say I had a request going to http://www.mskynet.com/foo/log?id=123&param=456, and I wanted to redirect to http://www.mskynet.com/bar/do_work?id=123&param=456, I could do the following:

redirect_to :overwrite_params => {:controller=>’bar’, :action=>’do_work’}

This syntax would let you overwrite any of the parameters that you want to change and keep everything else the same.

If I just wanted to overwrite the parameter ‘id’ but keep the controller and action, I could do:

redirect_to :overwrite_params => {:id=>’234′}

Check out the :overwrite_params option here: http://api.rubyonrails.org/classes/ActionController/Base.html#M000649


Actionmailer with BackgroundRb

We have been working on a mailing service for a while now and we really like what we can do with html emails. Unfortunately, with the plethora of smart phones these days, HTML support is inconsistent. We figured that a good stop-gap solution was to offer both HTML and text versions of an email. Preferably in one package.

And that’s where multipart emails come in.

Suprisingly (or not), this was rather easy to implement in Rails 2.3.

There are two ways of doing this. Implicit and explicit multipart emails. I decided to go with the former.

All I had to do was to make sure the content type is set correctly:

content_type    “multipart/alternative”

Then, I just had to make sure my template file ends with the right signature “foo.text.plain.erb” and “foo.text.html.erb” for plain text and html emails respectively.

See the multipart email section in the official doc here: http://api.rubyonrails.org/classes/ActionMailer/Base.html

I used a controller to trigger the email and it all worked just like a charm. I had my email with both text and html components. I checked in the code, fired up my mailer service which runs in backgroundrb and called it a day.

That’s when I saw this error popping up:

Message:can’t convert nil into String
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:551:in `quote’
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:551:in `read_multipart’
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:540:in `parse_body_0′
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:522:in `parse_body’
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:476:in `body=’
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/base.rb:650:in `create_mail’

can’t convert nil into String

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:551:in `quote’

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:551:in `read_multipart’

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:540:in `parse_body_0′

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:522:in `parse_body’

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:476:in `body=’

/usr/lib/ruby/gems/1.8/gems/actionmailer-2.3.2/lib/action_mailer/base.rb:650:in `create_mail’

After some investigation, I figure that this is one of those things where backgroundrb is incompatible with a certain feature of rails. I considered fudging around with the tmail code but gave up on that endeavor quickly. Instead, I decided to work around with an explicit version of the multipart email. To my delight, that worked perfectly.

content_type “multipart/alternative”

part :content_type => “text/plain”,
:body => render_message(”foo.text.plain”, :variable1=>variable1, :variable2 => variable2)
part :content_type => “text/html”,
:body => render_message(”foo.text.html”, :variable1=>variable1, :variable2 => variable2)

Be sure to declare text/plain and text/html in that order. Email clients like Gmail may not offer the text alternative or show the HTML version automatically if you change that order.

The template files work as-is without any modifications. Well, crisis averted. Back to making some barcodes. :)


Mongrel and Snow Leopard

So the ongoing saga of upgrade woes continues… You would think that “sudo gem update” should take care of upgrading all my old gems in Snow Leopard with all its 64-bit glory…

Nope.

It started when “ruby script/server” started WebBrick instead of Mongrel. That’s odd, but I figure I could work around it by just running “sudo gem install mongrel” again.

Unfortunately, that didn’t do the trick. So I tried summoning Mongrel directly:

mongrel_rails start

and I got this in return:

/Users/…/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle: dlopen(/Users/…/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle, 9): no suitable image found.  Did find: (LoadError)

/Users/…/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle: no matching architecture in universal wrapper – /Users/…/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle

from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require’

Luckily, Google saves the day and I found this: http://stackoverflow.com/questions/1350486/ruby-on-rails-staring-mongrel-server

I ran the following commands and it all worked as advertised. Phew, that one was easy.

sudo gem uninstall mongrel

sudo gem uninstall fastthread

sudo gem install mongrel

/Users/yowhan/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle: dlopen(/Users/yowhan/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle, 9): no suitable image found.  Did find: (LoadError)
/Users/yowhan/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle: no matching architecture in universal wrapper – /Users/yowhan/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/http11.bundle
from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require’
from /Users/yowhan/.gem/ruby/1.8/gems/mongrel-1.1.5/bin/../lib/mongrel.rb:12
from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `gem_original_requir

MySQL, Rails and Snow Leopard

Ruby on Rails

After much hesitation, I took the dive and upgraded to Snow Leopard today. I ran into some ruby and MySQL errors, but other early adopters in our dev team had already prepared me for that. However, I would soon run into an issue that got me stumped. It turned out that I had forgotten something really simple.

So I had reinstall MySQL 64-bit and updated all my gems, but a particular mysql gem would refuse to reinstall.

There was this helpful document that several devs on my team sent me: http://stackoverflow.com/questions/991708/rails-mysql-and-snow-leopard

Unfortunately, I would get the following error even executing the recommended solution:

yowhan:~ $ sudo env ARCHFLAGS=”-arch x86_64″ gem install -V  mysql — –with-mysql-config=/usr/local/mysql/bin/mysql_config
GET 200 OK: http://gems.rubyforge.org/latest_specs.4.8.gz
GET 200 OK: http://gems.github.com/latest_specs.4.8.gz
GET 200 OK: http://gems.rubyforge.org/quick/Marshal.4.8/mysql-2.8.1.gemspec.rz
Installing gem mysql-2.8.1
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/COPYING
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/COPYING.ja
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/History.txt
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/Manifest.txt
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/README.txt
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/Rakefile
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/ext/mysql_api/extconf.rb
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/ext/mysql_api/mysql.c
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/extra/README.html
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/extra/README_ja.html
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/extra/tommy.css
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/lib/mysql.rb
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/tasks/gem.rake
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/tasks/native.rake
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/tasks/vendor_mysql.rake
/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/test/test_mysql.rb
Building native extensions.  This could take a while…
ERROR:  Error installing mysql:
ERROR: Failed to build gem native extension.
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb
checking for mysql_query() in -lmysqlclient… no
checking for main() in -lm… yes
checking for mysql_query() in -lmysqlclient… no
checking for main() in -lz… yes
checking for mysql_query() in -lmysqlclient… no
checking for main() in -lsocket… no
checking for mysql_query() in -lmysqlclient… no
checking for main() in -lnsl… no
checking for mysql_query() in -lmysqlclient… no
checking for main() in -lmygcc… no
checking for mysql_query() in -lmysqlclient… no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.
Provided configuration options:
–with-opt-dir
–without-opt-dir
–with-opt-include
–without-opt-include=${opt-dir}/include
–with-opt-lib
–without-opt-lib=${opt-dir}/lib
–with-make-prog
–without-make-prog
–srcdir=.
–curdir
–ruby=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
–with-mysql-config
–without-mysql-config
–with-mysql-dir
–without-mysql-dir
–with-mysql-include
–without-mysql-include=${mysql-dir}/include
–with-mysql-lib
–without-mysql-lib=${mysql-dir}/lib
–with-mysqlclientlib
–without-mysqlclientlib
–with-mlib
–without-mlib
–with-mysqlclientlib
–without-mysqlclientlib
–with-zlib
–without-zlib
–with-mysqlclientlib
–without-mysqlclientlib
–with-socketlib
–without-socketlib
–with-mysqlclientlib
–without-mysqlclientlib
–with-nsllib
–without-nsllib
–with-mysqlclientlib
–without-mysqlclientlib
–with-mygcclib
–without-mygcclib
–with-mysqlclientlib
–without-mysqlclientlib
Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/mysql-2.8.1 for inspection.

Results logged to /Library/Ruby/Gems/1.8/gems/mysql-2.8.1/ext/mysql_api/gem_make.out

sudo env ARCHFLAGS=”-arch x86_64″ gem install -V  mysql — –with-mysql-config=/usr/local/mysql/bin/mysql_config

GET 200 OK: http://gems.rubyforge.org/latest_specs.4.8.gz

GET 200 OK: http://gems.github.com/latest_specs.4.8.gz

GET 200 OK: http://gems.rubyforge.org/quick/Marshal.4.8/mysql-2.8.1.gemspec.rz

Installing gem mysql-2.8.1

/Library/Ruby/Gems/1.8/gems/mysql-2.8.1/test/test_mysql.rb

Building native extensions.  This could take a while…

ERROR:  Error installing mysql:

ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb

*** extconf.rb failed ***

Could not create Makefile due to some reason, probably lack of

necessary libraries and/or headers.  Check the mkmf.log file for more

details.  You may need configuration options.

Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/mysql-2.8.1 for inspection.

Results logged to /Library/Ruby/Gems/1.8/gems/mysql-2.8.1/ext/mysql_api/gem_make.out

After several frustrating moments of poking around, a helpful fellow developer pointed out that “mysql” was not in my path. Duh!

I guess I just committed a newbie linux mistake. A quick update to .profile to include “/usr/local/mysql/bin” later, and the above command works perfectly now!

Here’s what I added to my .profile:

#Add mysql to path

export PATH=/usr/local/mysql/bin:$PATH

Hopefully that’s the last of my upgrade issues. :)


Webistrano and EC2

Our Deployment

When we first deployed our system we had a very simple Web->Database server architecture:  we had one web server talking to a single database server deployed with Webistrano.  As the site grew we quickly outgrew this setup.  We decided to build a dynamically scaling system on top of Amazon EC2 to make sure we would never have to worry about adding or removing hardware.

Our EC2 system will spin up and spin down web servers based on a set of metrics that we collect on a continuous basis.  This presents some fairly interesting challenges with Webistrano.  While Webistrano provides a great set of features it’s obviously designed for deploying to a fixed set of servers.

We looked at a variety of options for deployment but we wanted to stick with Webistrano for the following reasons:

  1. Convenient central store for recipes
  2. Easy integration into our source control system
  3. Tracked and repeatable deployments

We are also firm believers in not reinventing the wheel, so how did we work around the static nature of Webistrano?  We leveraged the power of open source :)  We added a RESTful API call to Webistrano that allows a remote host to add or remove an ip address to a Webistrano deployment stage.

Each instance of our application has a backgroundrb task that hits this web service every minute.  Consequently the list of active servers is always up to date.  Not only that, but when our images start up they will deploy the latest released version of the code.  How do we deal with servers that die?  We added a Webistrano backgroundrb job that that removes hosts that have not been updated within the last five minutes.

What does all this mean?  Our deployments always go to our currently active servers and our system scales on demand.


Copyright © 1996-2010 MSKYNET BLOG. All rights reserved.
iDream theme by Templates Next | Powered by WordPress