2

インスタンス変数をレコードにアタッチする組み込みの方法はありますか?たとえば、:を含むクラスUserがあるとします。foo attr_accessor

class User < ActiveRecord::Base
  ...
  attr_accessor :foo
end

私が行った場合

u = User.first
u.foo = "bar"
u.foo # -> "bar"
User.find(1).foo # => nil

これは正しい動作であり、その理由を理解しています。インスタンス変数は、レコードではなくインスタンス(メモリ内)に存在します。私が欲しいのはレコード変数のようなものなので、上記の例では、アプリケーションの同じインスタンス内でu.foo同じ値をUser.find(1).foo返します。永続性は必要ありません。テーブルの列になるのは適切ではありません。たとえば、コントローラーアクション、コンソールセッションなどのライフサイクル中に、同じレコードに対して同じ値を返すのが適切です。と同じである必要がある理由がないため、を介してクラス変数が必要です。foofoocattr_accessorUser.find(1).fooUser.find(2).foo

私が思いつくことができる最善の策は、クラス変数配列とインスタンスメソッドを使用してそれを偽造し、配列の適切な要素を取得/設定することです。

class User < ActiveRecord::Base

  cattr_accessor :all_the_foos
  self.all_the_foos = Array.new

  def foo
    self.all_the_foos[id]
  end

  def foo= new_foo
    self.all_the_foos[id] = new_foo
  end

end

このALMOSTは機能しますが、保存されていないレコードでは機能しません。たとえば、User.new.foo = "bar"失敗します。

4

2 に答える 2

2

を使用Thread.currentして、コントローラー アクション呼び出しのコンテキスト内でアクティブな変数を設定できます。現在の実装では、呼び出し間でコンテキストがリセットされることは保証されていません。

class User < ActiveRecord::Base

  after_create    :set_thread_var

  def foo
    new_record? ? @foo : Thread.current["User-foo-#{id}"]
  end

  def foo=(val)
    new_record? ? (@foo = val) : (Thread.current["User-foo-#{id}"] = val)
  end

private

  def set_thread_var
    Thread.current["User-foo-#{id}"] = @foo if defined?(@foo)
  end

end
于 2011-10-21T18:37:23.023 に答える
1

あなたのクラス変数ソリューションよりも良いアイデアは考えられません。あなたの " new" 問題を解決するには、 を使用しますafter_initialize

class User < ActiveRecord::Base
  after_initialize :init_foos

  cattr_accessor :all_the_foos

  def foo
    self.all_the_foos[id]
  end

  def foo= new_foo
    self.all_the_foos[id] = new_foo
  end

  private
  def init_foos
    @@all_the_foos ||= []
  end
end
于 2011-10-21T17:39:29.017 に答える