Rails3 ActiveSupport Notification subscription – Rails3 Tricks #03

Hello there! Almost 10 months without any post due to a very busy period: I moved from Paris to San Francisco.
Ok, so now facts has been said, this will hopefully change! I plan to write a new series of Rails3 posts, starting with a quick one on the new way Rails handles notifications.

The new version of ActiveSupport shipped with Rails brings along a new notification system which is heavily used by Rails3 internarlly. Rails doesn’t write directly to logs anymore, instead of that, it publishes a notification which can be caught by any observers.

In production, Rails will by default publish deprecation warnings through this notification system. Last week I was looking for a way to play with that and didn’t find a clear example on the web, so here is a small snippet of code if you are also looking for a nice way to log your deprecation warnings:


# In config/initializers/deprecations_logger.rb
DeprecationLogger = Logger.new(Rails.root.join('log/deprecations.log'))

ActiveSupport::Notifications.subscribe(/deprecation/) do |type, date,b,c, event|
  DeprecationLogger.info("#{date} - #{event[:message]}")
end
  • Share/Bookmark

Rails3 and will_paginate, Doing easy remote links – Rails3 Tricks #02

As you know, Rails3 use only UJS (unobtrusive javascript), so for every remote link, Rails3 just add the data-remote attribute to links :

<a href="ajax_page.html" data-remote="true">A remote link !</a>

If you want to do ajaxed pagination, there is no easy way with will_paginate to do remote link (or I didn’t find any one), but with Rails3 UJS, there is a little tricks do to this easily !

Here is the haml and jquery code :

= will_paginate(@users)
:javascript
  $('.pagination a').attr('data-remote', 'true');

This snippet of code just add the attribute data-remote to pagination links. And that’s it ! Our pagination will now be ajaxed :)

  • Share/Bookmark

Using Rspec with multiple version of Rails – Rspec Tricks #01

With the upcoming release of Rails3, it’s important to maintain gem/plugins compatibility with both Rails 2.x series and the new Rails 3.x series.

That’s why I wanted to implement specs supporting both versions for one of my plugin.

With a simple rake task, I wanted to run spec for a specific Rails version.

By default, Rspec create this kind of Rakefile :

require 'rubygems'
require 'rake'
require 'spec/rake/spectask'
 
spec_files = Rake::FileList["spec/**/*_spec.rb"]
 
desc "Run specs"
Spec::Rake::SpecTask.new do |t|
  t.spec_files = spec_files
  t.spec_opts = ["-c"]
end
 
task :default => :spec

Here we have the default Rake task : “spec” wich will run specs for our code.

Custom our Rakefile to specify the Rails version

At this point, we need to pass an extra option to spec script in order to specify wich rails version we want to use in our tests.

The way I found to do this is to tricks spec_opts (which is command line options for spec) by assigning to it, a Proc, like this :

t.spec_opts = lambda do
  @rails_version ? ["-c -- rails_version=#{@rails_version}"] : ["-c"]
end

Then add two tasks to your Rakefile :

desc "Run Rails 2.x specs"
task :rails2_spec do
  @rails_version = 2
  Rake::Task['spec'].invoke
end

desc "Run Rails 3.x specs"
task :rails3_spec do
  @rails_version = 3
  Rake::Task['spec'].invoke
end

Ok, now we have our three tasks passing Rails version to our spec tests !

kwi@ ~/Projects/i18n_routing$ rake -T
(in /Users/kwi/Projects/i18n_routing)
rake rails2_spec  # Run Rails 2.x specs
rake rails3_spec  # Run Rails 3.x specs
rake spec         # Run specs for current Rails version

Retrieve the Rails version parameter

Then in your spec_helper.rb file, where you include your dependencies, you just need to retrieve the rails version with this (dirty) piece of code :

rails_version = ARGV.find { |e| e =~ /rails_version=.*/ }
rails_version = rails_version.split('=').last.to_i rescue 2

Now we have the correct rails version we want for running our tests, so we need to include our dependencies depending on the Rails’ version we want to load.

For this, there is a tricky things with gem method for specify wich version of gem you want to use :

gem 'actionpack', (rails_version < 3 ? '< 2.9' : '> 2.9')
require 'action_controller'

(Example for loading action_controller only, here I use 2.9 version number in order to support Rails3beta)

Here we are, now we can run our tests on both Rails version just with a simple rake task :

rake rails2_spec
# Or
rake rails3_spec
  • Share/Bookmark

Nginx rewriting and redirection tips

Rewriting url with nginx

Last week, I needed to rename a folder in a rails’ public directory served by nginx. But, doing that, I wanted to keep some links already indexed by search engines and especially jpg images still accessible. And only jpg images, so symbolic links were not appropriate.

  # Ensure JPEG are still accessible after
  # renaming my folder videos to thumbs
  location ~* ^/videos/.+\.(jpg)$ {
    rewrite ^/videos/(.*)$ /thumbs/$1 last;
  }

This snippet of code solve my problem. For every .jpg asked in the videos folder, it rewrites videos by thumbs and then continue serving the file.

Redirection with nginx

Another things can be useful, here is a little piece of code that check if a file is present on the file system and if not, redirect the request to another server :

location ~* ^/thumbs/.+\.(jpg)$ {
  if (-f $request_filename) {
    # The file is present, so serve the file with caching header
    expires 1y;
    add_header Cache-Control public;
    break;
  }

  # the file is not present, redirect to another server
  rewrite ^(.*) http://another.thumbs-server.com$1 permanent;
}
  • Share/Bookmark

Another way to compare Class – Ruby Tricks #04

Today, just a really small tricks but I find it kind of cool. (But useless :) )

When we want to check an object class, we often use .is_a?(ClassName).

But you can do this with the === operator too :

Hash === {}
# => true

But be careful, put the Class before, as it’s not the same :

{} === Hash
# => false
  • Share/Bookmark

Hash creation on the fly – Ruby Tricks #03

So many times, we want to extract from an Array an Hash in order to access more easily some values.

For example, your are in your Rails environment and you want to extract from your Articles tables an Hash with hash[article_id] => Article.

Here is the tricks do to this with just one line of code :

Article.all.inject({}) { |h, article| h[article.id] = article; h }

A real world example I use for caching data in an active record model with a constant :

class Locale < ActiveRecord::Base
  ...
  LocaleCached = self.find(:all).inject({}) { |h, l| h[l.short.to_sym] = l; h }
  ...
end
  • Share/Bookmark

String concatenation performance – Ruby Tricks #02

When it’s come to make string concatenation that you use hundred time in your every day projects, you have the choice in Ruby !

Most common cases :

"Hi #{login}"

'Hi ' + login

s = 'Hi '
s += login

s = 'Hi '
s << login

But, all these methods for concatening strings does not really behave the same :

First case, += VS << :

s = 'Hi '
s += login

The + operator for strings create a new string object by concatening two strings, here ‘Hi ‘ and login. So we have instanciated two strings in order to just get one.

s = 'Hi '
s << login

On the other hand, the << append directly the content of the second string in the first string, so you do not re-instantiate a new string. But you modify your first object, so be careful especially when it comes from a variable.

Second case, + VS #{} :

'Hello ' + 'ruby ' + 'world'

Create the ‘Hello ruby ‘ string then re-create the last string : ‘Hello ruby world’
=> So create unecessary strings.

"Hello #{'ruby '}#{'world'}"

Directly create the full string ‘Hello ruby world’ without an intermediate state like seen before

Conclusion

  • Privilegiate << when you can !
  • Use the “#{}” concatenation manner when you concatenate more than 2 strings together.
  • Share/Bookmark

Symbol#to_proc – Ruby Tricks #01

First tricks today, here is an easy one :

If you are using Active Support (shipped with Rails), or a ruby version superior or equal to 1.8.7, you can use the symbol proc shortcut :

Here is the standard way declaring a block :

>> ['a', 'b', 'c'].collect {|letter| letter.capitalize}
=> ["A", "B", "C"]

Here is the handy method :

>> ['a', 'b', 'c'].collect(&:capitalize)
=> ["A", "B", "C"]

But, keep in mind that the shortcut method is a little bit slower in term of performance than the normal way cause it creates a new Proc on each call !

Benchmark :

t = Benchmark.realtime do
  (['a'] * 1000000).collect(&:to_s)
end
puts "Time using to_proc: #{t}"

t = Benchmark.realtime do
  (['a'] * 1000000).collect do |e|
    e.to_s
  end
end
puts "Time using normal block: #{t}"

# Time using to_proc: 0.631899118423462
# Time using normal block: 0.246822834014893
# Results are the same if you test the normal block first
  • Share/Bookmark