friendly_fixtures plugin on github

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 plugin on github

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.

delegate unless nil

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.

creating YAML fixtures from existing data

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.

valid model attribute factory test helpers

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 case
  
class 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

		
		
	

friendly_fixtures plugin for Rails

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

how to use view helpers in functional tests

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.

shoulda: a spec framework without the hype

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.

render a partial from inside a model

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

controller/action specific CSS - Part 2

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 */

assert_incremented_by test helpers

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


assert fixtures are valid

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

verify all AJAX actions as XHR

put this in your controllers/application.rb

  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

rails helper for google maps

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&amp;v=2&amp;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; }

Converting a hex string to ascii in rails

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

controller/action specific CSS

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

Green goodness with autotest + growl

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:

rails_fail.png rails_ok.png

For the newer version of Zentest (3.5.1 and greater) substitute the following line in the previous code snippet:
   output = at.results.last.slice(/(\d+).*errors/)
actionmailer ActionView ajax alphadecimal audio autotest BDD blocks capistrano ssh ruby controller css dashboard widget delegate dog puppy naming name DRM email obfuscation exceptions factories-and-workers factory pattern filemerge find and replace finder fink fixtures fun gem git google maps helper helpers imagemagick Intertrust javascript logo math meetup model openssl OS X patch Pioneer Electronics plugin polymorphism prototype.js rails rails gotcha railsconf rake rmagick RSA encryption ruby script shoulda subversion SyncTV TDD testing textmate tricks unique hashes unix shell validation view yaml zebra stripes