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
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:
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.