你能把自己传给lambda in rails吗?
我想定义一个可以访问局部变量的类方法。 因此,对于每个类的实例,这将是不同的。 我知道你可以使用lambda创建一个类方法动态,就像你在named_scope中使用它一样。 但是,这可以针对特定于实例的值完成吗?
详细说明,它是rails中paperclip插件的has_attached_file方法。 我想为样式哈希传递lambda,以便图像样式可以基于存储在DB中的对象的属性。 这可能吗?
免责声明:首先,问题( 你能将自己传递给lambda吗? )和你试图解决的问题(使用回形针的动态样式)并不完全匹配。 我不会回答原来的问题,因为它与你的问题并不完全相关,而且猖獗地采取了勇敢的措施。
我会回答你的回形针问题。
详细说明,它是rails中paperclip插件的
has_attached_file
方法。 我想为样式哈希传递lambda,以便图像样式可以基于存储在DB中的对象的属性。 这可能吗?
对的,这是可能的。 在paperclip中, :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
好的,你还不清楚。
ruby中的局部变量以小写字母开头(如foo
, bar
或steve
),并且是词法范围的(如C
变量)。 他们与“一个class级的实例”无关
ruby中的实例变量以@foo
(如@
@foo
, @
@bar
或@carl
) @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
是一个类方法。
如果你想要一个(类或实例)方法触发更改或从当前范围获取值,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
的返回值。
ruby中的lambda
这个词通常表示lambda
关键字,它用于使块成为独立的函数类对象(它们本身通常被称为lambda
, proc
或Proc
)。
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"]