Brandon
Brandon

Reputation: 10953

Generics Example Question

I am fairly new to Ruby on Rails and as a C# developer, when I want to re-use code (for a repository class), I could put it into a base class of type <T> to be able to do something like this:

public virtual IEnumerable<T> GetAll()
{
    return Context<T>.GetAll();
}

If I need to do any custom logic, I could, of course, override the method in my 'User' repository.

In Ruby, I am familiar that you can do this:

class UsersController < ApplicationController

This will allow access to all methods in ApplicationController and it's parent classes. When using scaffolding, it generates the following method in each of my child classes:

def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
  end
end

What I end up with is 10 classes that have the same method, but the only difference is 'User.all', 'Post.all', etc.

How would I make this method generic so I can put it in my ApplicationController class?

Thanks for any assistance you can provide to a Ruby on Rails newbie.

Upvotes: 4

Views: 2005

Answers (5)

Jed Schneider
Jed Schneider

Reputation: 14671

The first thing to realize about the scaffolding code is that it can be abreviated, as such:

def index
  @users = User.all
end

unless you intend to deliver the view in another format, like json, html, pdf, the respond_to block is unnecessary. If you still feel the need to dry up this method, you could do something like

# app/controllers/concerns/autoload_records.rb

module AutoloadRecords
  included do
    before_action :load_records, only: :index
    before_action :load_record, only: [:create, :show, :edit, :update, :destroy]
  end

  private
  def load_records
    @records = model_class.all
  end

  def load_record
    @record = model_class.find(params[:id])
  end

  def model_class
    klass = self.class.to_s[/\A(\w+)sController\Z/,1] #=> get the name of the class from the controller Constant
    Object.const_get(klass)
  end
end

and write your controller like

class UsersController < ApplicationController
  include AutoloadRecords

  def index
    @records # => #<ActiveRecord::Relation[...]>
  end

  def show
    @record # => #<User ...>
  end

  def non_rest_action
    @record # => nil
    @records # => nil
  end
end

Upvotes: 4

Maybe a simple solution could be to rely on mixins.

You define a module,

module MyModule 
   def my_index(klass)
     @elements = klass.all

     respond_to do |format|
       format.html # index.html.erb
       format.xml  { render :xml => @elements }
     end
   end
end 

Then, you have in your controller,

include MyModule
def index
   my_index(User)
end 

Of course, you need to use @elements in your views. If you want a different variable name in each view you can do

   def my_index(klass, var_name)
     self.instance_variable_set(var_name, klass.all)
     ...
   end

Upvotes: 3

Art Shayderov
Art Shayderov

Reputation: 5110

  1. Based on my experience, you rarely end up with 10 index action looking like @user = User.all. If you know in advance that some actions between different models will be identical - well then may be it makes sense to extract common logic. But then again may be these models are somehow connected? I wouldn't say in advance that Post and User will have identical index actions.
  2. For a short method like this I wouldn't try to eleminate repetition because you may end up losing readability.

Upvotes: 0

nuclearsandwich
nuclearsandwich

Reputation: 455

Rather than doing an eval where you really don't want to be doing one. Check out Jose Valim's Inherited Resources gem. It provides the standard CRUD methods for all of your controllers and is quite sophisticated. It is also thoroughly tested so you don't have to worry about making sure your generic code operates as expected in all cases.

For details on how to use it see the GitHub page linked.

Upvotes: 3

noodl
noodl

Reputation: 17388

There are several rails plugins that help to reduce this kind of duplication. This one was covered in railscast episode 230.

https://github.com/josevalim/inherited_resources

Upvotes: 1

Related Questions