Friday, October 30, 2009

Fast Online Documentation - GotAPI.com

When I'm coding and have an API question, I have one and only one demand from my documentation source -- speed. I want to be able to quickly find the information I seek. "Now. I want it now."

I use PDF versions of my favorite books and the generated Ruby 2.3.4 API documentation locally, which is all good, but I found something that may be even better. GotAPI has a clean interface, is wicked fast and utterly intuitive. It even holds your recent searches.

In addition to lightning fast Ruby and Ruby on Rails look ups, it has tabs for HTML, Javascript DOM and CSS; all the things I need during RoR development!

I've added it to my browser bar so I can get to it quickly and it's rapidly becoming my reference tool of choice. Highly recommended.

Saturday, October 10, 2009

Conditional state transitions with AASM

Also part of the same state machine I wrote about in "Chaining state transitions with Acts As State Machine (aasm)" required two different types of behavior for a state transition.

The task reviewer state was optional and would be triggered only if selected. I represented that state as a "use_reviewer" boolean column in the object. If use_reviewer == true, then the object transitions from task_created to "to_reviewer", otherwise it needed to transition to "to_writer"

The solution to this is rather straight forward. Use the :guard option in the aasm_event definition.

Here's the code:

aasm_event :created do
transitions :to => :to_reviewer, :from => [:task_created], :guard => Proc.new {|p| p.use_reviewer == true }
transitions :to => :to_writer, :from => [:task_created]
end

The event causes the object to transition to the "to_reviewer" state if the objects "use_reviewer" is true, otherwise it transitions to "to_writer". Here's the code evidence from script/console:

>> a = Assignment.new
=> (Assignment object)
>> a.use_reviewer = true
=> true
>> a.save
=> true
>> a.created!
=> true
>> a.aasm_current_state
=> :to_reviewer

Now we start over:
>> a = Assignment.new
=>(Assignment object)
>> a.save
=> true
>> a.created!
=> true
>> a.aasm_current_state
=> :to_writer

Chaining state transitions with Acts As State Machine (aasm)

Have you ever had state transition requirements that required automatic chaining of transition changes? I've got a system that transitions to a "to_reviewer" state after it has been created.

In the "to_reviewer" state, a group of reviewers are presented the assignment and they may accept the assignment, or a project manager may assign the assignment to a reviewer: in either case, as soon as there is a reviewer assigned, the assignment transitions to the "reviewer" state.

However, if the system already has a reviewer assigned at the "created" event, then it should cascade the state transition change and go directly to "reviewer" without staying in "to_reviewer"

Having selected the Rubyist AASM gem to implement my state machine in this Ruby on Rails project, I expected that I would be able to use the :after options of the aasm_state statement. I got that idea from the README.rdoc for version 2.1.1, which lists "event:after" in the callback chain.

However, the :after callback is called on an event (aasm_event) and I can't find documentation on how to hook into that callback. Digging into the the code, I found the following syntax works correctly in my Assignment object (derived from ActiveRecord). The solution is to use the :after_event option in the aasm_state definition statement.

This first part defines the states:
aasm_initial_state :task_created
aasm_state :task_created

aasm_state :to_reviewer, :after_enter => :check_reviewer
aasm_state :reviewer

This next part defines the events:
aasm_event :created do
transitions :to => :to_reviewer, :from => [:task_created]
end

aasm_event :task_review do
transitions :to => :reviewer, :from => [:to_reviewer]
end

This last part is the definition of the :after_enter method:
def check_task_reviewer
task_review unless reviewer_id.blank?
end


Now, when the object is created, the aasm_current_state is "task_created". If the object receives the "created" event, it transitions to the "to_reviewer" state. However, after the object enters the "to_reviewer" state, the "check_task_reviewer" method advances the state again if a reviewer as been assigned (indicated by the presence of the reviewer_id).

Checking the code in the console, we get:

>> a = Assignment.new
=> (Assignment object information)
>> a.reviewer_id = 5
=> 5
>> a.save
=> true
>> a.created!
=> true
>> a.aasm_current_state
=> :reviewer

And we can see that the object transitioned through the "to_reviewer" state an on into the "reviewer"state.

Thursday, October 08, 2009

The Ruby Toolbox

I've had the good fortune to stumble across The Ruby Toolbox.

This delightful site is filled with collections of helpful, nifty plugins and gems that help make the life of any Ruby and/or Rails developer much more pleasant. There are some 75+ different categories that deal with ActiveRecord, Backups, Code Metrics, CSS Frameworks, E-Commerce, Game Libraries, Gem Creation, and on, and on.

Not only are projects, gems & plugins listed, but they are rated in terms of how much attention the project is receiving. The most popular projects are listed at the top with an attractive scale along side to the right to help you compare the popularity of different options.

If you're doing Ruby and/or Rails development and haven't visited this site, then you really deserve to do yourself a favor. I'm headed there now!

Thursday, October 01, 2009

Rails Trick: Access an ActiveRecord field in a loop in a view

I came up with a little trick that I want to share, even though I'm sure there are better ways of doing this (perhaps someone will suggest them!)

OK, picture this: We have over half a dozen different types of workers -- Writers, Sr. Editors, Editors, etc. -- and we're going to pay them. So, in the database we have things like writer_payment, sr_editor_payment and editor_payment.

In the edit view, we have something like this (actually, there are many more cells, but I've omitted them for clarity):



And, I fully expect that we will be adding many more workers, so we can look forward to much cutting and pasting.

Well, I wanted to come up with a way of declaring an array, iterating through it and creating each row automagically.

My first thought was this:



But this doesn't work at all. The ActiveRecord attribute can't be built dynamically with string interpolation in the view. Similar efforts failed miserably.

But then I took at look at the ActiveRecord::Base class and noticed the attributes method. This got me a-thinkin' and here's what I came up with. Get the attributes from the ActiveRecord. Build a string with the array value and the "_payment", use straight array access to get the payment value



And, Holy Smakeral -- it works!