Ruby

Environment specific files and gitignore

I love my Mac for doing development! Textmate and Passenger make it my all-time favorite environment to develop Ruby / Rails in. It does have some very specific “junk” files I don’t want in my git, though.

In the beginning I would include all those files in every project’s .gitignore file. This worked since all my colleagues also work on a Mac with Passenger & TextMate. Recently I’ve come across people with different setups, which made the project’s .gitignore look like this:

.DS_Store
Thumbs.db
tmp/restart.txt
.idea
.todo
.bundle
.rake_tasks~

After some searching I found you can easily setup a global .gitignore which ignores your environment specific files. Doing so is easy:

# ~/.gitignore
.DS_Store
tmp/restart.txt
.rake_tasks~
.idea

And added this to my global git config

git config --global core.excludesfile ~/.gitignore 

This all makes your .gitignore file much cleaner and more relevant for the project itself.

Ruby and Rails beginner talk

Last night I gave a talk for Zeus about Ruby and Rails, talking about Ruby fundamentals and a short intro to the Rails philosophy and MVC.

I don’t know if you’ll be much with the slides without hearing the talk, but you can find them on http://defv.be/ruby.and.rails.pdf.

If you want to see the entire presentation you should come to ArrrrCamp, where I’ll be giving it again.

Autoload JQuery Plugins

Rails has the convenient helper javascript_include_tag where you can give up :default, which automatically loads prototype / effects, or with the jrails plugin adds jquery, jquery-ui and jrails.

But when using jQuery you want to use more than those files, you want to use jQuery plugins, or write your own, all in small .

Loading this can be a pain in the ass. Thats why I wrote this little initializer which adds everything in public/javascripts/jquery/*.js to the defaults.

# config/initializers/add_jquery_to_defaults.rb
Dir[File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR,'jquery','*.js')].each do |js|
  ActionView::Helpers::AssetTagHelper.register_javascript_include_default "jquery/#{File.basename(js)}"
end

Custom ActiveRecord timestamps

Some legacy databases have already defined their own created_at or updated_at fields. They can be easily filled in with ActiveRecord before_save filters, but after implementing this behaviour a few times the urge arose to write something reusable, so I created the custom_timestamps plugin.

With custom_timestamps you can easily define the columns you want to be updated on creation/change

class LegacyModel < ActiveRecord::Base
  set_create_column :creation_time
  set_update_column :change_time
end

The plugin can be found on github.

ArrrrCamp Belgium

We have been talking between ourselves about hosting a Belgian Ruby conference since the last Railsconf Europe, and we have finally decided to proceed with the idea. Colleague Joren put things in motion and today we’re proud to announce Arrrrcamp, which stands for “About Ruby, Rails, Radiant and Rum Camp”.

The event will take place on friday, May 8th, in Ghent, Belgium. The idea is to have a barcamp-like organisation of talks, where everybody is encouraged to participate by either doing talks, taking pictures, do some hacking, … You can read all about the event on the website.

Application-wide timeranges

In some applications you need to work with time-ranges a lot. Displaying todays messages, a count of all messages that were posted this month, or last month, … ActiveRecord has pretty good support for times and ranges, so you could do it the default way.

Message.find(:all, :conditions => {:created_at => Time.now.beginning_of_month..Time.end_of_month})

And although this works perfectly it’s not very easy on the eyes. It’s also not very DRY since I need this on a lot of models. It’d be a lot better if I could have it in a scope, like this

Message.in(:this_month)

So how do we achieve this?

In our config/initializer folder we have a file called timeranges.rb.

# timeranges.rb
TimeRanges = {
  :today      => lambda { Time.now.beginning_of_day..Time.now.end_of_day },
  :yesterday  => lambda { 1.day.ago.beginning_of_day..1.day.ago.end_of_day },
  :this_week  => lambda { Time.now.beginning_of_week..Time.now.end_of_week },
  :last_week  => lambda { 1.week.ago.beginning_of_week..1.week.ago.end_of_week},
  :this_month => lambda { Time.now.beginning_of_month..Time.now.end_of_month },
  :last_month => lambda { 1.month.ago.beginning_of_month..1.month.ago.end_of_month }
}

Which gives us a constant TimeRanges we can use all over the application. So in our Message we define the named_scope in

class Message < ActiveRecord::Base
  named_scope :in, lambda { |period| 
      :conditions => {:created_at => TimeRanges[period].call}
  }
end

Now we could even make this more DRY and make a module that defines the named_scope when it’s included, but in my application I had a lot of different use-cases for the TimeRanges, and didn’t really need the scope a lot. Leave me a note if you want it in a module but don’t know how to, else it’s an exercise for the interested and easily challenged reader 😉

Testing Forgery Protection

Try to follow here:

I needed to test 1 controller to see if he was not protected from forgery protection, because requests to that controller come from an external source.

So basically I need to test if my code has

skip_before_filter :verify_authenticity_token

In the test environment, the authenticity_token check is disabled. You can re-enable it in your test like this:

class AccountsControllerTest < ActionController::TestCase
  def setup
    super
    AccountsController.allow_forgery_protection = true # Make sure we have forgery protection before filter turned off
  end
end

When you do a post and you haven’t skipped the filter, the test will fail.

session_lifetime plugin

One of the things I take for granted on the internet is session expiry. I’m used to the fact that if I’m not active for a certain amount of time, I automatically get logged out. This happens with my Online Banking, with ordering tickets online, …. Most information-critical applications have this.

Because of that, I was surprised that Rails doesn’t have this functionality on board. There is no way to set an expiry date on your session. I googled a bit and stumbled upon the dynamic session exp plugin. This gives you the possibility to expire your session through the cookie’s expires-flag.

An example

# in environment.rb
CGI::Session.expire_after 1.day

This plugin worked fine until I wanted to send a message to the webuser, informing him why this happened. Because we’re working with the expire-setting in a cookie, this isn’t possible. Because of that, I wrote my own plugin that gave me that possibility.

With session_lifetime you can set after how much time of inactivity your session should expire, you can execute an action when the session expires, and you can set where to redirect_to after session expiry.

An example

class ApplicationController << ActionController::Base
  expires_session :time => 2.hours, :redirect_to => '/login'

  protected
  def on_expiry
    flash[:notice] = "Your session has been expired, and you have been logged out."
  end
end

More information can be found on github.

Missing something?

I wrote this plugin to solve the problems I had with default Rails session handling. If you have an additional need which you think would be great for this plugin, give me a shout at [email protected], or through GitHub, and I’ll be more then happy to implement your proposal.

RailsXLS Revived

RailsXLS is a plugin to generate Excel files from a .rxls view. It uses a Java Bridge to talk to the Jakarta POI library. I’ve used this plugin in a few projects and it works great for generating .xls files.

On a recent project I once again needed an “Export to Excel” feature. The project is on Rails Edge, and apparently there were some changes to how TemplateHandlers are handled since the plugin was written. I dove into the code and recreated the plugin for Rails Edge / 2.2.

You can find the new plugin on github

It’s usage is quite easy:

# in your environment.rb
Mime::Type.register "application/excel", :xls

# in controller
def index
  @clients = Client.all
  respond_to do |speaks|
    speaks.html
    speaks.xls { render :layout => false }
  end
end

# in index.xls.rxls
sheet = workbook.createSheet("Client List")

@clients.each_with_index do |client, index|
  row = sheet.createRow(index)
  row.createCell(0).setCellValue(client.id)
  row.createCell(1).setCellValue(client.name)
end

As you can see there are still some rough patches in the view. I’m working on a wrapper around this, so all obvious tasks can be easily performed.

Any comments/suggestions for the plugin are very welcome! A big thanks to Venkata Subramaniyan for writing the initial plugin, which can be found here

Do Not Forget: NamedScope and dates

One of the scopes I had in a project was from_this_month

class Transaction
  named_scope :from_this_month, :conditions => {:created_at => Time.now.beginning_of_month..Time.now}
  ...
end

Which worked great to know all transactions from a user in a certain month. I could do current_user.transactions.from_this_month. When I tested this in developer mode all went well.. Production, however, didn’t work at all.

As we all know, Developer mode reloads classes every time you make a request. Production mode obviously doesn’t, so in Production, the first time you load the page you get a correct time-scope, if you do it a day later, however, the scope is still the same of yesterday. This can be solved with a proc or a lambda, which will get called on each request!

So, just to make sure I never forget, Use a lambda!

class Transaction
  named_scope :from_this_month, lambda { {:conditions => {:created_at => Time.now.beginning_of_month..Time.now}} }
  ...
end

Pros and Cons

A recent commit to the Rails github repository had quite some comments. In one of the tests there was the string:

    topic = @target.new(:title => "The pros and cons of programming naked.")

After which Mislav Marohnić asked the obvious question:
Commit 4d9a7ab5f5c28820e0b076f9ca44bdd20e19e6ea to rails's rails — GitHub

After which we now have a pretty nice list of all the pro’s and cons of naked programming!

Refactoring: Implementing filtering

For a project at Openminds we needed an implementation for filtering some data from a list with the aid of checkboxes. Our first solution looked like this:

def index
  session[:order_filter] = params[:filter] if params[:filter] 
  session[:order_filter] ||= { :active => '1', :inactive => '1' } 

  if session[:order_filter][:active] == '0' && session[:order_filter][:inactive] == '0'
    @orders = []
  elsif session[:order_filter][:active] == '0'
    @orders = @user.orders.find(:all, :conditions => {:state => 'active'})
  elsif session[:order_filter][:inactive] == '0'
    @orders = @user.orders.find(:all, :conditions => {:state => 'inactive'})
  else
    @orders = @user.orders.find(:all)
  end
end

So this code takes the filter-parameters from params, or initializes them from defaults, and then does the correct finds.

In the next iteration, I stumbled onto this code and thought to myself “This can be done better”. After making sure the index action was sufficiently covered with tests, I had a go at it, this is what I came up with:

def list
  if filter.active? && filter.inactive?
      @orders = @user.orders.all
  elsif filter.active?
     @orders = @user.orders.active
  elsif filter.inactive?
    @orders = @user.orders.inactive
  else
    @orders = []
  end
end

protected
def filter
  return @filter if @filter
  session[:order_filter] = params[:filter] if params[:filter]
  session[:order_filter] ||= { :active => '1', :inactive => '1' }

  @filter = SearchFilter.new("Order", session[:order_filter])
end
helper_method :filter

# search_filter.rb
class SearchFilter
  attr_reader :name

  def initialize name, hash
    @name = name
    hash.each do |key, value|
      instance_eval <<METHOD
        def #{key}?
          #{value} == 1
        end
METHOD
    end
  end
end

This code is more readable in my opinion. I extracted the session-storing and parameter-juggling to the filter-method, which then returns a SearchFilter object. Thanks to SearchFilter I can now query filter for set values in a nice way. I can also call filter in my views, which is nicer then calling session[:order_filter][:active]. I also used named_scope instead of doing the finds manually, which improves readability a lot too!

How would you refactor this?

acts_as_state_machine with auto named_scope powers

Rails 2.1 (Soon to be released?) brings us integrated named_scope‘ing. I’ve already mentioned named_scope here and there, and you can find a complete writeup on Ryan Daigle’s Blog

While working on projects on Rails Edge, we used to have the same pattern over and over.

class Mail < ActiveRecord::Base
  include AASM

  aasm_state :created
  aasm_state :sending
  aasm_state :sent
  aasm_state :failed
  aasm_state :delayed

  named_scope :created, :conditions => {:status => 'created'}
  named_scope :sending, :conditions => {:status => 'sending'}
  named_scope :sent, :conditions => {:status => 'sent'}
  named_scope :failed, :conditions => {:status => 'failed'}
  named_scope :delayed, :conditions => {:status => 'delayed'}
end

For each state we had, we created a named_scope, because it is so great and easy to be able to say Mail.failed.each .... However, this isn’t really DRY, and we can see a clear pattern here. When you think about it, you really should have a scope for every state you define. So I forked from AASM on github and started hacking away.

The result:

class Mail < ActiveRecord::Base
  include AASM

  aasm_state :created
  aasm_state :sending
  aasm_state :sent
  aasm_state :failed
  aasm_state :delayed
end

...
>> Mail.failed
=> []
>> Mail.sent
=> [#<Mail id: 1, email: "[email protected]", content: "hallo", :aasm_state: "sent", created_at: "2008-05-21 15:39:38", updated_at: "2008-05-21 15:39:38">, #<Mail id: 2, email: "[email protected]", status: "sent", created_at: "2008-05-21 15:39:39", updated_at: "2008-05-21 15:39:39">]

You can find my named_scope enabled AASM on github. At the moment only the master branch has been patched with my functionality, I’ll do the no_aasm_prefix branch somewhere this evening / tomorrow. Let me know if you’re using it, and what could be better
This has all been implemented in the AASM Core now.

For those interested in the code, this is the bit that does the magic.

module AASM::NamedScopeMethods
  def self.add_named_scope base
    # Don't add unless it's a class which understands `named_scope`
    return unless base.respond_to? :named_scope

    base.extend AASM::NamedScopeMethods::ClassMethods
    base.class_eval do
      class << self
        alias_method :aasm_state_without_named_scope, :aasm_state
        alias_method :aasm_state, :aasm_state_with_named_scope
      end
    end
  end

  module ClassMethods
    def aasm_state_with_named_scope name, options = {}
      aasm_state_without_named_scope name, options

      self.named_scope name, :conditions => {self.aasm_column => name.to_s} unless self.scopes.include?(name)
    end
  end
end

Gotcha

While testing the plugin we found out 1 gotcha to this: do not define a aasm_state :new, because this will override the constructor of the class, and you’ll lose all creating-functionality.

Update

This is now default AASM behaviour.