Search code examples
ruby-on-railshelper

How to call reusable code in rails model?


I have a repeated methods in my model and I want to put those codes into one place and just want to access it into my model.

My model have few methods such as:

class ProductionProductivity7 < ApplicationRecord
def self.import1(file)
  spreadsheet = Roo::Spreadsheet.open(file.path)
        header = spreadsheet.row(1)
        (2..spreadsheet.last_row).each do |i|
          row = Hash[[header, spreadsheet.row(i)].transpose]
          puts row.to_hash
          product = find_by(id: row["id"]) || new
          product.attributes = row.to_hash
          product.save!
    end
end
def self.search(search,compare)
  if search == "All"
    all.order(:id)
  elsif compare == "Bihar vs District"
    where("Districts = ? OR Districts = ?", search, "Bihar")
  else
    where(Districts: search)
  end
end

end

There are 3 more methods like this , I want to put this code block into helper and just want to call it inside model. For that I have tried this put these codes into my helper. And I am calling it using:

include ApplicationController.ProductionProductivity7sHelper

I am including this inside my model but I am getting this error:

undefined method `ProductionProductivity7sHelper' for ApplicationController:Class

My controller code is like this:

 def test
      @ProductionProductivity7s = ProductionProductivity7.search(params[:search],compare)
      a = ProductionProductivity7.query(@ProductionProductivity7s,params[:year],rain_fall_type,views,compare)
 end 

I have added a module name code.rb in app folder.

   module Code
    def search(search_scope,compare)
        if search_scope == "All"
        all.order(:id)
        elsif compare == "Bihar vs District"
        where("Districts = ? OR Districts = ?", search_scope, "Bihar")
        else
        where(Districts: search_scope)
        end
    end
end

I just want to write all of my methods of my model somewhere it can be either module or helper without changing anything. Is it possible I just want this code block in my model.

I am adding whole controller code and model code in gist files. Please have a look. My controller and Model code link

I am getting this error:

 undefined method `search' for #<Class:0x00007ff115974fd8> Did you mean? search1

Solution

  • How about just making a module like:

    module Import1
    
      def import1(file)
        spreadsheet = Roo::Spreadsheet.open(file.path)
        header = spreadsheet.row(1)
        (2..spreadsheet.last_row).each do |i|
          row = Hash[[header, spreadsheet.row(i)].transpose]
          puts row.to_hash
          product = find_by(id: row["id"]) || new
          product.attributes = row.to_hash
          product.save!
        end
      end
    
      def search(search_scope,compare)
        if search_scope == "All"
          all.order(:id)
        elsif compare == "Bihar vs District"
          where("Districts = ? OR Districts = ?", search_scope, "Bihar")
        else
          where(Districts: search_scope)
        end
      end
    
    end
    

    I think I would put it somewhere in your app folder so that you don't have problems with autoloading. You could put it in your root app folder, but that seems messy to me. You could also put it in your models folder, but then you have two very different kinds of things in the same folder which also seems messy to me. I think I would be tempted to create a new folder, something like app/model_modules or perhaps app/shared_model_modules and put your import_1.rb file in there. Then, it's clear what the file is.

    And then do:

    class ProductionProductivity7 < ApplicationRecord
      extend Import1
    end
    

    Or, how about using a service instead of a helper? IMO, it makes it more explicit what is going on whereas helpers can obsfucate where code lives.

    A bare bones BaseService might look like:

    class BaseService
    
      attr_accessor :args
    
      class << self
    
        def call(args=nil)
          new(args).call
        end
    
      end # Class Methods
    
      #=======================================================================
      # Instance Methods
      #=======================================================================
    
        def initialize(args)
          @args = args || {}
          assign_args
        end
    
      private
    
        def assign_args
          args.each do |k,v|
            class_eval do 
              attr_accessor k
            end
            send("#{k}=",v)
          end
        end
    
    end
    

    Then, your file import service might look something like:

    class ImportFileService < BaseService
    
      def call
        spreadsheet = Roo::Spreadsheet.open(file.path)
        header = spreadsheet.row(1)
        (2..spreadsheet.last_row).each do |i|
          row = Hash[[header, spreadsheet.row(i)].transpose]
          puts row.to_hash
          product = klass.find_or_initialize_by(id: row["id"])
          product.attributes = row.to_hash
          product.save!
        end
      end
    
    end
    

    And you would call your service something like:

    ImportFileService.call(file: file, klass: ProductionProductivity7)