Many Skinny Methods
This refactoring is based on a topic Marcel and I covered at RailsConf Europe.
Before
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Expense < ActiveRecord::Base belongs_to :payee protected # Nice and concise, but what happens as we add more rules # and how do we write test cases for the four different possible # validation states? def validate errors.add("Not enough funds") if payee.balance - amount > 0 errors.add("Charge is too great") if payee.account.maximum_allowable_charge > amount end end |
After
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class Expense < ActiveRecord::Base belongs_to :payee # Instead of one large validation method, break each individual # rule into methods, and declare them here. validate :ensure_balance_is_sufficient_to_cover_amount validate :amount_does_not_exceed_maximum_allowable_charge protected # These validation callbacks simply add error messages if a particular # condition is met. Each of them can be tested and understood on their own # without having to understand the entire body of the validate method. def ensure_balance_is_sufficient_to_cover_amount errors.add("Not enough funds") if insufficient_funds? end def amount_does_not_exceed_maximum_allowable_charge errors.add("Charge is too great") if exceeds_maximum_allowable_charge? end # By defining separate predicate methods we can test each of them individually # and new programmers can see the intent of the code, not just the implementation. # Instead of subtracting the amount from the balance and checking if the # value is greater than 0, change the implementation to mirror the intent. # There was a bug in the before code, this is more obvious. def insufficient_funds? amount > payee.balance end def exceeds_maximum_allowable_charge? payee.account.maximum_allowable_charge > amount end end |
While the refactored version may have more lines of code, but don’t let that scare you. It’s far more important for code to be human readable than incredibly concise.
Association Proxies
We’ve had several submissions in the past which failed to fully utilise rails’ collection proxies. For future reference, I figured I’d write up the most commonly missed opportunities. To demonstrate this we’ll assume a Todo list application where each user can manage multiple Todo lists, with a simple model like this:
1 2 3 4 5 6 7 |
class User < ActiveRecord::Base has_many :todo_lists end class TodoList < ActiveRecord::Base belongs_to :user end |
Case One – Restricting Access to User Data
If you’re following the new restful conventions the url to access a Todo List is going to look something like http://todo.example.com/todo_lists/1
To keep your application secure, you’ll want to ensure that users can’t just change the ID in the url and manage someone else’s tasks. This is one of the most commonly asked questions on mailing lists and IRC channels, and we’ve seen quite a few anti-patterns to solve it. Some of the most common anti-patterns we’ve seen are these:
Anti-Pattern #1: Manually specifying the IDs when you construct the queries;
1 2 3 4 5 |
def show unless @todo_list = TodoList.find_by_id_and_user_id(params[:id], current_user.id) redirect_to '/' end end |
Anti-Pattern #2: Querying globally, then checking ownership after the fact;
1 2 3 4 |
def show @todo_list = TodoList.find(params[:id]) redirect_to '/' unless @todo_list.user_id = current_user.id end |
Anti-Pattern #3: Abusing with_scope for a this simple case either directly, or in an around_filter.
1 2 3 4 5 |
def show with_scope(:find=>{:user_id=>current_user.id}) do @todo_list = TodoList.find(params[:id]) end end |
Best Practice: The most effective way to do this is to call find on the todo_lists association.
1 2 3 |
def show @todo_list = current_user.todo_lists.find(params[:id]) end |
In the event that the user plays with the URL to point to something they don’t own, an ActiveRecord::RecordNotFound exception will be raised. In general I simply ignore these exceptions as it’s unlikely they’re caused by legitimate use, but if you want to handle them you can simply rescue the exception.
1 2 3 4 5 6 |
def show @todo_list = current_user.todo_lists.find(params[:id]) rescue ActiveRecord::RecordNotFound => e flash[:warning] = "Stop playing around with your urls" redirect_to '/' end |
Additionally, the find method on todo_lists is just like the regular find method; you’re not restricted to merely passing it an ID. So to build a secure search action you can do something like:
1 2 3 4 |
def search @todo_lists = current_user.todo_lists.find(:all, :conditions=>["name like ?", params[:q]], :include=>[:items]) end |
That dispatching isn’t limited to find, you can call any method defined on the TodoList class. For example, say you wanted to encapsulate the search behaviour inside the TodoList class, you can define a class method:
1 2 3 4 5 6 |
class TodoList < ActiveRecord::Base # ... def self.search(query) find(:all, :conditions=>["name like ?", query], :include=>[:items]) end end |
Then you can call that by using class methods on the has_many association:
1 2 3 |
def search @todo_lists = current_user.todo_lists.search(params[:q]) end |
Case Two – Assigning Ownership
Just as you need to find TodoLists for a user, your Todo list application will also need to create new lists for a user. Frequently in submissions we’ll see something like this:
1 2 3 4 5 6 |
def create @todo_list = TodoList.new(params[:todo_list]) @todo_list.user_id = current_user.id @todo_list.save! redirect_to todo_list_url(@todo_list) end |
Just like find, the has_many association creates a number of methods to help you create new members of the todo_lists collection. If there’s no validation on the todo list model, then the simplest option is just to use create!:
1 2 3 4 |
def create @todo_list = current_user.todo_lists.create! params[:todo_list] redirect_to todo_list_url(@todo_list) end |
This will raise an exception in the event the model fails validation, which prevents your application from silently failing. However, it doesn’t provide a particularly good user experience (in fact, it sucks). In situations where validation failures are likely, you can still use the association proxies:
1 2 3 4 5 6 7 8 |
def create @todo_list = current_user.todo_lists.build params[:todo_list] if @todo_list.save redirect_to todo_list_url(@todo_list) else render :action=>'new' end end |
Case Three – Special Queries
The final case I wanted to cover is where association declarations can be used to save you time is where you have a special query you’re frequently using. In our case, let’s say you occasionally want to get the user’s completed lists, and their active ones. The simplest way to get these is:
1 2 3 4 |
def show @completed_lists = current_user.todo_lists.find_all_by_complete(false) @active_lists = current_user.todo_lists.find_all_by_complete(true) end |
However if you’ve been following our posts to date, you probably know that you should make your model match your domain, and that means your statements should read reasonably close to plain English. So if you want to find the user’s active_lists, you should probably do that with something like current_user.active_lists. A first pass at that may be.
1 2 3 4 5 6 7 8 9 |
class User < ActiveRecord::Base def completed_lists todo_lists.find_all_by_completed(true) end def active_lists todo_lists.find_all_by_completed(false) end end |
However a big downside to this approach is that repeated calls to active_lists will issue the query again, then construct the records from the result set, wasting time and memory. The obvious solution is to make sure the calls are cached, and the easiest way to do that is to use a has_many association.
1 2 3 4 |
class User < ActiveRecord::Base has_many :completed_lists, :class_name=>"TodoList", :conditions=>{:completed=>true } has_many :active_lists, :class_name=>"TodoList", :conditions=>{:completed=>false} end |
More Idiomatic Ruby
Carl Fyffe submitted a small snippet of an existing application, asking how we’d polish it. We’ve decided to try out a new format for this kind of micro-refactoring; let us know what you think.
Before:
1 2 3 4 5 6 7 8 9 10 11 12 |
# returns Hash of Engagements keyed on date. def self.find_by_year_month(year, month) date = Time.gm year, month engagements = {} all = find(:all, :conditions => ["start >= ? and start <= ?", date.last_month.end_of_month, date.next_month.beginning_of_month], :order => :start) all.each do |e| key = e.start.strftime('%Y%m%d') engagements[key] = [] unless engagements.has_key?(key) engagements[key].push e end engagements end |
After:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
def self.find_by_year_month(year, month) # We're dealing with Dates, so let's use the Date class. Date can # be a little slower than Time, but as this method isn't going to # perform thousands of operations per request, we'll be fine. requested_date = Date.new(year, month, 1) # The range in the original code went from the last day of the # previous month, to the first day of the following month. # Date#- subtracts days from a given date, which lets us find # the end of last month. from = requested_date - 1 # Date#>> lets us advance requested date by one month, # giving us the first of the next month. to = requested_date >> 1 # The BETWEEN operator lets you simplify the SQL, and it more # clearly matches the intent. engagements = find(:all, :conditions => ["start BETWEEN ? and ?", from, to], :order => :start) # Enumerable#group_by returns a hash with the date strings as keys # and an array of the corresponding engagements as the values. # If you find yourself initializing a collection, adding some items # then returning it, you can probably find a method to tidy that # up. engagements.group_by do |engagement| engagement.start.strftime("%Y%m%d") end end |
AssetsGraphed: Part 2
This is part two in our series reviewing, John Topley’s AssetsGraphed.
One of the common anti-patterns I saw was the overuse of, and over-reliance on, the identifiers of objects. In general, unless it’s going into a url or the session, there’s very little reason for you to be passing around the id of an active record object, you should be using the object itself.
There are a few places in Assets Graphed which use IDs, but the most common is the get_account_id method in ApplicationController.
1 2 3 |
def get_account_id session[:account_id] end |
This is used all over the application, and it’s almost always passed straight to Account.find. A simpler, and probably more efficient, alternative is to make the application helper return the current_account instead of its ID, not only is this ‘more object oriented’, you can also use memoization to ensure you only issue one database query per request.
1 2 3 |
def current_account @account ||= Account.find(session[:account_id]) end |
Now you can call current_account whenever you need the current account, instead of Account.find(get_account_id).
One extremely common (ab)use of IDs is using them for finding associated records. The FeedController contains the following code for finding the first 10 Incoming’s for a given Asset.
1 2 3 4 5 6 |
def asset @asset = Asset.find_by_feed_url(params[:id]) @incomings = Incoming.find(:all, :conditions => { :asset_id => @asset.id }, :limit => 10) #... end |
However, Active Record’s associations let you avoid this use of the id. To find the first 10 Incomings, simply ask the asset for them.
@asset.incomings.find(:all, :limit=>10) |
Once you start thinking about your methods in terms of the objects they relate to, instead of the opaque integers which identify them, several other refactorings become apparent.
Always be on the lookout for duplicated code. If you find yourself doing asset.incomings.find(:all, :limit => 10) in multiple places, then perhaps you need to pull that into a method of its own. Given the use of the magic number “10”, it might not be a bad idea to do so anyway:
1 2 3 4 5 6 7 |
class Asset < ActiveRecord::Base has_many :incomings do def recent(count=10) find(:all, :limit => count) end end end |
Then, you just have to do asset.incomings.recent to get the first 10 items.
Another common use of IDs, is when manipulating belongs_to associations, for example the create method of the AssetController contains the following code.
1 2 3 4 5 6 7 8 9 10 |
def create @asset = Asset.create(params[:asset]) @asset.starting_balance = params[:asset][:starting_balance] @asset.account_id = get_account_id if @asset.save flash[:notice] = "Successfully added #{@asset.name}." redirect_to assets_path else render :action => :index end |
If we transform it to use the account object instead of the ID, it will look like this:
@asset.account = current_account |
But once you’re dealing with the object itself, it should become apparent that we can use the association proxies to tidy up that code.
@asset = current_account.assets.build params[:asset] |
This lets you halve the code, and also ensures that the Asset appears in current_account.assets, guaranteeing a consistent in memory object graph.
For a final example we can take a look at the total_incomings method on the Asset class. The current implementation takes the ID of an account, and returns the ‘number of incomings’ across all its assets.
1 2 3 4 5 6 7 8 9 |
def self.incomings_count(account_id) incomings_count = 0 assets = Asset.find_all_by_account_id(account_id) or raise ActiveRecord::RecordNotFound assets.each do |asset| incomings_count += asset.incomings.count end incomings_count end |
The first step is to refactor this to a method on an object instance, instead of a static method taking an identifier. The purpose of the method is to determine the “number of incomings” for an Account, so ideally we can call it with @account.number_of_incomings. If we move the implementation to the Account class, and throw in some Enumerable#inject magic we’re left with:
1 2 3 4 5 |
class Account < ActiveRecord::Base def number_of_incomings assets.inject(0) {|total, asset| total += asset.incomings.size } end end |
While this is functionally correct, it’s going to issue one database query for every Asset belonging to an account. This will put excessive load on the database, and it’s just plain wasteful. Thanks to has_many :through we can issue one database query to figure it out. First define the associations.
1 2 3 4 |
class Account < ActiveRecord has_many :assets has_many :incomings, :through=>:assets end |
Now you can get the number of incomings for an account the same way you would any other association. @account.incomings.size
So every time you find yourself accessing an object’s id, try to think if it’s really necessary, chances are there’s a nice opportunity to improve the design, and performance, of your application.
AssetsGraphed: Part 1
John Topley has been kind enough to submit his AssetsGraphed application to us for review. He even set up a publicly-accessible subversion repository for (a subset of) the code: http://johntopley.com/svn/projects/assetsgraphed/.
AssetsGraphed is, according to the site, “a simple asset tracking application that also tracks your data.” “Assets”, in this sense, is referring to financial assets, so you can keep track of your spending. Koz and I will be spending a few articles discussing better ways to approach some of the programming problems John has tackled in this application.
For this first article, I’d like to focus on how to refactor away some of the grosser kinds of code duplication. One of the first questions John asked us in his submission was about how to clean up the duplication in the “incoming” and “outgoing” controllers and models. (Compare them here: incoming and outgoing controllers; incoming and outgoing models.) As you can see, the two are almost completely word-for-word identical—a poster child for refactoring if there ever was one.
Let’s begin by looking at the models. In this case, the only textual difference between the two files is the name of the model:
1 2 3 4 5 |
$ diff app/models/incoming.rb app/models/outgoing.rb 1c1 < class Incoming < ActiveRecord::Base --- > class Outgoing < ActiveRecord::Base |
Given that the two models have that level of similarity, my first suspicion was that they were really the same model, simply considered from different directions. What if, instead of Incoming and Outgoing models to track “money you are getting” and “money you are spending”, we simply defined a single “Item” model that encapsulated all transactions?
The differentiator between “incoming” and “outgoing” transactions then simply becomes the sign on the amount. “Incoming” transactions are positive numbers, and “outgoing” transactions are negative. As long as an index exists that includes the “base_amount” column, queries that ask for “base_amount < 0” and “base_amount >= 0” should be plenty efficient.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class DefineItemsTable < ActiveRecord::Migration def self.up create_table :items do |t| t.column "asset_id", :integer t.column "category_id", :integer t.column "transaction_date", :date t.column "created_on", :datetime t.column "base_amount", :integer end # for finding all items by asset create_index :items, %w(asset_id base_amount) # for finding all items by category create_index :items, %w(category_id base_amount) end end |
(That second index, for finding all items by category, may be superfluous, unless the application needs to retrieve items by category.)
Once that new table and model is in place, we can add some methods to the Asset class for returning the “incoming” and “outgoing” items:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Asset < ActiveRecord::Base # all items, incoming and outgoing has_many :items, :dependent => :destroy_all do def incoming @incoming ||= find(:all, :conditions => "base_amount > 0") end def outgoing @outgoing ||= find(:all, :conditions => "base_amount < 0") end end end |
The “do…end” syntax on the has_many association simply extends the association with an anonymous module containing the given methods. In other words, we can now do something like this:
1 2 3 4 5 6 7 8 9 10 11 |
# given some asset... asset = Asset.find(params[:id]) # we can return the list of _all_ items... asset.items # or only the incoming items... asset.items.incoming # or only the outgoing items... asset.items.outgoing |
An alternative to the anonymous modules is to simply declare two more associations.
1 2 3 4 5 |
class Asset < ActiveRecord::Base has_many :items, :dependent => :destroy_all has_many :incoming_items, :class_name => 'Item', :conditions => "base_amount > 0" has_many :outgoing_items, :class_name => 'Item', :conditions => "base_amount < 0" end |
It’s a little simpler to understand, and clocks in at a whopping 6 fewer lines of code. The only difference is that instead of accessing the incoming items with @asset.items.incoming, you’d use @asset.incoming_items.
Once the models are unified, the task of unifying the duplicated controllers follows naturally, resulting in a single ItemsController. The client must simply submit the item amount as either positive (for incoming) or negative (for outgoing), and the categorization occurs automatically. If there is a concern about requiring users to enter negative numbers, you can pass an extra parameter to the create action that indicates whether the number should be negative or not.
I really love extreme refactorings like this—there is very little in programming that gives me as much pleasure as axing code.
Idiomatic Ruby
Dan Hancu sent us a nice, bite-sized snippet of code, asking if there was a more elegant way to do what he was doing. His code was from a controller, part of a larger application that included a system for tracking an inventory of lenses. When creating a new type of lens, you just input a few parameters and the system then autogenerates all relevant lenses for that type of lens.
His code (in essense) is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class TypeOfLensController < ApplicationController def create @type_of_lens = TypeOfLens.new(params[:type_of_lens]) if @type_of_lens.save flash[:notice] = 'TypeOfLens was successfully created.' populate(@type_of_lens) redirect_to :action => 'list' else render :action => 'new' end end private def populate(type_of_lens) x_count = type_of_lens.minimum_cylinder y_count = type_of_lens.maximum_sphere increment_step_size = 0.25 while y_count >= type_of_lens.minimum_sphere while x_count <= type_of_lens.maximum_cylinder lens = Lens.new lens.lens_type_id = type_of_lens.id lens.sphere = y_count lens.cylinder = x_count lens.quantity = 0 lens.save x_count += increment_step_size end x_count = type_of_lens.minimum_cylinder y_count -= increment_step_size end end end |
Now, naturally, since I’m not acquainted with the domain of the application in question, it’s hard for me to judge the bigger picture, such as whether the model associations and such are appropriate or not. I’m going to assume that Dan, who is much more acquainted with the domain than myself, did all that right, and I’ll just focus on some of the smaller mechanical details of the code in question.
First off, the populate method really ought to be a method on TypeOfLens, called via an after_create hook:
1 2 3 4 5 6 7 |
class TypeOfLens < ActiveRecord::Base after_create :populate def populate #... end end |
By setting up populate as an after_create hook, we can rest easily knowing that every time a new TypeOfLens is instantiated, that populate method will be auto-invoked. That knowledge lets us simplify the controller significantly:
1 2 3 4 5 6 7 8 9 10 11 |
class TypeOfLensController < ApplicationController def create @type_of_lens = TypeOfLens.new(params[:type_of_lens]) if @type_of_lens.save flash[:notice] = 'TypeOfLens was successfully created.' redirect_to :action => "list" else render :action => "new" end end end |
Now, we can turn our attention to the populate method itself. Although Dan’s original code certainly suffices, it has the flavor of C code, simply modified to use basic Ruby syntax. By taking advantage of standard Ruby methods and patterns, we can not only compact the code, we can also make it look like idiomatic Ruby code instead of C code.
First of all, note the loop idiom being used:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def populate(type_of_lens) x_count = type_of_lens.minimum_cylinder y_count = type_of_lens.maximum_sphere increment_step_size = 0.25 while y_count >= type_of_lens.minimum_sphere while x_count <= type_of_lens.maximum_cylinder #... x_count += increment_step_size end x_count = type_of_lens.minimum_cylinder y_count -= increment_step_size end end |
All this is doing is iterating from some upper bound, maximum_sphere, to a lower bound, minimum_sphere, by some constant increment. The inner loop does the same thing, from a lower bound to an upper bound. Ruby, predictably, has a method for that specific use case, Numeric#step:
1 2 3 4 5 6 7 8 9 |
STEP_SIZE = 0.25 def populate maximum_sphere.step(minimum_sphere, -STEP_SIZE) do |sphere| minimum_cylinder.step(maximum_cylinder, STEP_SIZE) do |cylinder| #... end end end |
The Numeric#step method simply calls the associated block once for every increment of the second parameter, from the receiver to the first parameter. In other words, it “steps” (in the case of the outer loop) from maximum_sphere to minimum_sphere in increments of -STEP_SIZE.
Lastly, let’s address the process of creating a new Lens instance. In Dan’s original code, this was done by first creating an empty Lens object, then assigning the relevant attributes one-by-one, and saving:
1 2 3 4 5 6 |
lens = Lens.new lens.lens_type_id = type_of_lens.id lens.sphere = y_count lens.cylinder = x_count lens.quantity = 0 lens.save |
The Rails way of doing this employs the create method, passing it a hash of the attributes you wish the new object to have. It then returns the newly created (and saved) object. In our version, we ignore the return value, since it is not relevant to what we’re doing—it suffices us to know that the Lens object was created.
1 2 |
Lens.create :lens_type_id => type_of_lens.id, :sphere => y_count, :cylinder => cylinder, :quantity => 0 |
The final version of the TypeOfLens model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class TypeOfLens < ActiveRecord::Base STEP_SIZE = 0.25 after_create :populate # ... def populate maximum_sphere.step(minimum_sphere, -STEP_SIZE) do |sphere| minimum_cylinder.step(maximum_cylinder, STEP_SIZE) do |cylinder| Lens.create :lens_type_id => id, :sphere => sphere, :cylinder => cylinder, :quantity => 0 end end end # ... end |
In the end, I think this exercise illustrates nicely the value of learning Ruby, as well as Rails. Certainly, we all must start some place, and Rails offers plenty to learn for newcomers. But once you know the basics, it pays in a big way to keep stretching, learning Ruby idioms for common patterns.
The Rails way, after all, is only a specialized subset of the greater Ruby way.

