joker1007
2015/11/26 01:03:58 投稿
5

パラメーター引数のクラス化とActiveSupport::Concernによるモジュール化

<p>ElasticSearchを使って検索する際にfacetによる絞り込みやベースとなるキーワード、ページネーションにおけるアイテム数等、パラメーター数が多くなってきたため、デフォルト値やパラメーターの有無による条件分岐の責任を一手に引き受けるパラメータークラスを作った。<br> コントローラーから呼び出す際は、パラメーターを丸ごと渡せばよしなにやってくれるし、モデル側からはパラメーターオブジェクトのアクセサメソッドを呼べば良いだけになったので、コードの見通しが良くなった。また、単体テストにコントローラーのパラメーターを考慮する必要もなくなった。<br> この後、色々と検索のパターンを拡充していったが、変更する範囲も少なく済んだ。</p> <p>ついでに検索に関わる処理は他と独立して変更できる部分だったので、ActiveSupport::Concernを使ったモジュール化で1ファイルにまとめてみた。</p> <p>(下記コードは関係のありそうな箇所のみの抜粋)</p>

Before

# app/models/pasokara.rb
class Pasokara < ActiveRecord::Base
  validates_presence_of :title, :fullpath, :md5_hash
  validates_uniqueness_of :md5_hash

  include SimpleTaggable

  searchable do
    text :title, stored: true
    string :title_sort do
      title
    end
    string :tags, multiple: true, stored: true do
      tags.map(&:name)
    end
    string :nico_vid, stored: true
    integer :nico_view_count, trie: true
    integer :nico_mylist_count, trie: true
    text :nico_description, stored: true
    time :nico_posted_at, trie: true
    integer :duration, trie: true
  end

  class << self
    def all_with_facet_tags(page: 1, per_page: 100)
      search(include: [:tags]) do
        facet :tags
        paginate page: page, per_page: per_page
      end
    end
  end

  paginates_per 100

  include CreateMethods
  
  # 省略...
end

After

# app/models/pasokara.rb
class Pasokara < ActiveRecord::Base
  validates_presence_of :title, :fullpath, :md5_hash
  validates_uniqueness_of :md5_hash

  include SimpleTaggable
  include Searching

  paginates_per 100

  include CreateMethods

  # 省略...
end

# app/models/pasokara/searching.rb
module Pasokara::Searching
  extend ActiveSupport::Concern

  included do
    searchable do
      text :title, stored: true
      string :title_sort do
        title
      end
      string :tags, multiple: true, stored: true do
        tags.map(&:name)
      end
      string :nico_vid, stored: true
      integer :nico_view_count, trie: true
      integer :nico_mylist_count, trie: true
      text :nico_description, stored: true
      time :nico_posted_at, trie: true
      integer :duration, trie: true
    end
  end

  class SearchParameter
    attr_reader :keyword, :tags, :page, :per_page
    def initialize(keyword: nil, tags: nil, page: 1, per_page: 100)
      @keyword = keyword
      @tags = tags || []
      @page = page || 1
      @per_page = per_page.to_i
      freeze
    end
  end

  module ClassMethods
    def search_with_facet_tags(search_parameter)
      search(include: [:tags]) do
        fulltext search_parameter.keyword if search_parameter.keyword.present?
        search_parameter.tags.each do |tag_name|
          with(:tags, tag_name)
        end
        facet :tags
        paginate page: search_parameter.page, per_page: search_parameter.per_page
      end
    end
  end
end

みんなのコメント