how to write tests for observers 1
Rails Observers are especially important to test. With validation errors, they'll either silently fail without you knowing, or they'll raise exceptions and break your production code.
You'll want to disable your observers in your config/environments/test.rb environment, because they complicate unit tests:
config.active_record.observers = []
Here's what my observer tests look like:
require 'spec_helper' describe ArchiveObserver do before(:each) do entry = Factory.create(:entry, :orbits => [Factory.create(:orbit), Factory.create(:orbit)]) @archive = Factory.create(:archive, :entry => entry) end describe 'after_create' do it "creates 1 new_archive_event per orbit" do lambda { ArchiveObserver.instance.after_create(@archive) }.should change(NotificationEvents::NewArchiveEvent, :count).by(2) end end describe 'after_destroy' do before(:each) do ArchiveObserver.instance.after_create(@archive) end it "destroys any new_archive_events it created" do lambda { ArchiveObserver.instance.after_destroy(@archive) }.should change(NotificationEvents::NewArchiveEvent, :count).by(-2) end end end
Faster Ruby Gem installs 4
Ruby gems install slowly because rdoc and ri documentation are generated as the gems install. Since this is rarely used, why not turn it off? I always check the internet for up-to-date docs anyway, which give me a chance to see if there's a newer version of the gem out.
To turn these off, modify your ~/.gemrc file and add the line:
gem: --no-rdoc --no-ri
ActiveRecord-driven global configuration plugin for Rails 1
I just released a simple plugin called global_config for keeping track of application configuration settings in your database.

The nice thing about it is that it uses method missing on a GlobalConfig class to dynamically grab and set settings. For example, if you had two settings called admin_email and memcached_server_ip, you could fetch them like so:
GlobalConfig.admin_email # => 'admin@example.com' GlobalConfig.memcached_server_ip # => '127.0.0.15'
You can also set the values in migrations (or anywhere) like so:
GlobalConfig.admin_email = 'webadmin@example.com'
The next thing I'd implement is an admin page for managing the configuration settings. If there's some interest in it, I may add this to the plugin (lemme know).
Caching support is built in; Memcached is recommended.
Methods are objects
my_string = "test" size_method = my_string.method(:size) size_method.arity # 0 size_method.call # 4
how could conditionals be evil?
If you've seen the Anti-If Campaign, and overheard talk about the "if" statement being evil, you might be thinking "Why? It's a core part of any language, and you couldn't write a program without them", and you'd be right.
The conditional is not evil itself, it smells of evil. The conditional invites evil not because of what it does, but because of what it shouldn't be doing. It's an indication of a code smell, and should be a metric that you track. If you don't track it, you need to at least know what to watch for.
For example: I was working on my code today, and I found a constant that violated the Single Responsibility Principle. It managed both A) the various pages in a checkout process, and B) which "next" and "previous" buttons displayed on each page.
STATES = {
:details => [nil, :payment],
:payment => [:details, :final_review],
:final_review => [:payment, :confirmation],
:confirmation => [nil, nil]
}The subtle problem here is that the confirmation page doesn't have a back button, but the application can easily send the user back a page if the payment processing fails for some legitimate reason (Maybe the payment gateway is down?). This seemingly innocuous violation of SRP caused an explosion of IF statements throughout the application. One of the best examples of this is:
if !@checkout.valid?
if @checkout.state.previous.present?
@checkout.state.previous!
elsif @checkout.state.is_confirmation?
@checkout.state.set_state(:final_review)
end
endThree conditionals just to move back a page when there are errors. Once I removed the violation, the code nicely cleaned up to a single line with one reasonable conditional:
@checkout.state.previous! if !@checkout.valid?Conditionals themselves are not evil, they're needed to get the job done. Take a good look at your code, though; if you see a lot of "if", "case", ternary, or other conditionals, you likely have a problem somewhere. Move the conditional in to the class that's responsible for it, avoid case statements that do something different depending on some enumerator value or state.
Conditionals smell a little. In numbers, they outright stink!
Running migration code in the console
This is a useful debugging/testing/fixing tool, but not a best practice for developing apps. So be careful with it. :-)
git pull --rebase 2
rather than using git pull, which merges remote changes in to your local branch, learn when to use git pull –rebase to keep your history and logs clean; but only if you haven’t pushed the commits anywhere yet!
git bisect
Ok, this is my first screencast, I think I may move to video blog format… Feedback welcome.
I know it’s hard to read, you may want to make it full screen. I’ll do better on the next one. :)
paranoia and split personalities, for your models! 1
is_paranoid is exactly what I’ve been looking for. It’s like acts_as_paranoid, but up to date with ActiveRecord’s new default scopes, which simplifies the gem quite a bit.
One idea I really like was brought on by seeing this example:
class Pirate < ActiveRecord::Base
is_paranoid :field => [:alive, false, true]
end
class DeadPirate < ActiveRecord::Base
set_table_name :pirates
is_paranoid :field => [:alive, true, false]
end
Here we have the same table acting like two models based on a default scope. So simple, yet brilliant. Why didn’t I think of this earlier?
Consider this:
class User < ActiveRecord::Base
is_paranoid
end
class UserArchive < ActiveRecord::Base
set_table_name :users
end
You could use the User model for every day use, and if you need to specifically look at inactive users, you use UserArchive. Spectacularly to the point. Of course this has uses outside the scope of is_paranoid.
CabooseConf is a failure 4
If the purpose of CabooseConf is to get experienced Ruby developers together to hack out great things together, it’s a failure.
While about half of the tables at CabooseConf have developers at them at this second, the majority of them are chatting and browsing the web. The anti-conf could use some leadership and direction; even a list of projects people are working on and who to contact.
As it stands the open-source community is getting more benefit from random people coding while ignoring the sessions they’re sitting in.
Lame.
