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.

  • Henning said 8 months later:

    @Chris: Thank you for the encouragement :)

  • Tyler Durden said 10 months later:

    Is it possible to get the Software behind the notes for private use?

  • Henning said 10 months later:

    @Tyler: We might have something exciting in store for you. Please be patient a little while longer.

Avatar

Mon, 27 Sep 2010 20:47:00 GMT

by henning

Tags:

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
end

Under 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
end

By 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

end

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

Avatar

Sun, 15 Aug 2010 10:12:00 GMT

by henning

Tags:

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

end

Obviously 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

Avatar

Mon, 07 Jun 2010 07:50:00 GMT

by arne

Tags:

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.

Avatar

Tue, 25 May 2010 22:16:00 GMT

by henning

Tags:

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.

Avatar

Sun, 23 May 2010 13:30:00 GMT

by henning

Tags:

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'
end

This 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.rb

Each 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
end

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

Avatar

Sat, 17 Apr 2010 18:59:00 GMT

by henning

Tags:

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!

  • Brady said 26 days later:

    I’ve tacked on a gzip compression option, available at:

    http://gist.github.com/532561

    Thanks for the original, works like a charm.

  • Thomas said 26 days later:

    Good idea, thanks for sharing!

Avatar

Fri, 23 Jul 2010 09:48:00 GMT

by thomas

Tags:

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
end

Notice 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 :)

Avatar

Thu, 25 Mar 2010 21:12:00 GMT

by henning

Tags:

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
end

Now 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
end

Finally 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
end

Now 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 a unless consider_all_requests_local block. When my ActionController is interpreted consider_all_requests_local is true, before I can spoof it in cucumber’s support/env or a Given - so the rescue_from clauses 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.

Avatar

Wed, 24 Mar 2010 21:45:00 GMT

by henning

Tags:

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] }}
end

It 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}'
end

The 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
end

I'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
end

You 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
end

Was this post helpful to you? Then let us know!

Avatar

Sun, 21 Mar 2010 20:27:00 GMT

by henning

Tags: