0

名前付きスコープを介してインスタンス変数を初期化することは可能ですか?最初の検索後に検索条件を覚えておきたい。以下を参照してください。

class Foo < ActiveRecord::Base    
  attr_accessor :search

  named_scope :bar, lambda {|search|
    # Do something here to set the @search variable to search
    {
      :conditions => [
        "name LIKE :search OR description LIKE :search", 
        {:search => "%#{search}%"} ]
    } 
  }

  def match
    if name.match(@search)
      :name
    elsif description.match(@search)
      :description
    end
  end
end

または、名前付きスコープを呼び出して結果を繰り返し、それぞれにインスタンス変数を設定するクラスメソッドを作成することもできますが、その場合、名前付きスコープの連鎖性が失われます。

4

3 に答える 3

0

名前付きスコープは、反復されるまで、単一のレコードをインスタンス化しません。そして、あなたが正しく言うように、それらはより多くのスコープで連鎖している可能性があります。

考えられる解決策は 2 つあります。データベースに依存して、偽の列に一致する値を入力します。クエリへの特別なオプション(またはRails 3:selectの特別な引数)を使用して簡単にする必要があります.select()

もう 1 つは、コントローラーに依存することです。これは実装がはるかに簡単ですが、Ruby の「一致」がデータベースの「LIKE」と同じであることを確認する必要があります。照合、Unicode 正規化フォームなどについて話しています。おそらく、両方のエンジンで動作が異なるケースが少なくとも 1 つあります。

于 2012-10-08T23:25:33.453 に答える
0

私はこの 1 週間、この同じ問題を必死に達成しようとしてきましたが、最終的にそれを管理することができました。悩みの中でこの質問を見つけたので、他の誰かが同様の問題に直面した場合に備えて、ここで答えを共有したいと思いました.

彼の回答で書き直されたように、モデルインスタンスがまだないため、スコープからモデルにインスタンス変数を設定することはできません。スコープは、スコープの結果を反復しようとしたときに評価される sql 式を構築するためだけのものです。ただし、存在するのは ActiveRecord::Relation オブジェクトです。これは、スコープの詳細を保持するものです。保持したい変数をそのオブジェクトに置くことで、後でアクセスできるようになります。

では、Relation オブジェクトの変数を取得するにはどうすればよいでしょうか? モンキーパッチはここであなたの救助に来ます:

/lib/core_ext/add_scope_context_to_activerecord_relation.rb:

module ActiveRecord
  class Relation
    attr_writer :scope_context

    def scope_context
      @scope_context ||= {}
    end
  end

  module SpawnMethods
    alias_method :old_merge, :merge

    def merge(r)
      merged = old_merge(r)
      merged.scope_context.deep_merge! r.scope_context
      merged
    end
  end
end

これで、すべてのスコープに scope_context 変数ができました。SpawnMethods#merge の悪意は、scope_context がスコープのチェーンに沿って保持されるようにすることです (例: Foo.search(xxx).sort(xxx).paginate(xxx))

/app/concerns/searchable.rb:

require 'active_support/concern'

module Searchable
  extend ActiveSupport::Concern

  included do
    self.scope :search, Proc.new { |params|
      s = scoped

      s.scope_context[:searchable] = {}
      s.scope_context[:searchable][:params] = parse_params params

      s.scope_context[:searchable][:params].each do |param|
        s = s.where generate_where_expression param
      end
      s
    } do
      def search_val col_name
        param = scope_context[:searchable][:params].find do |param|
          param[:col_name] == field.to_s
        end
        param.nil? ? '' : param[:val]
      end
    end
  end

  module ClassMethods
    def parse_params params
      # parse and return the params
    end

    def generate_where_expression param
      "#{param[:col_name]} like '%#{param[:val]}%'"
    end
  end
end

これで、コントローラー、モデル、およびビューは次のようになります。

/app/controllers/foo_controller.rb:

class FooController < ApplicationController
  def index
    @foos = Foo.search(params[:search])
  end
end

/app/models/foo.rb:

class Foo < ActiveRecord::Base    
  include Searchable

  attr_accessor :name, :description
end

/app/views/foos/index.html.erb:

<p>You searched for:</p>
<table>
  <tr>
    <th>Name</th>
    <td><%= @foos.search_val :name %>
  </tr>
  <tr>
    <th>Description</th>
    <td><%= @foos.search_val :description %>
  </tr>
</table>
<p>And you got:</p>
<table>
  <% @foos.each do |foo| %>
    <tr> xxx </tr>
  <% end %>
</table>

ここで、上記でほのめかされた core_ext および懸念ディレクトリに気付いたかもしれません。アプリケーションは、次のことを認識している必要があります。

/config/application.rb:

# Should be fairly obvious where to put this line
config.autoload_paths += Dir[Rails.root.join('app', 'concerns', '{**}')]

/config/initializers/load_extensions.rb:

Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l }

サーバーを再起動することを忘れないでください (何も忘れていないと仮定して)。

ああ、私は Rails 3.2.13 と Ruby 1.9.3 でこの解決策を考え出しました。他のバージョンでどのように動作するかわかりません。

于 2013-11-14T06:35:05.897 に答える