2

I am building a sample e-commerce app using ruby on rails.One of my controller name is "products_controller".This controller is also placed inside as a nested controller.The actions inside these controllers are same.How can we represent these actions without duplication of codes. The code samples are given below.

app/controllers/products_controller.rb

def index
@product = Product.all
@vari = @products.variants
.............
.............
end

app/controllers/master_admins/products_controller.rb

def index
@product = Product.all
@vari = @products.variants
.............
.............
end

app/controllers/master_admins/properties_controller.rb

def product
@product = Product.all
@vari = @products.variants
.............
.............
end

The above actions contains the same set of codes.How can we refactor this so that the code doesnt get repeated.

Thanks in advance....

Milind
  • 4,535
  • 2
  • 26
  • 58
Mani kandan
  • 261
  • 1
  • 4
  • 9
  • 1
    I think a better question is why do you need multiple controllers with the same code? If you're worried about access control, why not handle that using roles? – vich Mar 31 '15 at 14:59
  • am using this set of codes in nested controllers also.This product controller index action happens in the following urls in my project. "localhost:3000/products","localhost:3000/master_admins/products", "localhost:3000/property_admins/products". – Mani kandan Apr 01 '15 at 07:10

3 Answers3

5

I would suggest using concerns which are awesome for DRY.

For the controller, common methods can be placed here:

In my app/controllers/concerns/common.rb

module Common
  extend ActiveSupport::Concern

  module ClassMethods
    ## This is a Class method, call it just like you call any other class method
    def get_products_and_variants
      @product = Self.all
      @vari = @product.variants
    end
  end

## Instance method, if you don't want aclass method, use this instance method
def my_instance_method
  ## code for method
end

Then, call it by including common.rb in the controller*

include Common

def index
  ## This will make @product and @vari available
  Product.get_products_and_variants

  # ............
end



## Other method using same method call
def product
  ## This will make @product and @vari available
  Product.get_products_and_variants

  # .............
end

If you have multiple classes using this class method, you can use something like this (in common.rb):

def get_details        
  if self == Product
     ## Get products
  elsif self == Variant
     ## Get variants
  elsif self == ProductDetail
     ## Get product details
  end              
end
jvperrin
  • 3,368
  • 1
  • 23
  • 33
Milind
  • 4,535
  • 2
  • 26
  • 58
  • can i use something like this..... product = Product.all product_category = ProductCategory.all product_sub_category = ProductSubCategory.all properties = product_sub_category.properties – Mani kandan Apr 01 '15 at 07:38
  • yes @Manikandan,,for multiple Classes using same class method in common.rb,,i will suggest using if elseif way of coding(like method get_details),so that you can check which class is calling such as self==Product and can go ahead.you can also eager load using includes :) – Milind Apr 01 '15 at 12:02
  • Thanks for the help @Milind ...This helped me solve my issue......thankyou once again for spending your prescious time on this....:) – Mani kandan Apr 01 '15 at 14:30
2

You can do something like:

class GenericController < ActiveRecord::Base
  def index
    do something interesting
  end
end

class PropertiesController < GenericController
  # index is done
end

class ProductsController < GenericController
  # index is done
end
railsdog
  • 1,503
  • 10
  • 10
0

You can use before_action in the controller. It will execute find_product before executing index:

before_action :find_product, only: :index

def find_product
    @product = Product.all
    @vari = @products.variants      
end

def index
# some useful code
end

You can remove the "only:" part to execute find_product before other actions, or move it to ApplicationController (or other parent controller class) to execute the before_action in all corresponding controllers.

Oleg K.
  • 1,549
  • 8
  • 11