Railsで自分自身をラムダに渡すことはできますか?
-
12-09-2019 - |
質問
ローカル変数にアクセスできるクラスメソッドを定義したいと考えています。したがって、これはクラスのインスタンスごとに異なります。ラムダを使用して、named_scope で使用する場合と同様に、クラスメソッドを動的にできることはわかっています。しかし、インスタンスに固有の値に対してこれを実行できるでしょうか?
詳細には、rails のペーパークリップ プラグインの has_attached_file メソッドです。DB に保存されているオブジェクトの属性に基づいて画像スタイルを作成できるように、スタイル ハッシュのラムダを渡したいと考えています。これは可能でしょうか?
解決
免責事項: まず、質問(自分自身をラムダに渡すことはできますか?)と解決しようとしている問題(ペーパークリップを使用した動的スタイル)は完全には一致しません。元の質問にはお答えしません。それはあなたの問題と完全に関係がないためです。Rampion はそれに果敢に挑戦しました。
代わりに、あなたのペーパークリップの質問に答えます。
詳しく言えばそれは、
has_attached_file
レールのペーパークリッププラグインのメソッド。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
他のヒント
[OK]を、あなたが不明確であることだ。
ルビーにおけるローカル変数は、(foo
、bar
、またはsteve
など)小文字で始まり、字句(C
変数のように)スコープされています。彼らは、「クラスのインスタンス」とは何の関係もありません。
@
の現在値がそれらが格納されているオブジェクトであるときはいつでも、ルビーでインスタンス変数(@foo
、@bar
、又は@carl
など)self
のシギルで始まり、および範囲内にある。
、それはインスタンスメソッドと呼ばれています。例えば、battle_cry
とinitialize
両方インスタンスメソッドである
class Character
def initialize(name)
@name=name
end
def battle_cry
@name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
クラスメソッドは、対照的に、Class
オブジェクトのメソッドであり、そのオブジェクトのインスタンス変数のいずれかへのアクセス権を持っていません。上記の例では、
default
は、クラスメソッドである。
、ルビーは、ブロックと呼ばれるコールバックのタイプを使用します。
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
キーワードを意味し、
独立にブロックを作るために、自身が通常である(オブジェクトのように機能します
lambda
s、proc
s、又はProc
s)と呼ばれる。
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
だから私は何を求めていることは、あなたがProc
の代わりに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"]