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.
Comments on "Living on the Edge of Rails, Part 3"
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 Cueb Answers
The other day, while I was at work, my sister stole my apple ipad and tested to see if it can survive a twenty five foot drop, just so she can be a youtube sensation. My apple ipad is now destroyed and she has 83 views. I know this is completely off topic but I had to share it with someone!
That is the suitable blog for anyone who desires to seek out out about this topic. You understand so much its almost onerous to argue with you (not that I truly would want…HaHa). You positively put a brand new spin on a topic thats been written about for years. Nice stuff, simply nice!
Very interesting information!Perfect just what I was looking for!
you are really a good webmaster. The site loading speed is incredible. It seems that you’re doing any unique trick. Also, The contents are masterwork. you have done a great job on this topic!
t0zQeO fbtvmeawwicx, [url=http://hmfhvcgnlpgw.com/]hmfhvcgnlpgw[/url], [link=http://oqlhqvzjedvm.com/]oqlhqvzjedvm[/link], http://ckoikxvmddtd.com/