Living on the Edge of Rails, Part 3

The inside look at Edge Rails rumbles on.

I’ve been looking at a lot of Rails code lately. In fact, I’ve seen a wide gamut of style, knowledge, and craftsmanship in the last week alone.

One developer is obviously new to Ruby; Rails doesn’t cloak verbosity. Another developer uses tried-and-true Rails paradigms. The code is clean and it works, yet could be improved with some of the more recent conveniences. Another developer is obviously quite skilled; I am still sleuthing that code, trying to determine where all sorts of cool stuff came from. It’s code that makes me think I’ve lived under a rock for the past year.

Here, in the third installment of a series, I continue to unearth what’s new in Edge Rails and recent releases. Today’s topic: Convenience.

The Dreaded Nil

One of my favorite features of Ruby and by extension Rails is chaining. In one expression, I can refer to a method, a named scope, and a dynamic finder. The trouble is the value nil. If nil appears anywhere amidship, the application goes belly up with an exception. One workaround is to rescue and continue.

joes_teacher = Student.find_by_name(  'joe'  ).home_room.teacher rescue nil

rescue does the trick, but it could mask more serious errors inadvertently. Press keyboard to head. Bang. Repeat.

But, fear not. A fairly recent addition to Rails will prevent you from damaging your keyboard. The feature is aptly named try and it’s available on every Object. If you try() to invoke a method that doesn’t exist, try() simply returns nil. Thus, the chain above could be rewritten like this:

joes_teacher = Student.find_by_name(  'joe'  ).try( :homeroom ).try(: teacher )

If Joe cannot be found among the roster and yields nil, the attempt to call homeroom() yields nil, prompting the attempt to call teacher() to also yield nil. You can also pass arguments with try, so this is valid as well:

joes_classroom = Student.find_by_name( 'joe' ).try( :period, 3 ).try( :classroom )

The alternative to try()? Write code like this:

joe = Student.find_by_name(  'joe' )
joes_teacher = joe ? joe.homeroom.teacher : nil

Dynamic Scopes

The dynamic finder of the previous example conserves development time. There is no need to write extra methods to query an individual field or combinations of fields in your model. Rails provides the machinations to pull…

Shirt.find_all_by_maker_and_color_and_size( :maker => 'Brioni',
  :color => 'blue', :size => 50 )

Default and named scopes also provide shorthand to affect query results.

class Student < ActiveRecord::Base
  default_scope :order => 'last_name ASC'
  named_scope :seniors, :conditions => { :year => 'senior' }
end

The default scope imposes a sort order on all results from Student.find*(). The named scope invents the method Student.seniors() to be the analog of Student.find(:all, :conditions => { :year => 'senior' }. Ultimately, the combination of scopes produce a query such as SELECT * FROM `students` WHERE `year`='senior' ORDER BY `last_name` ASC for Student.seniors(). Again, which code would you prefer to write and read?

You can now scope dynamically, too. This code…

Student.scoped_by_year( 'senior' ).find( :all )

… is the equivalent of the named scope, albeit more wordy. Which is better? Named scopes or dynamic scopes? Both can take arguments and both can be chained. Your own style might dictate which technique to use. Certainly, a dynamic scope is the only option if you assemble the name of the method to call.

Student.send( "scoped_by_#{option1}_#{option2}.avg(:gpa)", value1, value2 )

However, I am not sure how contrived that last bit of code is.

Controllers Now Under Control

Rails has provided RESTful controllers for some time, but the typical code for such a controller is dreadful and repetitious. In general, every RESTful controller looks something like this:

 def create
    @student = Student.new(params[:student])

    respond_to do |format|
      if @student.save
        flash[:notice] = 'Student was successfully created.'
        format.html { redirect_to(@student) }
        format.xml  { render :xml => @student,
          :status => :created, :location => @student }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @student.errors,
          :status => :unprocessable_entity }
      end
    end
  end

Edge Rails does away with all that cruft and reduces the same controller method to this:

respond_to :html, :xml

def create
  @student = Student.new(params[:student])
  flash[:notice] = 'Student was successfully created.' if @student.save
  respond_with(@student)
end

respond_with() deduces what format is required and attempts to find a view associated with the action and the format. For instance, in the case of XML, the respond_with() in index() checks for app/views/student/index.xml.erb. If the view template cannot be found, respond_with() falls back to to_xml, if that’s an option.

And Validations for All

Finally, all the features of Rails’s validation suite are now available to any object. A new module called ActiveModel::Validations encapsulates the code. You can now write something like this:

class Rocket
  include ActiveModel::Validations

  validates_presence_of :astronauts
  ...
end 

# r = Rocket.new
# puts r.valid?
#   false
# puts r.errors
# { :astronauts => [ "cannot be blank" ] }

Notice that Rocket is not an ActiveRecord.

Jumping the Track

Tomorrow we change tracks a little and look at new tools, gems, and plugins that are changing Ruby and Rails development.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62