matsuda
2/1/2010 - 2:01 AM

active_search_base.rb

# 
# 検索用クラス
# ActiveFormプラグインに似ている
# 
# これまでのActiveSearchをモジュール化し、ActiveFormを拡張するようにしました
# selectable_attrがインクルードされています
# ------------------------------------------------------
# 使い方
# SearchFormクラスを継承
# Ext::ActiveSearch::Baseモジュールをインクルード
# active_search_for クラスメソッドで対象とするモデルを設定
# conditionsインスタンスメソッドの実装
# newでインスタンス生成
# paginateかfindで結果取得
# ------------------------------------------------------
# 例)
# model:lib/models/content_link_search.rb
# 
# class ContentLinkSearch < ActiveForm
#   include Ext::ActiveSearch::Base
#   active_search_for 'ContentLink'
# 
#   def conditions
#     conds ||= []
#     conds << ["#{table_name}.http_status_code IS NOT NULL"] if self.status.present? && self.status == '1'
#     conds << ["#{table_name}.url REGEXP ?", self.url] if self.url.present?
#     conds << ["#{table_name}.description LIKE ?", "%#{self.description}%"] if self.description.present?
#     conds.present? ? flatten_conditions(conds) : nil
#   end
# end
# 
# ------------------------------------------------------
# controller:app/controllers/admin/content_links_controller.rb
# 
# class Admin::ContentLinksController < Admin::BaseController
#   def index
#     @content_link_search = ContentLink::Search.new(params[:content_link_search])
#     @content_links = @content_link_search.paginate(
#       :include => {:site => :company},
#       :order => 'content_links.id desc',
#       :page => params[:page],
#       :per_page => per_page
#     )
#   end
# end
# 
# ------------------------------------------------------
# view:app/views/admin/content_links/index.html.erb
# 
# <%- form_for :content_link_search, :url => admin_content_links_path, :html => {:method => :get} do |f| -%>
# <table>
#   <tr>
#     <th>リンク先URL</th>
#     <td><%= f.text_field :url %></td>
#   </tr>
#   <tr>
#     <th>備考</th>
#     <td><%= f.text_field :description %></td>
#   </tr>
#   <tr>
#     <td>
#       <%= f.check_box :status, :value => '0' %>未処理リンクのみ
#     </td>
#     <td><%= f.submit '検索' %></td>
#   </tr>
# </table>
# <%- end -%>
# 

module Ext
  module ActiveSearch
    module Base
      def self.included(base) # :nodoc:
        base.extend ClassMethods
        base.class_eval do
          extend Ext::ActiveSearch::Base::SingletonMethods
          include Ext::ActiveSearch::Base::InstanceMethods
          include Ext::ActiveRecord::FlattenCondition
          include ::SelectableAttr::Base
        end
      end

      module ClassMethods
        def active_search_for(target, *attributes)
          klass = target.constantize
          set_class_name target
          set_table_name klass.table_name
        end
      end

      module SingletonMethods
        private
        def set_class_name(value)
          define_attr_method :class_name, value
        end

        def set_table_name(value)
          define_attr_method :table_name, value
        end

        # ActiveRecord::Base
        def define_attr_method(name, value=nil, &block)
          klass = class << self; self; end
          if block_given?
            klass.send :define_method, name, &block
          else
            # use eval instead of a block to work around a memory leak in dev
            # mode in fcgi
            klass.class_eval "def #{name}; #{value.to_s.inspect}; end"
          end
        end
      end

      module InstanceMethods #:nodoc:
        def initialize(attributes, *opt)
          @options = opt
          super(attributes)
        end

        def table_name; self.class.table_name; end
        def class_name; self.class.class_name; end
        def options; @options; end
        def conditions; end

        def results
          warn('[DEPRECATION] Ext::ActiveSearch::Base.results is deprecated ' +
               'and will be removed from future versions. ' +
               'Use Ext::ActiveSearch::Base.paginate instead')

          opts = {}
          opts[:conditions] = self.conditions if self.conditions.present?
          self.class.class_name.constantize.paginate( opts.reverse_merge(options.extract_options!) )
        end

        def paginate(options)
          options.delete(:conditions) if options[:conditions].present?
          options[:conditions] = self.conditions if self.conditions.present?
          self.class.class_name.constantize.paginate( options )
        end

        def find(*args)
          options = args.extract_options!
          options.delete(:conditions) if options[:conditions].present?
          options[:conditions] = self.conditions if self.conditions.present?
          self.class.class_name.constantize.find( args.first, options )
        end
      end

    end
  end
end