Configuration for Rails, the Right Way

I still see people promoting various gems and plugins to handle miscellaneous configuration elements for your application. One little known secret is that Rails 3 allows you to define your own configuration elements trivially.

Create a new gemset with RVM

To use different Ruby versions on your computer you can use the Ruby Version Manager. It also allows you to pack different gemsets for each of your applications. In order to create on of those just type ...

rvm use 1.8.7-p323@makandra_project_xy --create

This command creates and switches to the newly created gemset.

Excel files can't hold more than 65535 rows per worksheet

"Classic" Excel (XLS) has a magic limit of 65535 rows per worksheet. This was fixed in XLSX but the version used by the Spreadsheet gem can only write XLS files.

If you want to write more data, consider hacking around it using in_groups_of and writing multiple column sets of your data (i.e. split 1 block of 3 columns and 100000 rows into 4 blocks of 3 (12 in total) columns and 25000 rows).

You can do this until you reach the other limit of 256 columns. After that, open up a new workbook or consider a ...

Find geocoded records that are close to a location (radius search)

When you have objects in your database that hold latitude and longitude and you want to find others that are close to given coordinates you can use the Graticule gem.

Graticule

Graticule offers several methods to compute the distance between two geo-dated objects but fetching records from the database that are within a given radius of a location is a bit trickier:

def close_destinations(latitude, longitude)
  distance_sql = Graticule::Distance::Spherical.to_sql(:latitude => l...

Be careful with "memoize"

ActiveSupport's memoize has a dangerous feature you might not know about.

Assume you have

class DeepThought
  extend ActiveSupport::Memoizable

  def life_universe_and_everything
     # some lengthy calculation returning 42
  end
  memoize :life_universe_and_everything
end

Then #life_universe_and_everything will of course cache the result after calculating it once. However, you can trigger a recalculation by calling #life_universe_and_everything(true).

If, however, your method looks like

de...

Ruby: Downloading files from the Internet

This is easy:

require 'open-uri'

File.open('/target/path/to/downloaded.file', "wb") do |file|
  file.write open('http://example.com/your.file').read
end

Basic Authentication

When your file is protected by HTTP Basic Authentication, pass your credentials as hash:

File.open('/target/path/to/downloaded.file', "wb") do |file|
  file.write open('http://example.com/your.file', :http_basic_authentication => [your_username, your_password]).read
end

Multi-line Ruby block in Haml

There are several options, but most of them are impractical. The best way is to use the :ruby filter:

:ruby
  puts javascript_include_tag(
    'lib/jquery-1.6.1.min.js',
    'lib/jquery-rails-ujs.js',
    'lib/jquery-ui-1.8.13.custom.min.js',
    'lib/jquery.ui.datepicker-de.js',
    'lib/jquery-ui-timepicker-addon.min.js',
    'lib/jquery.tools.min.js',
    'application.js',
    'google-maps.js',
    :cache => true
  )

...

"Show me the page" fails to open a browser window

If you get an error like this:

Unable to launch /home/bruce/Projects/myproject/tmp/capybara/capybara-201110311210111407691101.html 

... update your launchy gem. It failed for us in version 0.4.x. We could fix the issue by upgrading to 2.0.5.

Rails 3.1 error message: Could not find a JavaScript runtime

After starting the Rails server in a freshly generated Rails 3.1 project you could see an error message such as

/usr/lib/ruby/gems/1.8/gems/execjs-1.3.0/lib/execjs/runtimes.rb:50:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

Just add a JavaScript runtime to your Gemfile and the error vanishes.

Examples:

gem 'therubyracer'
gem 'extjs'

MySQL: Do not use "WHERE id IN (SELECT ....)"

Note: This applies specifically to MySQL. In PostgreSQL for example, this is not an issue.

If you care about performance, never use a query like

UPDATE users SET has_message = 1 WHERE users.id IN (SELECT user_id FROM messages)

MySQL does not optimize this and seems to scan the temporary table, which isn't indexed, for every row in the update statement. This applies to other statements than UPDATE as well.

Instead, either use a JOIN like

UPDATE users INNER JOIN messages ON messages.user_id = users.id SET has_message =...

How the Clearance gem remembers and clears sessions

Clearance is a gem that provides authentication functionality (e.g. login, logout). This note explains, how the clearance login, logout and (in old Clearances) remember me functionality works.

Login

Clearance defines a database column called "remember_token". When you login in, that token will be saved in a cookie. For that reason you don't have to re-sign-in when you close and open the browser again.
This also means that you can be logged in in more than a single browser. Also see [When ses...

How to use html_safe correctly

By default, Rails views escape HTML in any strings you insert. If you want to insert HTML verbatim, you need to call #html_safe. However, #html_safe does not "unescape" a string. It merely marks a string as safe for unescaped insertion.

How html_safe works

Calling html_safe on a String returns a new object that looks and acts like a String, but actually is a ActiveSupport::SafeBuffer:

"foo".length
# => 3
"foo".class
# => String

"foo".html_safe.length
# => 3
"foo".html_safe.class
# => ActiveSupport::S...

Cucumber.yml was found, but could not be parsed.

If you encounter the error message above when running cucumber, just execute...
rm rerun.txt
...in the Rails directory.

Or run...
tests
...from the geordi gem. This will do the work for you automatically.

Ruby 2.0 Implementation Work Begins: What is Ruby 2.0 and What’s New?

While 2.0 will include a number of syntax changes, new features and general improvements, mentioned below, it is anticipated to remain backward compatible with code written for 1.9.3 and Matz has stated that the changes are less significant than those made in the 1.8 to 1.9 jump.

Javascript equivalent of Ruby's array.collect(&:method)

The most common use case for Ruby's #collect is to call a method on each list element and collect the return values in a new array:

['hello', 'world', 'this', 'is', 'nice'].collect(&:length)
# => [5, 5, 4, 2, 4]

Although there is no equivalent to this idiom in naked Javascript, there is a way to collect object properties (but not method results) if you are using common Javascript libraries.

If you are using jQuery with the Underscore.js utility library, you can use [pluck](htt...

Monitor a Rake task with God

In order to monitor a Rake task using God your Rake file must write a file with its process ID (PID) to a path determined by God. This way God can check whether the Rake process is still alive.

Here is how to do this: In your God config, call the Rake task with an environment variable PIDFILE. This variable should equal the PID file path desired by God:

God.watch do |w|
  w.dir = "#{rails_root}"
  w.name = "my_task"
  w.interval = 10.seconds
  w.pid_file = "#{rails_root}/tmp/pids/#{w.name}...

Mailcatcher: An alternative to inaction_mailer

Looks simpler than inaction_mailer:

gem install mailcatcher
mailcatcher

Setup Rails to send mails to 127.0.0.1:1025. Usually you want the following config in config/environments/development.rb and maybe in test.rb or cucumber.rb.

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address => 'localhost',
  :port => 1025
}

Now you can see sent mails in your browser when opening http://127.0.0.1:1080

Note: In order to s...

Start Rails console or server with debugger

When you require the Ruby debugger to be available from your Rails console (e.g. you want to inspect a method's magic), you need to enable it explicitly:
script/console --debugger

If you cannot access local variables etc, see this card.

WEBrick

For WEBrick, enable it similarly:
script/server --debugger

Properly adding fields with default values to a model

When adding a new field to your model's database table, don't set any defaults in the database.

It makes people wonder why they get such values when reading attributes.\
Why? Because nobody looks at the database layout since such things are part of your application's logic -- and thus they belong into the corresponding model.

How to

Do it like this:

  • In your migration, after adding the field, update all fields to your desired default:

    update "UPDATE users SET locked = #{quoted_false};"
    
  • In your model, set a defau...

Install the Oniguruma gem

Oniguruma is an advanced regular expression engine for Ruby.

Install Oniguruma with binary dependencies like this:

sudo apt-get install libonig2 libonig-dev
sudo gem install oniguruma

On the Mac do:

brew install oniguruma
sudo gem install oniguruma

MySQL: For each group, retrieve a comma-separated list of values in a given column

The technique described in this card has an important caveat: The result of GROUP_CONCAT is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. This will cause horrible, data-destroying bugs in production. For this reason you should probably not use GROUP_CONCAT ever. At least you must set the value of group_concat_max_len to an insanely high value on every database server your application runs on.


Lik...

Using heredoc for prettier Ruby code

You can use heredoc to avoid endlessly long lines of code that nobody can read. Heredoc strings preserve linebreaks and can be used like this:

def long_message
  puts(<<-EOT)
    Here goes a very long message...
    Sincerely,
    foobear
  EOT
end

<<-EOT will be somewhat of a placeholder: anything you write in the line after you used it will be its value until you write EOT in a single line.

You can use any string to flag your heredocs. To be more verbose you...

How to fix: Gems are unavailable although they are installed

  • If Rails or Rake are complaining about a missing gem that is listed in your Gemfile.lock and the listed version is properly installed, something is seriously wrong and needs to be fixed.
  • If you accidently executed bundle install some_gem although you wanted bundle update some_gem

What is wrong

Let's say your Gemfile asks for some-gem which you can see when running gem list but bundle show some-gem just gives you an error:

Could not find gem 'some-gem', in any of the sources

Another indicator: Doing a `...

Use a Ruby method like a block or lambda

Sometimes you want to use a vanilla Ruby method like a block. You can use Object#method to obtain a method reference that responds to #call:

foo_plus = "foo".method(:+)
foo_plus.call("bar") # => "foobar"

The method reference also understands #to_proc so you can feed it to block-taking methods by prefixing it with &:

printer = method(:puts)
[1, 2, 3].each(&printer) # will print one line per number