2

ローカル変数にアクセスできるクラス メソッドを定義したいと考えています。したがって、これはクラスのインスタンスごとに異なります。名前付きスコープで使用するときのように、ラムダでクラスメソッドを動的にできることを私は知っています。しかし、これはインスタンスに固有の値に対して実行できますか?

詳しくは、Rails の paperclip プラグインの has_attached_file メソッドです。スタイル ハッシュのラムダを渡して、DB に格納されているオブジェクトの属性に基づいて画像スタイルを設定できるようにしたいと考えています。これは可能ですか?

4

2 に答える 2

13

免責事項:まず、質問 ( Can you pass self to lambda? ) と解決しようとしている問題 (ペーパークリップを使用した動的スタイル) が完全には一致しません。元の質問にはお答えしません。なぜなら、それはあなたの問題とは完全には関係がないからです。

代わりに、クリップの質問にお答えします。

詳しくはhas_attached_filerails の paperclip プラグインのメソッドです。スタイル ハッシュのラムダを渡して、DB に格納されているオブジェクトの属性に基づいて画像スタイルを設定できるようにしたいと考えています。これは可能ですか?

はい、可能です。 ペーパークリップでは、:stylesオプションは Proc を取ることができます。添付ファイルが初期化されると、Proc が使用された場合、添付ファイル自体が Proc に渡されます。添付ファイルには関連付けられた ActiveRecord オブジェクトへの参照が含まれているため、それを使用して動的なスタイルを決定できます。

たとえば、has_attached_file宣言は次のようになります (ユーザーがアバターのサイズをカスタマイズできるユーザーとアバターのシナリオを想定しています)。

class User < ActiveRecord::Base
  has_attached_file :avatar, :styles => lambda { |attachment| 
    user = attachment.instance
    dimensions = "#{user.avatar_width}x#{user.avatar_height}#"
    { :custom => dimensions }
  }
end
于 2009-07-10T14:35:16.407 に答える
9

わかりました、あなたは不明確です。

rubyのローカル変数は、小文字( 、、、、など)fooで始まり、字句スコープ(変数など)になります。それらは「クラスのインスタンス」とは何の関係もありませんbarsteveC

@rubyのインスタンス変数は、シジル(、、、、など)@fooで始まり、の現在の値が格納されているオブジェクトである場合は常にスコープ内にあります。@bar@carlself

オブジェクトのインスタンス変数に直接アクセスできるメソッドが必要な場合、それはインスタンスメソッドと呼ばれます。たとえば、battle_cryinitializeは両方ともインスタンスメソッドです。

class Character
  def initialize(name)
    @name=name
  end
  def battle_cry
    @name.upcase + "!!!"
  end
  def Character.default
    new("Leeroy Jenkins")
  end
end

対照的に、クラスメソッドはClassオブジェクトのメソッドであり、そのオブジェクトのインスタンス変数にはアクセスできません。上記の例で defaultは、はクラスメソッドです。

現在のスコープの変更をトリガーする、または現在のスコープから値を取得する(クラスまたはインスタンス)メソッドが必要な場合、rubyはブロックと呼ばれるタイプのコールバックを使用します。

class Character
   ATTACKS = [ "Ho!", "Haha!", "Guard!", "Turn!", "Parry!", "Dodge!", "Spin!", "Ha", "THRUST!" ]
   def attack
     ATTACKS.inject(0) { |dmg, word| dmg + yield(word) }
   end
end

person = Character.default
puts person.battle_cry

num_attacks = 0;
damage = person.attack do |saying|
  puts saying
  num_attacks += 1
  rand(3)
end
puts "#{damage} points of damage done in #{num_attacks} attacks"

上記の例でattackは、yieldキーワードを使用して、渡されたブロックを呼び出します。を呼び出すとattack、ローカル変数num_attacksは、渡すブロック(ここではで区切られますdo ... end)のスコープ内にあるため、インクリメントできます。 attackはブロックに値を渡すことができます。ここでは、値が変数にキャプチャされsayingます。ブロックはまた、値をメソッドに返します。これは、の戻り値として表示されますyield

lambdaルビーの単語は通常lambda、ブロックを独立した、オブジェクトのように機能させるために使用されるキーワードを意味します(それ自体は通常、lambdas、procs、またはProcsと呼ばれます)。

bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]

ですから、あなたが求めているのは、メソッドの引数としてProcaの代わりにaを渡すことができるかどうかだと思います。Hashそして答えは「状況次第」です。メソッドがメソッドのみを使用する場合は#[]、はい:

class Character
  attr_accessor :stats
  def set_stats(stats)
    @stats = stats
  end
end

frank = Character.new("Victor Frankenstein")
frank.set_stats({ :str => 7, :dex => 14, :con => 9, :int => 19, :wis => 7, :cha => 11 })

monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
  rand(20)
end)

ただし、他のHash特定のメソッドを使用したり、同じキーを複数回呼び出したりすると、奇妙な結果が生じる可能性があります。

monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
  rand(20)
end)

monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1

その場合は、リクエストを中間ハッシュにキャッシュしたほうがよい場合があります。Hashはイニシャライザブロックを持つことができるため、これはかなり簡単です。したがって、上記を次のように変更すると、次のようになります。

monster.set_stats(Hash.new do |stats_hash, stat_name|
  stats_hash[stat_name] = rand(20)
end)

monster.stats[:dex] #=> 3
monster.stats[:dex] #=> 3

結果はハッシュにキャッシュされます

Hashブロック初期化子の詳細については、以下を参照してくださいri Hash::new

-------------------------------------------------------------- Hash::new
     Hash.new                          => hash
     Hash.new(obj)                     => aHash
     Hash.new {|hash, key| block }     => aHash
------------------------------------------------------------------------
     Returns a new, empty hash. If this hash is subsequently accessed
     by a key that doesn't correspond to a hash entry, the value
     returned depends on the style of new used to create the hash. In
     the first form, the access returns nil. If obj is specified, this
     single object will be used for all default values. If a block is
     specified, it will be called with the hash object and the key, and
     should return the default value. It is the block's responsibility
     to store the value in the hash if required.

        h = Hash.new("Go Fish")
        h["a"] = 100
        h["b"] = 200
        h["a"]           #=> 100
        h["c"]           #=> "Go Fish"
        # The following alters the single default object
        h["c"].upcase!   #=> "GO FISH"
        h["d"]           #=> "GO FISH"
        h.keys           #=> ["a", "b"]

        # While this creates a new default object each time
        h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
        h["c"]           #=> "Go Fish: c"
        h["c"].upcase!   #=> "GO FISH: C"
        h["d"]           #=> "Go Fish: d"
        h.keys           #=> ["c", "d"]
于 2009-07-10T14:16:49.837 に答える