I previously posted about my friendly_fixtures plugin
It’s now available at github: http://github.com/dfl/friendly_fixtures/tree/master
factories-and-workers at github
Factories and Workers is a Rails plugin originally written by Nathan Herald @ myobie.com after being inspired by Dan Manges’ blog post on factories. It uses some slick metaprogramming to generate factory methods for your ActiveRecord models. Over the past few months I’ve refactored the code, added a bunch of new features, and most importantly wrote some tests!
See it in action:
>> factory :monkey, { :name => "George" }
=> #<FactoryBuilder:0x1a4f2a8>
>> valid_monkey_attributes
=> {:name=>"George"}
>> build_monkey
=> #<Monkey id: nil, name: "George">
>> build_monkey( :name => "Bob" )
=> #<Monkey id: nil, name: "Bob">
>> Monkey.count
=> 0
>> create_monkey
=> #<Monkey id: 1, name: "George">
>> Monkey.count
=> 1
There’s much more info in the README.
An issue came up yesterday that involved an strange nil error, where an attribute was being delegated to a nil association.
I recalled reading this ticket opened by court3nay from caboo.se and thought it might come in handy for others if I put it in the blogosphere.
The ticket ended up being closed because it was deemed unnecessary. Someone with the moniker “protocool” (presumably trevor at http://protocool.com/) shared the final solution which works out of the box with no patches. It goes something like this:
delegate :some_method, :to => '(some_association or return nil)'
The cool thing about this solution, over the proposed patch, is that you can do whatever you like (rather than simply return nil), such as return a default value or raise a custom exception.
I just came across this cool tip from Tom Preston-Werner of rubyisawesome.com. Basically, if you end a mysql commandline query with \G instead of a semicolon, you’ll get a nicely formatted query that is suitable for pasting into a YAML file.
But what if you are not using mysql? We have a project with postgres. There’s probably a psql command to do something similar, but there is also a database agnostic way: Just use the rails console! :)
>> puts User.find(:all).to_yaml
---
- !ruby/object:User
attributes:
status: unverified
salt: L52b2pxGCL
can_invite: "0"
hashed_password: L50/yIQjPCBiU
is_admin: "0"
id: "478674008"
first_name: Mickey
last_name: Mouse
watchlist_by_email: "0"
created_at: 2008-01-30 15:34:26
email: mickey@mouse.com
I’ve been playing with block helpers for some time now, and I love them for their semantic goodness and clarity.
I know that Rails 2 has an assert_email method, which plays nicely with assert_select assert_select_email method.
However, as far as I could tell, this doesn’t allow you to assert any of the headers such as to, from, etc.
Enter assert_emails_sent:
def test_send_crash_data
device_id = 2
assert_email_sent :to => AdminNotifier::CLIENT_CRASH_EMAIL,
:from => AdminNotifier::SYSTEM_EMAIL,
:subject => "Application crashed.",
:body => /device id #{device_id} crashed.+whoops I crashed/ do
AdminNotifier.deliver_send_crash_data('whoops I crashed', device_id)
end
end
- One cool feature is that the values of :to and :from will be automagically sent #email, so you can just say :to => @user, as long as @user.responds_to?(:email).
- Another bonus feature is that you can use either strings or regexps.
grab the code here.
I was suffering with a problem running tests inside of textmate on a Rails 2 project, until I found this comment by David Vrensk at the bottom of this blog post by Rob Sanheim:
Posted by David Vrensk 18 January 2008 @ 9am
I think the easiest solution can be gleaned from the TM ticket that you link to (http://macromates.com/ticket/show?ticket_id=F4DA8B03). I just modify test/test_helper.rb in my current projects so that it starts with
$:.reject! { |e| e.include? ‘TextMate’ }
No patching the distros, and svn still works the way it should.
I like to use factory test helpers to generate attributes which I then pass to ActiveRecord. The default output is expected to be valid (hence the usage with create-bang below). If you want to override any of the defaults, you can simply use Hash#merge.
Here’s an example, using shoulda. It should be pretty clear how it can be applied to vanilla Test::Unit (or the testing framework du jour ;).
## I put this in test_helper.rb so I can use it across all tests. ## You could put it in the actual derived test class as well. def valid_user_attrs( unique = DateTime.now ) { :email => "{unique.hash.abs}@example.com", :first_name => "dummy", :last_name => "user_#{unique}", :profile_attributes => valid_profile_attrs } end def valid_profile_attrs ... end## Here’s the actual test caseclass UserTest < Test::Rails::TestCase context "Two Users" do setup do @user = User.create!( valid_user_attrs ) @user2 = User.create!( valid_user_attrs.merge( :email => "dummy123@dummy.com" ) ) end should "not be friends" do assert !@user.friends.include?( @user2 ) assert !@user2.friends.include?( @user ) end end end
In a previous post, I posted a monkey patch to enhance the behavior of fixtures. I converted it into a plugin format for convenience and testing purposes. I am still aiming to submit it as a patch to Rails 2.0. I’d appreciate any feedback!
get it here:
svn export --username=public svn://internautdesign.com/public/plugins/friendly_fixtures vendor/plugins/friendly_fixtures
UPDATE: now available at github: http://github.com/dfl/friendly_fixtures/tree/master
from the README:
FriendlyFixtures
This plugin is a simple extension to add some cool features to the fixtures macro in Test::Unit. It is intended for Rails 2.0, but works with 1.2.3 as well.
It enables you to:- load dependent models, which are found by object introspection on a model’s ActiveRecord associations.
- assert that all the loaded fixtures are valid. This can be very helpful in finding bugs.
Example Usage:
class SomeTest < Test::Unit::TestCase
fixtures :user, :dependencies => true, :validate => true
end
I recently wrote a functional test for an AJAX callback action. I wanted to assert that the RJS replaced some text with a particular string. This string is generated by a view helper. To keep this flexibility, I wanted to call the helper from the test.
It turns out to be quite simple. All you have to do is mix-in the helper to your TestCase class, like so:
class CompanyControllerTest < Test::Rails::TestCase
include CompanyHelper
...
end
In a previous post, I mentioned Gabriel Gironda’s snippet showing how to use view helpers in a controller. Calling view-specific helpers from the controller is a particularly useful technique in AJAX callback methods where you need to replace some HTML. The alternative is rendering a partial, which I felt was cumbersome and overkill if you just need to replace a single string of text.
However, the above technique doesn’t work out of the box if you need to use a custom helper that you have written in your app/helpers directory tree. The trick is to use the extend method to mix-in your custom helper modules.
Here is an example, where I extend the help singleton object with the CompanyHelper module, in order to be able to the text_for_action and text_for_notice methods.
def ajax_add_to_portfolio
help.extend ::CompanyHelper
raise "Company doesn't exist!" unless @company
kind = params[:category]
raise help.text_for_action( kind )+" already performed!" if @company.in_portfolio?( @current_user, kind )
@current_user.portfolio_items.create!( :company_id => params[:id], :category => kind )
action_text = help.text_for_action( kind, :past_tense => true )
notice_text = help.text_for_notice( kind )
render :update do |page|
page.flash_then_fade :success, action_text
page.replace_html kind, notice_text
end
rescue Exception => e
handle_exception e
end
this replaces my earlier post about testing for valid fixtures. Rails 2.0 patch coming soon…?
UPDATE: This monkey patch has been replaced by a plugin (with tests!): friendly_fixtures.
Rathole, a fixtures plugin so sweet it’s been incorporated into edge rails, is great—but unfortunately it doesn’t support polymorphic associations. That is, until now! :)
Get the patch: here.
UPDATE: I learned that the author, John Burnette, has renamed the project to foxy fixtures. He also massaged my patch into a contribution for rails 2.0: Ticket 10183.
There is some discussion that this patch is not necessary.
This morning I just discovered Shoulda, a really sweet testing framework that gives context and behavior without the whizz-bang syntax of RSpec and test-spec. Check it out!
My only complaints so far:
- the example code on their homepage is missing “do” keywords after some of the “should” statements. oops!
- I can no longer run an individual test in textmate with APPLE-shift-R, as there is no literal method to run, just a block. Hopefully this will be fixed with a forthcoming shoulda textmate bundle.
Have you ever wanted to render a partial to a string from inside a model?
This solution was inspired by this snippet showing how to use view helpers in a controller. It’s clean and simple, no need to mess with ERB or metaprogramming!
class Model
class Helper < ActionView::Base
include Singleton
end
def help
Helper.instance
end
...
def summarize
## Note that we have to use the FULL path here!
help.render( :partial => "#{RAILS_ROOT}/app/views/notifications/text_order_summary", :object => self )
end
alias_method :to_s, :summarize
end
Have you ever wanted to refactor something into the model ( skinny controller, fat model ) but didn’t know how to deal with your flash notifications?
Here’s a solution that I cooked up:
module FlashExceptions
class FlashException < StandardError; end
class FlashError < FlashException; end
class FlashNotice < FlashException; end
class FlashSuccess < FlashException; end
end
###########################################
class Model < ActiveRecord::Base
include FlashExceptions
def self.do_something! opts = {}
raise FlashError, opts[:error] if opts[:error]
raise FlashNotice, opts[:notice] if opts[:notice]
raise FlashSuccess, opts[:success] if opts[:success]
end
end
###########################################
class Controller < ApplicationController
def action
Model.do_something! :error => 'oops!', :notice => 'ahem.', :success => 'yay!'
rescue FlashException => e
flash[ e.class.underscore.split('_').last ] = e.message
end
end
I wrote a previous entry about a pattern for making CSS rules specific to controllers and actions.
Unfortunately this didn’t work in IE. Here is an alternative solution, which is cross-browser compatible:
<body id="<%= controller.controller_name %>" class="action-<%= controller.action_name %>">
(I chose to prefix the action name with action, to avoid any potential namespace contamination )
Here are the new CSS rules:
body#register {} /* all actions in controller */
body#register.action-step_1 {} /* only step_1 action */
I wrote these test_helper methods after being inspired by Zack Chandler’s assert_toggled method
def assert_incremented_by( number, object, method, *args )
initial_value = object.send(method, *args)
yield
object.reload if object.respond_to? :reload
assert_equal initial_value + number, object.send(method, *args)
end
def assert_decremented_by( number, object, method, *args, &block )
assert_incremented_by -number, object, method, *args, &block
end
def assert_no_change_in( object, method, *args )
initial_value = object.send(method, *args)
yield
object.reload if object.respond_to? :reload
assert_equal initial_value, object.send(method, *args)
end
example usage:
def test_ajax_create_network__for_city_is_successful_and_updates_form_id_value
assert_incremented_by 1, City, :count do
xhr :post, :ajax_create_network, :type => 'City', :object => { :name => 'Dummy', :state => 'CA' }
end
assert_response :success
assert_match /\.value='\d+'/, @response.body
end
...
def test_after_create__watchlist_notification_on_posting
assert users(:david).watchlist_companies.include?( companies(:HP) )
assert_incremented_by 1, WatchlistNotification, :count, :conditions => {:kind => 'posting_commented'} do
postings(:rodney_HP).comments.create!( :body => 'booyashaka!', :user => users(:greg) )
end
end
def test_after_create__watchlist_notification__doesnt_happen_on_profile
assert users(:david).watchlist_companies.include?( companies(:HP) )
assert_no_change_in WatchlistNotification, :count do
users(:rodney).profile.comments.create!( :body => 'booyashaka!', :user => users(:greg) )
end
end
put this in your test_helper.rb
def test_all_models_valid model
objs = model.camelize.constantize.find(:all)
objs.each do |obj|
assert obj.valid?, "#{model} #{obj.id} is invalid: #{obj.errors.full_messages.join(', ')}"
end
end
def test_all_models_valid! filename
test_all_models_valid klass = filename.split('/').last.sub(/_test.rb/,'').camelize
end
Now you can create a test like this in each of your your model/unit tests:
def test_valid_fixtures
test_all_models_valid! __FILE__
end
Sweet! :)
Update #1: Thanks to Nicolas Sanguinetti for the tip on how to avoid the use of eval.
I’ve been using this a lot in one of our current projects.. it comes in pretty handy. just create a
<div id="message" style="display: none"></div> in your view.
Here is an example from our login page:
# this goes in your application_helper.rb
# call it from your layout like this:
# <%= flash_for :error %>
# <%= flash_for :notice %>
# <%= flash_for :success %>
def flash_for symbol
# <div id="flash_error" class="message-block error">
# text
# </div>
content_tag( :div, flash[symbol].to_s, :id => "flash_#{symbol}", :class => "message-block #{symbol}",
:style => flash[symbol] ? nil : 'display: none' )
end
# this goes in your controller
def login #http get, ajax post
redirect_to home_url( current_user ) and return if user_logged_in? #already logged in?
if request.xhr?
raise "Please enter a valid email address." unless params[:user][:email] =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
raise "Please enter a password!" if params[:user][:password].blank?
if user = User.authenticate( params[:user][:email], params[:user][:password] )
::ActionController::Base.session_options[:session_expires] = params[:user][:remember] ? 2.weeks.from_now : nil # remember me for 2 weeks if checked
login_user user
ajax_redirect_to home_url(current_user)
else
session[:forgotten_email] = params[:user][:email]
raise "Incorrect email address or password."
end
end
rescue Exception => e
handle_ajax_error e
end
# this goes in application.rb
protected
def ajax_redirect_to url
url = url_for( url ) if url.is_a? Hash
render :update do |page|
page << "window.location = '#{url}'"
end
end
def handle_ajax_error e
render :update do |page|
page.flash_then_fade :error, "Error: <nobr>#{e.message}</nobr>"
page << "Element.scrollTo('flash_error');"
end
logger.error "\n\n#{e.message}"
logger.info e.backtrace.join("\n")
end
# this goes in /lib directory in a file that gets loaded in environment.rb
module ActionView
module Helpers
module PrototypeHelper
class JavaScriptGenerator
module GeneratorMethods
def flash_then_fade symbol, message, seconds=0, fadeout=1
# update message text
replace_html "flash_#{symbol}", message
# hide all notices except current
(%w[ error success notice ] - [ symbol.to_s ]).each do |sym|
hide "flash_#{sym}"
end
dom_id = "flash_#{symbol}"
visual_effect :highlight, dom_id, :duration => 2.0
show dom_id
if seconds.to_f > 0
delay( seconds ) do
visual_effect :fade, dom_id, :duration => fadeout
end
end
end
end
end
end
end
end
def initialize *args
ajax_actions = self.public_methods(false).grep(/^ajax/i)
unless ajax_actions.empty?
self.class.send :eval, <<-RUBY
verify :xhr => true,
:only => [#{ajax_actions.map(&:to_sym).join(', ')}],
:render => {:text => '405 XHR only', :status => 405 }
RUBY
end
super *args
end
I just wrote a rails helper to assist me with adding google maps to my applications. It uses the layout/content_for template engine to insert javascript code into the header.
module ApplicationHelper
def js_for_google_maps( id = "map" )
content_for("header") do <<-JAVASCRIPT
<script src="http://maps.google.com/maps?file=api&v=2&key=#{GOOGLE_MAPS_KEY}" type="text/javascript"></script>
<script type="text/javascript">
Event.observe( window, 'load',
function() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("#{id}"));
var point = new GLatLng(37.763135, -122.4106);
map.setCenter(point, 15);
var marker = new GMarker(point);
map.addOverlay(marker);
}
}
);
Event.observe( window, 'unload', GUnload() );
</script>
JAVASCRIPT
end and return
end
end
The google maps key is defined in the environment files. Here is the one from development.rb corresponding to localhost:3000
GOOGLE_MAPS_KEY = "ABQIAAAASH81C6sj132EpSsZDgoERhTJQa0g3IQ9GZqIMmInSLzwtGDKaBQISjQeH9qFJ5o5SEiH-ulbhhxOPg"
Here is the template code that makes all this work:
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>internaut design - agile software development - san francisco bay area</title>
<%= stylesheet_link_tag 'site' %>
<%= javascript_include_tag :defaults %>
<%= @content_for_header %>
</head>
Don’t forget to define your HTML
<div id="map"></div>
and CSS:
#map { width: 400px; height: 250px; }
I am doing some work with encryption keys and was trying to find a method in ruby that would convert a string of hex values into their ascii equivalent. I am storing the keys in my database as a string. Here is a sample key that I am using:
"4bd3eb6bd171595764ec2050a20382e1"
which is represented in a hex array as:
0x4b, 0xd3, 0xeb, 0x6b, 0xd1, 0x71, 0x59, 0x57, 0x64, 0xec, 0x20, 0x50, 0xa2, 0x03, 0x82, 0xe1
I iterate through my array, 2 characters at a time, thanks to the rails enumerable "ingroupsof". I take the string of two hex values and convert it to their ascii equivalent first by converting the text into hex and then asking for the char equivalent in ascii. I stuff this back into a new string and voila!... I've got my ascii string.
def hex_string_to_ascii str
new_str = ''
arr = str.split('')
arr.in_groups_of(2){|c| new_str << ("#{c[0]}#{c[1]}".hex.chr) }
new_str
end
Here’s a trick that I use to put styles on specific pages, or even better on all the pages in a specific controller—with no extra markup needed!
I call them “controller specific” CSS rules.
In the layout template, I give the body tag a name that is based on the controller attributes:
<body id="<%= "#{controller.controller_name}-#{controller.action_name} %>">
Now in the CSS I can give rule to any of the registration pages pages
(for instance relating to register_controller.rb):
body[id|=register] { width: auto; }
You can also give a rule to a specific page
body[id|=register-step_1] { width: auto; }
For more info, see W3C CSS 2.1 Spec 5.8.1
I got so hooked on redgreen autotest that I wanted to see green in my growl notifications. So I came up with this modification to Labrat’s advice on autotest and growl.
Greg and I decided we don’t like the sticky window for errors, hence we commented out the “-s” parameter. It took me awhile to realize that one can customize the display properties of different message priorities in the Growl control panel, but I prefer to change the image instead of the background color.
Add the following to your ~/.autotest file:
module Autotest::Growl
def self.growl title, msg, img, pri=0, stick=""
system "growlnotify -n autotest --image #{img} -p #{pri} -m #{msg.inspect} #{title} #{stick}"
end
Autotest.add_hook :ran_command do |at|
output = at.results.slice(/(\d+).*errors/)
if output =~ /ns.*[1-9]/
growl "Test Results", "#{output}", '~/Library/autotest/rails_fail.png', 2 #, "-s"
else
growl "Test Results", "#{output}", '~/Library/autotest/rails_ok.png'
end
end
end
(Note: also make sure you comment out the “require ‘autotest/growl’” line at the top, as this replaces it.)
Here are the images that I use:

output = at.results.last.slice(/(\d+).*errors/)
