Daily Refactor #35: Pull up method for finding a controller’s model object

The Refactoring

Now that yesterday’s split method is done, I can use pull up method to remove several similar before_filters. I had to add a declarative method (#model_object) so each controller can state which model should be used in the finders.

Before

1
2
3
4
# app/controller/application_controller.rb
class ApplicationController < ActionController::Base
  # ...
end
1
2
3
4
5
6
7
8
9
10
11
# app/controller/documents_controller
class DocumentsController  [:index, :new]
 
  private
 
  def find_document
    @document = @object = Document.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end
end
1
2
3
4
5
6
7
8
9
10
11
# app/controller/issue_categories_controller
class IssueCategoriesController  :new
 
  private
 
  def find_category
    @category = @object = IssueCategory.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end 
end

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# app/controller/application_controller.rb
class ApplicationController < ActionController::Base
  def find_model_object
    model = self.class.read_inheritable_attribute('model_object')
    if model
      @object = model.find(params[:id])
      self.instance_variable_set('@' + controller_name.singularize, @object) if @object
    end
  rescue ActiveRecord::RecordNotFound
    render_404
  end
 
  def self.model_object(model)
    write_inheritable_attribute('model_object', model)
  end
end
1
2
3
# app/controller/documents_controller
class DocumentsController  [:index, :new]
end
1
2
3
4
5
6
7
8
9
10
11
12
# app/controller/issue_categories_controller
class IssueCategoriesController  :new
 
  private
 
  # Wrap ApplicationController's find_model_object method to set
  # @category instead of just @issue_category
  def find_model_object
    super
    @category = @object
  end
end

Review

As a result of this refactoring, five controllers were able to throw away their custom method to find an object. This will also let me throw away some custom methods in my plugins that do the exact same thing.

You might notice that IssueCategoryController is different. It uses the instance variable @category instead of @issue_category. By using Ruby’s inheritance I was able to wrap #find_model_object and set the correct instance variable.

Reference commit