In which we open source our knowledge base
We don't like to solve the same problem twice. That's why whenever we figure out something, we document it for the rest of the team. Over the last year we collected quite an amount of code snippets and HOWTOs this way.
This September we decided to take our in-house knowledge base and publish it for everyone to see. makandra notes contains some 200 HOWTOs and 500+ links for Ruby, Rails, RSpec, Cucumber and Javascript and is growing every day.
Whether you're looking to deliver Paperclip attachments securely, test concurrent Ruby code or marry Capybara with SSL-enabled applications, chances are we already solved your problem for you.
We hope that our notes will become a valuable resource for other developers working in our field. And if we could help you, or if you have an idea how to make makandra notes better, please let us know.
How to define helper methods in magic DSL code
Many Ruby gems expose their API as a DSL instead of a set of classes. A popular example of this is RSpec, which lets you write tests like this:
describe User do
describe '#full_name' do
it 'should return the concatenated first and last names' do
user = User.new(:first_name => 'Henry', :last_name => 'Cook')
user.full_name.should == 'Henry Cook'
end
end
endUnder the hood RSpec converts the describe something do syntax to plain Ruby classes and objects. However, RSpec never tells you the names of those magic classes and in fact they might not even have names. This makes it hard to find the right place for helper methods you'd like to use in your DSL blocks.
For the sake of this example, let's assume you'd like to extract the User.new(...) invokation into a helper method new_user(...).
Don't do this (although it happens to work):
def new_user(first_name, last_name)
User.new(:first_name => first_name, :last_name => last_name)
end
describe User do
describe '#full_name' do
it 'should return the concatenated first and last names' do
user = new_user('Henry', 'Cook')
user.full_name.should == 'Henry Cook'
end
end
endBy defining a method in the void you are extending Object, the mother of all Ruby classes. This means that every single object now has a new_user method.
A more humble way to define that method is to use a lambda:
describe User do
new_user = lambda do |first_name, last_name|
User.new(:first_name => first_name, :last_name => last_name)
end
describe '#full_name' do
it 'should return the concatenated first and last names' do
user = instance_exec('Henry', 'Cook', &new_user)
user.full_name.should == 'Henry Cook'
end
end
endHere new_user is a local variable that happens to contain a method. You can invoke it with instance_exec. It's not visible to other classes but can still be captured by every code block below it – exactly what we need.
We use the lambda-technique to DRY up our Rails routes machinist blueprints, and, of course, RSpec examples.
Overriding e-mail recipients in ActionMailer
Our customers like to have a staging environment so that they can always test the latest and greatest bleeding edge features. But what if an application is sending e-mails and you would not want someone@somecorp to be told to visit your awesome staging website?
Setting ActionMailer's delivery method to :test somewhat does the trick but there will be no real e-mail until the application goes live. That's sad.
Maybe you would want those mails to get delivered somewhere, like your customer's e-mail address or your own.
We ran into some difficulties using the sanitize_email gem on Rails 2.3.5 and were a bit unhappy with the way the gem was doing its work (configuring it application-wide felt somewhat weird). So we came up with a different solution.
For the magic to happen we put two lines into the designated environments files (like environments/staging.rb). If we want e-mails to be delivered to admin@example.com and foo@bar.baz instead of their actual recipients it looks like this:
require 'override_mail_recipients'
ActionMailer::Base.override_recipients = %w[ admin@example.com foo@bar.baz ]Of course, there is a bit more to it. Put the following code into a file like lib/override_mail_recipients.rb and ActionMailer will know what to do.
ActionMailer::Base.class_eval do
@@override_recipients = nil
cattr_accessor :override_recipients
def deliver_with_override!(mail = @mail)
if override_recipients.present?
mail.override_recipients! override_recipients
end
deliver_without_override! mail
end
alias_method_chain :deliver!, :override
end
TMail::Mail.class_eval do
def override_recipients!(recipients)
recipients = Array(recipients)
original_addresses = {
:to => override(:to, recipients),
:cc => override(:cc, recipients),
:bcc => override(:bcc, recipients)
}
self.body = original_addresses.to_yaml + "\n" + self.body
end
def override(method, recipients)
original_recipients = send("#{method}")
self.send "#{method}=", recipients if original_recipients
original_recipients
end
endObviously we "only" replace any existing e-mail recipients with ours and do not want to do anything fancy like secretly adding ourselves to BCC here. On staging every email is sent to the e-mail address(es) defined in the environment file and the original recipients are quoted at the beginning of each mail.
All other environments will behave like nothing ever happened.
Update: We extracted this trick into its own gem, Mail Magnet.
-
Jamie Wong said about 16 hours later:
If you want a really elegant solution to sending mail, especially from rails, check out PostageApp
Will Rails 3 obstruct plugin innovation?
One of the great things about Rails are the many gems and plugins that help you save time. With its comprehensive public API Rails offers a fertile breeding ground for all kinds of useful abstractions. It's one of the great success stories in plugin architecture, up there with Eclipse, Wordpress and others.
Some of that might change with Rails 3.
You see, a big theme for Rails 3 is framework agnosticism, meaning that you can take the ORM layer, templating engine or another component of Rails and replace it with a framework of your choice.
For the most part, this is awesome. We're already using RSpec, jQuery and Haml in lieu of the default Rails stack and it will be great to integrate those tools without the hacks that used to go with it.
But I'm not sure what will happen to the plugin ecosystem now that a plugin can no longer assume that ActiveRecord is handling the persistence. Where there was once a consistent API to manipulate and hook into the lifecycle of a persistent object, plugins must now perform very careful checks whether an object supports more advanced traits like transactions, observers or dirty attribute tracking.
Take the latest state_machine gem, which comes with adapters for four and a half persistence layers and selectively disables features depending on what ORM is available. While I applaud Aaron Pfeifer for going through the pain of writing all that scary integration glue, a lot of perfectly good code might not get published because the bar for being a good Rails plugin is now so much higher than it used to be.
-
Jeremy McAnally said about 8 hours later:
This is a big reason we have Active Model though. Theoretically the library behind it shouldn’t matter all that much; most plugins only need the external API’s that it provides. If you need something more specific, I’d recommend following the pattern of something like CarrierWave and having a file you can require for each ORM’s functionality.
-
Jake Moffatt said 3 days later:
Won’t somebody just write a plugin that sits between your plugin and your rails and figures out what ORM to use?
-
Henning Koch said 3 days later:
@All: Thank you for your wonderful comments.
@Jake: There are limits to what you can abstract away. You cannot make all persistence layers behave the same because some have inherently different approaches to O/R mapping.
A hot, hot Ruby Summer of Code
This year's student projects have been posted at the Ruby Summer of Code. I can't help but be amazed at the choice of teams that are going to receive stipends. Almost all the projects touch a topic that is dear to my heart. Identity maps for ActiveRecord? Hell yeah! Mail processing that works out of the box? Hell yeah! Yes, please! A setup-less teaching environment with Camping? Get out of my head!
Good luck to all the teams who made it. Your efforts will be appreciated by Ruby workers from all over the planet. Also a big thank you to thesponsors and mentors who made the event possible.
How to split a Ruby class into multiple source files
Many Rails projects have one or two models that act as a hub for the rest of the application. One is usually the User model, the others depend on your problem domain. If your application is about managing projects, chances are the Project class does a lot of things.
Such larger models are likely concerned with multiple themes like "permission checks" or "budget calculations". Each of these concerns requires a couple of validations and callbacks here, and a method definition there.
Lumping all these concerns into one large file blurs the lines between them. Someone opening the Project class for the first time will have a hard time seeing its individual aspects. In addition, a developer will usually be interested in a single concern in order to fix a bug or implement a new feature.
A way to better organize a large class is to split it into multiple source files. Ruby comes with a built-in tool for that: modules. Unfortunately vanilla Ruby modules lack support for many idioms popular in modern Ruby. Most importantly, we have become accustomed to composing our classes with meta-programming macros such as has_many, validates_presence_of or after_save. And modules weren't built with macros in mind.
Enter Modularity. Modularity is to your models what partials are for your views. It lets you write classes like this:
class Project
does 'project/billing'
does 'project/budgets'
does 'project/permissions'
endThis Project class is now organized in three additional sources files called traits:
app/models/project.rb
app/models/project/billing_trait.rb
app/models/project/budgets_trait.rb
app/models/project/permissions_trait.rbEach trait is packaging a single concern, including its method definitions and macro calls. Let's take a look at the trait that deals with project billing:
module Project::BillingTrait
as_trait do
has_defaults :billed => false
belongs_to :billing_address_id
validates_presence_of :bill_date, :if => :billed?
def billed?
billing_address_id.present?
end
def payment_overdue?
billed? && bill_date + 21.days < Time.now
end
end
endOrganizing a model into partial classes is only one of Modularity's many uses. Most importantly you can parametrize your traits. Just like has_many :cats and has_many :dogs will add different methods to your class, a parametrized trait can provide different behavior depending on which argument they are included with.
Find out more about rolling your own meta-programming macros with Modularity over at Agile Web Operations.
-
Henning said about 1 month later:
dm-is-remixable could be expressed in a Modularity trait.
You can use Modularity in any Ruby application, Rails is not required.
-
Ryan Bigg said about 1 month later:
What is the difference between doing this and using the standard “include” method Ruby provides? Is it just syntatic sugar for the sake of it or do I get a free rainbow with it?
-
Henning said about 1 month later:
@Ryan: Your free rainbow is that modularity traits can be parametrized, providing different behavior depending on which arguments they are included with. This is not possible with Ruby’s include.
Check out the link at the end of the article for an example.
Dumping database from within a Rails project
Dumping a database is a trivial task. Nevertheless it takes time to copy & paste credentials when using mysqldump or the like. Aside from that it's pretty boring.
We have a script to quickly dump a MySQL database. It saves all output to dumps in your home directory. The script will create the directory and set proper permissions for you.
A good place to put the following script into is ~/bin/dumple or /usr/local/bin/dumple when used by others on the machine. Remember to assign the executable flag: chmod +x ~/bin/dumple.
#!/usr/bin/env ruby
fail_gently = ARGV.include?("--fail-gently")
if ARGV.include?("-i")
puts "*******************************************************"
puts
system("du -sh ~/dumps")
puts
puts "*******************************************************"
exit
end
require "yaml"
config_path = 'config/database.yml'
unless File.exist?(config_path)
if fail_gently
puts "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
puts "* *"
puts "* *"
puts "* Script is not called from inside a Rails project, *"
puts "* *"
puts "* THE DATABASE WILL NOT BE DUMPED. *"
puts "* *"
puts "* *"
puts "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
sleep 5
exit
else
raise "Call me from inside a Rails project."
end
end
config = YAML::load(File.open(config_path))
environment = ARGV.reject{ |arg| arg[0].chr == '-' }.first || 'production'
config = config[environment] or raise "No production environment found. Please do `dumple [env_name]`"
dump_dir = "#{ENV['HOME']}/dumps"
unless File.directory?(dump_dir)
Dir.mkdir(dump_dir)
system("chmod 700 #{dump_dir}")
end
dump_path = "#{dump_dir}/#{config['database']}_#{Time.now.strftime("%Y%m%d_%H%M%S")}.dump"
puts
puts "Dumping database for environment \"#{environment}\"..."
system "mysqldump -u\"#{config['username']}\" -p\"#{config['password']}\" #{config['database']} -r #{dump_path}"
system "chmod 600 #{dump_path}"
dump_size_kb = (File.size(dump_path) / 1024).round
puts "Dumped to #{dump_path} (#{dump_size_kb} KB)"
puts
Run the script at the root folder of a Rails project and it will (try to) dump the production database. To dump a database belonging to a different environment, give it as first parameter:
user@host:~/rails/current$ dumple dev.example.com
Dumping database for environment "dev.example.com"...
Dumped to /home/thomas/dumps/dev_example_20100723_091715.dump (4127 KB)Maybe you spotted that --fail-gently can be given as argument and affects the way the script fails. This has to do with Unix exit codes. I'll tell you in the next article why we need it!
How to use models in your migrations (without killing kittens)
Sometimes a migration needs to do more than add or remove a column. When you must convert existing data, migrations can get nasty because SQL is all you can use. You can never call models in a migration because your models will evolve away from your migrations and everyone will die. Seriously, don't do that. Think of the children!
Fortunately, there is a way to use ActiveRecord magic in your migrations and still get the girl. By inlining classes you decouple your migration from any future changes in your models. Here is an example:
Say you have two classes Article and Vendor. Each Article has_many :vendors. Now you want to introduce a boolean flag current to your Vendor class, which determines whether the article is currently being procured from that vendor.
But what about the 14000 articles already in the database? You call up your client and decide on the following migration rule: The first vendor created for an article is to be flagged as "current".
While you can express that rule in pure SQL, you'd much rather use vanilla Ruby. So you write your migration like this:
class AddCurrentToVendor < ActiveRecord::Migration
class Vendor < ActiveRecord::Base
end
class Article < ActiveRecord::Base
has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at'
end
def self.up
add_column :vendors, :current, :boolean
Article.all.each do |article|
article.vendors.first.andand.update_attribute(:current, true)
end
end
def self.down
remove_column :vendors, :current
end
endNotice how our two classes were namespaced into the migration class, so Vendor becomes AddCurrentToVendor::Vendor.
-
Eric Anderson said about 5 hours later:
Awesome suggestion. I can’t tell you how many times I have had a migration crap out on me because of model changes. Glad someone had a lightbulb moment..
-
Matchu said about 1 month later:
…brilliant.
I feel like I’m good enough in Ruby, but it’s strokes of genius like this that make me realize just how much more I have yet to grow :)
Testing your custom error pages with Webrat and Cucumber
When your application raises an error during development, Rails and Webrat will provide you with useful information like the error's type and stacktrace.
But now you created that shiny, custom error page by overriding rescue_action_in_public. You would like to write a Cucumber feature that tests whether your new error page localizes into 10 languages and displays all the information it should.
Unfortunately it requires some serious Judo to make your error page visible to selected scenarios, while keeping Rails' helpful default behavior for your other tests. Here is how you do it.
First, we will tame Webrat. Webrat raises an exception when you visit a page that shows an error. So we're going to write a step that visits a page, but ignores errors:
When /^I ignore errors while visiting (.+)$/ do |page_name|
begin
visit path_to(page_name)
rescue
end
endNow we need to get Rails to render the error page for selected scenarios. Unfortunately we will have to flip a lot of switches to accomplish this.
Put the following into your support/env.rb. With this code we can make Rails treat selected requests as remote ones, (local requests never see error pages), regardless of what consider_all_requests_local is set to:
ActionController::Base.class_eval do
protected
cattr_accessor :local_request_override
def local_request_with_override?
local_request_override.nil? ? local_request_without_override? : local_request_override
end
alias_method_chain :local_request?, :override
endFinally we need a step that flips all the magic switches for the current scenario and reverts to the previous error handling afterwards:
Given /^error pages will be rendered$/ do
@old_allow_rescue = ActionController::Base.allow_rescue
@old_consider_all_requests_local = ActionController::Base.consider_all_requests_local
@old_local_request_override = ActionController::Base.local_request_override
ActionController::Base.local_request_override = false
ActionController::Base.allow_rescue = true
ActionController::Base.consider_all_requests_local = false
end
After do
@old_allow_rescue.nil? or ActionController::Base.allow_rescue = @old_allow_rescue
@old_consider_all_requests_local.nil? or ActionController::Base.consider_all_requests_local = @old_consider_all_requests_local
@old_local_request_override.nil? or ActionController::Base.local_request_override = @old_local_request_override
endNow you can write your error_pages.feature:
Feature: custom error pages
In order to not scare my users when things go wrong
As the site owner
I want to display friendly error pages
Scenario: A country cannot be found
Given error pages will be rendered
And I ignore errors while visiting the country "Narnia"
Then I should see "Oops, we couldn't find Narnia!"I recommend you extend your feature to cover some more cases and also make it check if proper HTTP status codes are returned.
Was this post helpful to you? Then let us know!
-
mike said 7 months later:
@Henning: thanks a lot! This saved quite some time. I still have some questions though.
1) Why do I have to make the requests not to be considered local? Indeed I see a stack trace, but why?
2) On another site (see ref below) I found the following that claims to achieve the same non-local effect (though I did not get it working):
header “REMOTE-ADDR”, “10.0.1.1”
Is that a sufficient replacement for your ActionControler change? Ref to the site: http://jjinux.blogspot.com/2009/08/rails-dynamic-404s-authlogic-cucumber.html
–Mike
-
Henning said 7 months later:
@Mike: It’s a built-in behavior of Rails that error pages are only rendered for remote requests. Requests that are considered local always get the stacktrace.
The method on the site you mentioned requires less code than our solution, which would be preferrable. Should you find a way to get it to work, let me know.
-
krukid said 12 months later:
@mike header “REMOTE-ADDRâ€, “10.0.1.1†in a “Given” clause is sufficient to spoof local_request? => false
I’ve had a similar problem - not being able to trap my custom
rescue_from ...behavior because it was wrapped in aunless consider_all_requests_localblock. When my ActionController is interpretedconsider_all_requests_localis true, before I can spoof it in cucumber’s support/env or aGiven- so therescue_fromclauses don’t apply. To fix that behavior you can either unwrap these clauses so that they work in your test environment or duplicate them like I did in support/overrides. Not pretty, but there seems to be no other way.
Using dynamic has_many conditions to save nested forms within a scope
Named scopes are a convenient way to filter record lists. When we want all future bookings of a given event, we can define a future scope for our booking:
class Event
has_many :bookings
end
class Booking
named_scope :future, lambda {{ :conditions => ['date >= ?', Date.today] }}
endIt gets tricky when you want a form to edit all future bookings of an event using nested forms. Nested forms weren't designed with scopes in mind, they only play nice with has_many associations.
Here is a little hack to make nested forms play along. Write your has_many association like this:
class Event
attr_accessor :bookings_filter_sql
has_many :bookings, :conditions => '#{@bookings_filter_sql}'
endThe single quotes around the conditions are not a typo. It's to prevent Ruby from interpolating the string at compile time.
You can now set the bookings_filter_sql attribute of an event to an SQL condition. All bookings of that event instance will then be scoped to that condition:
class EventController
def update
@event = Event.find(params[:id])
@event.bookings_filter_sql = "date >= '#{Date.today.to_s(:db)}'"
@event.attributes = params[:event]
@event.save!
end
endI'd like to recommend two improvements to this hack. First, you need to be super careful to not allow SQL injections when bookings_filter_sql gets directly piped into has_many. Better make that attribute a getter that sanitizes the SQL:
class Event
has_many :bookings, :conditions => '#{bookings_filter_sql}'
attr_writer :bookings_filter
def bookings_filter_sql
self.class.send(:sanitize_sql_for_conditions, @bookings_filter)
end
endYou can now set the filter with all the sugar you're used to:
@event.bookings_filter = ['date >= ?', Date.today.to_s(:db)]Secondly, the bookings association as written above only works when a filter is set. So let's give it a alternate condition that is always true (like '1=1') for that case:
class Event
has_many :bookings, :conditions => '#{bookings_filter_sql}'
attr_writer :bookings_filter
def bookings_filter_sql
self.class.send(:sanitize_sql_for_conditions, @bookings_filter || '1=1')
end
endWas this post helpful to you? Then let us know!



@Chris: Thank you for the encouragement :)
Is it possible to get the Software behind the notes for private use?
@Tyler: We might have something exciting in store for you. Please be patient a little while longer.