Bagzli
Bagzli

Reputation: 6569

Rails generalizing a method that uses models

I am trying to generalize few methods that will be used by multiple models/views/controllers but i'm having no luck. Here is the original code that works when it is just for 1 set called Trucks.

View

<h2>Trucks</h2>
  <%= form_tag trucks_path, :method => 'get' do %>
  <%= hidden_field_tag :direction, params[:direction] %>
  <%= hidden_field_tag :sort, params[:sort] %>
  <p>
    Search: 
    <%= text_field_tag :search %>
    by 
    <%= select_tag :search_column, options_for_select(Truck.translated_searchable_columns(['attribute1']), params[:search_column]) %>
    <%= submit_tag "Search" %>
  </p>
<% end %>

<!-- Display code goes here, but im not showing since its just a table -->

Controller

    def index
    @trucks = Truck.search(params[:search], params[:search_column]).order(sort_column(Truck, "truck_no") + " " + sort_direction)

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @trucks }
    end
  end

Model

  class Truck < ActiveRecord::Base
  attr_accessible :attribute1, :attribute2, :attribute3

  def self.search(keyword, column_name)
    if self.column_names.include?(column_name.to_s)
      where("trucks.#{column_name} LIKE ?", "%#{keyword}%")
    else
      scoped
    end
  end

  def self.searchable_columns(unwanted_columns)
    self.column_names.reject{ |column| unwanted_columns.include?(column) }
  end

  def self.translated_searchable_columns(unwanted_columns)
    columns = self.searchable_columns(unwanted_columns)
    result = columns.map{ |column| [Truck.human_attribute_name(column.to_sym), column] }
    result
  end
end

All this works without a hitch, now I can't figure out for the life of me how to move these methods to lib and have them generalized so that lets say Trailers is able to call in the same method and pass in its information and achieve the same result. I am trying to make this code DRY as possible. Could anyone explain me what I need to do to achieve this? How does lib access the database?

Upvotes: 2

Views: 282

Answers (1)

DigitalCora
DigitalCora

Reputation: 2232

The concept you're looking for is called a "concern". Rails has a convenience module for implementing concerns called ActiveSupport::Concern. Here's how you might extract your model methods:

module Searchable
  extend ActiveSupport::Concern

  module ClassMethods
    def search(keyword, column_name)
      if column_names.include?(column_name.to_s)
        where("#{table_name}.#{column_name} LIKE ?", "%#{keyword}%")
      else
        scoped
      end
    end

    def searchable_columns(unwanted_columns)
      column_names.reject{ |column| unwanted_columns.include?(column) }
    end

    def translated_searchable_columns(unwanted_columns)
      columns = searchable_columns(unwanted_columns)
      columns.map{ |column| [human_attribute_name(column.to_sym), column] }
    end
  end
end

And then in your model:

class Truck < ActiveRecord::Base
  include Searchable
  attr_accessible :attribute1, :attribute2, :attribute3
end

As for where exactly you should store the Searchable module, it's up to you -- it just has to be someplace that's included in config.autoload_paths, just like a model or controller. Rails 4 introduced a convention that model concerns are stored in app/models/concerns, and controller concerns in app/controllers/concerns, but there is nothing special about these locations other than being autoloaded by default.

Upvotes: 3

Related Questions