Archive Page 2

Just a tip for those who like me can’t find it immediately:

ruby script/plugin install http://svn.rubyonrails.org/rails/plugins/in_place_editing


This one looks like a bug!

  # sellers.yml
  shopify:
    name: Shopify

  # products.yml
  pimp_cup:
    seller: shopifi #what if we make a typo here?
    name: Pimp cup

  #foxy_test.rb
products(:pimp_cup).nil?
=> false #fixtures are loaded without problems!
products(:pimp_cup).seller.nil?
=> true #huh seller is not set

products(:pimp_cup).seller_id.nil?
=> false #this is totally wrong
sellers(:shopifi)
=> StandardError: No fixture with name 'shopifi' found for table 'sellers' #I'd prefer it at the fixtures loading time

I just made such a typo, which made me stare to my tests for a while..

Update:

It appears that this lack of checking comes for a reason – basically database foreign key constraints are switched off when fixtures are loaded – to be able to use things like ‘fixtures :all’, a very lovely feature. And this lack of checking seems to be not a big deal after conversion to foxy fixtures is completed – during normal dev cycle problems are seen as they come, mainly one by one.


I like the announced ‘foxy fixtures’ Rails 2.0 idea a lot and tried to use it but found that some things broke immediately.
After some investigation I found that you it doesn’t work if you specify primary key for fixture object that is used in another fixture object.

I.e. this works:

# sellers.yml
shopify:
name: Shopify

# products.yml
pimp_cup:
seller: shopify
name: Pimp cup

# foxy_test.rb
products(:pimp_cup).seller.nil?
=> false #that's right

And this doesn’t:

# sellers.yml
shopify:
id: 8
name: Shopify

# products.yml
pimp_cup:
seller: shopify
name: Pimp cup

# foxy_test.rb
products(:pimp_cup).seller.nil?
=> true #wtf??

And it appears to be even documented in AR’s changelog, so it’s questionable whether it worth a blog entry – well let’s pretend I’m a blogger )


Today I found that code


headers['status'] = '500'

doesn’t do what I want with Mongrel: HTTP Status header is still 200 while with Webrick it’s happily set to 500.I tried it with Rails 1.2.5 and 2.0 and Mongrel 1.1.1 both on Windows and Linux.Luckily using this instead helps (though I liked the previous code more):


render :action => action_name, :status => '500'

Why I wanted to set the goddamn header in first place? Because it seem to be a clean way to tell Ajax helper not to hide a form after failed submit:


<%remote_form_for :receipt, :url => { :action => "add_receipt"}, :success => "$('add_receipt_form').hide();",
    :html => {:id => "add_receipt_form", :style => "display: none;"} do |form| %>

This :success, it relies on HTTP Status header.


It’s hard not to upgrade to a newer version of Rails after reading this.

Also today I discovered some esoteric problem with passing HTTP status and Mongrel so I upgraded just to find out whether it helps. It didn’t but I found the upgrade (from Rails 1.2.5) to be rather smooth and enjoyed the power of test harness of my app. The only issue was with Globalize, I use globalize-for-1.2. When trying to update translated model I got

ArgumentError: wrong number of arguments (2 for 1)

I even went deeper and fixed it, so my app works on Rails 2.0 now )
My patch for Globalize is here.

I also tried Edge Globalize, it didn’t fix this ArgumentError but introduced a bunch of test failures so I reverted to globalize-for-1.2.


While using great acts_as_state_machine plugin at work I needed to make some improvements to it. This example illustrates new :log_transitions option to acts_as_state_machine directive and how it can be used to audit transitions to action_history_items table – I like to have an action history log for objects that travel by states.
I’d be happy to publish my improvements but don’t know yet in what form.

class ExpenseClaim < ActiveRecord::Base
  has_many :action_history, :class_name => 'ActionHistoryItem', :as => :document,
    :order => 'id desc'

  acts_as_state_machine :initial => States::DRAFT, :log_transitions => true

  state States::DRAFT, :owner => Roles::AUTHOR
  state States::MANAGER_APPROVAL_REQUIRED, :owner => Roles::APPROVING_MANAGER
  state States::REJECTED_BY_FINANCE, :owner => Roles::APPROVING_MANAGER

  #...
  event :send_for_manager_approval do
    transitions :from => States::DRAFT, :to => States::MANAGER_APPROVAL_REQUIRED
    transitions :from => States::REJECTED_BY_MANAGER,
      :to => States::MANAGER_APPROVAL_REQUIRED
  end

  event :approve do
    transitions :from => States::MANAGER_APPROVAL_REQUIRED,
      :to => States::FINANCE_APPROVAL_REQUIRED

    transitions :from => States::FINANCE_APPROVAL_REQUIRED,
      :to => States::APPROVED

    transitions :from => States::REJECTED_BY_FINANCE,
      :to => States::FINANCE_APPROVAL_REQUIRED
  end

  #...
  def log_transition(from, to, event, opts)
    user = self.updated_by
    raise "user is not set" if user.nil?
    as_role = self.class.states_table[from].opts[:owner]
    action_history << ActionHistoryItem.new({:from_state => from.to_s,
      :to_state => to.to_s, :action => event.to_s, :user_id => user.id,
      :as_role => as_role.to_s, :at => Time.now})
  end
#...
end

Update

Here’s the patch mentioned: http://pastie.caboo.se/151469, looks to be perfect way to publish it for now )


More info


Acts as State Machine:
http://rubyi.st/2006/1/21/acts-as-state-machine

Ruby on Rails Finite State Machine Plugin: acts_as_state_machine >> rails symphonies:
http://rails.aizatto.com/2007/05/24/ruby-on-rails-finite-state-machine-plugin-acts_as_state_machine/