你能把自己传给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中的局部变量以小写字母开头(如foobarsteve ),并且是词法范围的(如C变量)。 他们与“一个class级的实例”无关

ruby中的实例变量以@foo (如@ @foo@ @bar@carl@carl ,并且只要self的当前值是它们存储的对象,它们就在范围内。

如果您想要一个可以直接访问对象的实例变量的方法,那就称为实例方法。 例如, 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的返回值。

ruby中的lambda这个词通常表示lambda关键字,它用于使块成为独立的函数类对象(它们本身通常被称为lambdaprocProc )。

 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"]