Tuesday, August 14, 2007

Links

Dynamic Attribute-based Finder Extensions
szeryf has written a nice little exposé on how you can extend ActiveRecord::Base to add _or_ and _not_ operators to the dynamic finders that rails provides you with.

You can write the following types of finders out of the box with rails:

User.find_by_login_and_status(some_login, 1)
User.find_by_login_and_status_and_role_(some_login, 1, role)

The additional extensions as described in the article adds the following to the repertoire above:

User.find_by_login_and_status_or_role(some_login, 1, role)
User.find_by_login_and_status_not_role_(some_login, 1)

The two statements above would then result in the following SQL, respectively:

login = ? and (status = ? or (role = ?))
login = ? and (status = ? not (role = ?))

rparsec (the union of ActiveRecord :select and :include)
As you most probably already know you use :select in a find to modify the fields that are in your result set and :include to specify that related tables are loaded via joins to provide improved performance. Unfortunately you cannot use either of these two together due to the limitations imposed by the ActiveRecord implementation.

:select meets :include (or a pitch for rparsec) is an interesting article by Charlie Savage which suggests using a SQL SELECT parser to provide the required functionality.

He goes on to suggest doing this with the rparsec parser combinator framework.

The Controller Formula
Nick Kallen provides a lucid look at how one can produce poetic controller code.

Creating Multiple Models in One Action
This article is a followup on the previous one elaborating on the method that can be used to create multiple models from one action.

It covers the simplistic case where one model is simply created based on the creation of the other (when crating a group model, the creating uses needs to be the first member of the group) and the more complex case where there is a dependancy relationship between two models that needs to be enforced (a cyclops creation cannot succeed if the creation of the eye does not succeed).

Sunday, August 12, 2007

Prototype, IE and Edge Rails Failures

I recently wrote a little conceptual file upload application with scaffold_resource that used the iframe remoting pattern with some baked-in AJAX goodness to minimise the amount of data returned from the server as well as making the UI a lot more snappy.

Everything went well and tested a-OK in Firefox. Unfortunately my default development platform does not support IE so I only tested the app in IE a little later. To my horror IE rendered the page differently as well as spewing the following JS error:

Line: 1629
Char: 9
Error: Invalid target element for this operation.
Code: 0
URL: <REMOVED>

Prototype goodness wherefor art thou?
A few concise questions to the Oracle of Google and I found a thread started by Rob Sanheim which detailed the same problem I was seeing.

A fix, that worked for many of the thread readers, was proposed by Andy (12 December 2005 @ 1pm) in the same thread to deal with the way in which prototype inserts content in a tbody or tr tag.

So, off I went and upgraded my app to the latest Edge Rails using:

rake rails:freeze:edge

Edge Rails will you be the end of me?

Starting my app up I got a similar nasty to this:

/Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- active_resource (MissingSourceFile) from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:495:in `require' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:495:in `require' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `require_frameworks' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `each' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `require_frameworks' from ./config/../vendor/rails/railties/lib/initializer.rb:88:in `process' ... 8 levels... from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/gems/1.8/gems/capistrano-1.4.1/lib/capistrano/cli.rb:12:in `execute!' from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/gems/1.8/gems/capistrano-1.4.1/bin/cap:11 from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/bin/cap:16:in `load' from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/bin/cap:16

Seems that when you're using Edge Rails you need to use the active resource gem in tandem or you world will turn pear-shaped. Installing the gem, with dependancies did the trick:

gem install -y activeresource --source http://gems.rubyonrails.org

IE: Dark side of the moon
So, I have now upgraded to the latest prototype lib (via the Edge Rails upgrade) and got Edge Rails to run with the active_scaffold and active resources I was using in my app.

Was everything fine then and did I get to live a more fulfilling existence? NAY!

Back to the entry by Rob Sanheim and I noticed that Chris Nolan reported that the prototype lib had been fixed but that he was still experiencing the same issue.

There is a boat and I feel that Chris and I are both in it together, brothers in misery.

While debugging the issue with trusty old JS alert() he found that he was trying to insert content at an id that belonged to a table tag which was not supported. The tags supported for this were of course tbody and/or tr.

Even though the tbody tag seems to be optional according to the W3C IE still does not allow you to insert the content based on a table id.

The simple addition of the required tbody tags cured all.

Sunday, August 5, 2007

Turning responsibility inside-out via delegation

5 August 2007

Turning responsibility inside-out via delegation

What is the delegation pattern? You find this where you have an object that expresses a certain behaviour externally but internally defers, or, delegates the responsibility for implementation to another object in an inversion of responsibility.

Turning responsibility inside-out
Ruby provides five ways to accomplish this: three (SimpleDelegator, DelegateClass and Delegator) encapsulated in the delegate library and the remaining two (Forwardable and SingleForwardable) via the forwardable library.

Let's use a queue data structure that delegates to an array to illustrate the various ways of accomplishing delegation.

SimpleDelegator
This is the simplest way to accomplish delegation. You simply pass an object to the constructor and all methods supported by the object will be delegated.

_This object can be changed later._

require 'delegate'

class Queue
def initialize
@sd = SimpleDelegator.new([]) # we delegate to an array object
end

def enqueue(element)
@sd.push(element)
end

def dequeue
@sd.shift
end
end

q = Queue.new
q.enqueue(10) # [10]
q.enqueue(20) # [10, 20]
q.dequeue # [20]

If you want to change the object you're delegating to you just use __setobj__(obj). You should just keep in mind that this does *not* cause SimpleDelegator’s methods to change which means that you should only be delegating to objects of the same type as the original delegate to avoid nastiness.

DelegateClass
If SimpleDelegator does not spin your propeller then the next step would be to look at DelegateClass. Using the top level DelegateClass method to setup delegation through class inheritance is considered more flexible and is seemingly the most common use for this library.

require 'delegate'

class Queue < DelegateClass(Array) # we delegate to an array object
def initialize(arg=[])
super(arg)
end

alias_method :enqueue, :push # alias_method sets up the method aliasing for us
alias_method :dequeue, :shift
end

q = Queue.new
q.enqueue(10) # [10]
q.enqueue(20) # [10, 20]
q.dequeue # [20]

Delegator
The final tool from the delegator library is Delegator which provides you with full control over the delegation scheme. The contrived example below is derived from the SimpleDelegator’s implementation.

require 'delegate'

class QueueDelegator < Delegator # inherit from the Delegator class
def initialize(obj)
super # pass obj to Delegator constructor
@_sd_obj = obj # store obj for future use
end

def __getobj__
@_sd_obj # return the object we are delegating to
end

def __setobj__(obj)
@_sd_obj = obj # change delegation object, a feature we're providing
end
end

The conventional wisdom here however is that you should most likely be using the forwardable library instead of Delegator.

Fowardable
If you need class-level delegation this is your beast of burden.

require 'forwardable'

class Queue
extend Forwardable

def initialize(obj=[])
@queue = obj # delegate to this object
end

def_delegator :@queue, :push, :enqueue
def_delegator :@queue, :shift, :dequeue
def_delegators :@queue, :clear, :empty?, :length, :size, :<<
end

There are a few things to take note of here. First, def_delegator is used to set up the delegation relationship between the method call, the delegated object and the method to call on the delegated object.

Second, notice the syntax (:@queue, instead of @queue or :queue) to specify the delegated object we're defining methods for. This is simply an artefact of the way that Forwardable is implemented.

SingleForwardable
Where Forwardable provides class-level delegation, SingleForwardable provides object level delegation. For this example I'll simply copy the example provided in the library documentation.

require 'forwardable'

printer = String.new
printer.extend SingleForwardable # prepare object for delegation
printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
printer.puts "Howdy!"

Epilogue
Using DelegateClass and Forwardable for your delegation needs will most likely cover most of the cases you may end up needing to implement the delegator pattern.

Did we default or not?

You may sometimes find yourself having to distinguish whether a method attribute was supplied externally or taken from the default specified in the method definition.

Let's say, for example, that you want to warn a user when they have neglected to set an attribute but still continue with execution. Here is a snippet that would accomplish this:

irb(main):060:0> def some_method(first, second=(flag=true; '2nd'))
irb(main):061:1> p "Default value #{second} used for unspecified parameter 'second'" if flag.inspect == "true"
irb(main):062:1> end
=> nil
irb(main):063:0> some_method(1,2)
=> nil
irb(main):064:0> some_method(1,'2nd')
=> nil
irb(main):065:0> some_method(1)
"Default value 2nd used for unspecified parameter 'second'"
=> nil
irb(main):066:0>
Can you work out what is going on in the method parameter declaration?

All that's happening is that the code in the round brackets after the equals sign defines a local variable _flag_, sets its value and returns the default value we want to set it to.

Ruby rocks!

About Me

My photo
I love solving real-world problems with code and systems (web apps, distributed systems and all the bits and pieces in-between).