2

我想定义一个可以访问局部变量的类方法。所以这对于类的每个实例都是不同的。我知道您可以使用 lambda 使类方法动态化,就像将其与 named_scope 一起使用时一样。但是对于特定于实例的值可以这样做吗?

详细来说它是 rails 中回形针插件的 has_attached_file 方法。我想为样式散列传递一个 lambda,以便图像样式可以基于存储在数据库中的对象的属性。这可能吗?

4

2 回答 2

13

免责声明:首先,问题(Can you pass self to lambda?)和您试图解决的问题(使用回形针的动态样式)并不完全匹配。我不会回答最初的问题,因为它与您的问题并不完全相关,并且rampion对此进行了勇敢的尝试。

我会回答你的回形针问题。

详细来说就是has_attached_filerails中的回形针插件的方法。我想为样式散列传递一个 lambda,以便图像样式可以基于存储在数据库中的对象的属性。这可能吗?

是的,有可能。 在回形针中,该: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 中的局部变量以小写字母开头(如foobarsteve),并且是词法范围的(如C变量)。它们与“类的实例”无关

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

如果您想要一个可以直接访问对象的实例变量的方法,则称为实例方法。例如,battle_cryandinitialize都是实例方法:

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

lambdaruby 中的词通常表示lambda关键字,用于使块成为独立的、功能类似的对象(它们本身通常称为lambdas、procs 或Procs)。

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

所以我认为您要问的是您是否可以将 aProc代替 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

在这种情况下,您最好将请求缓存在中间哈希中。这相当容易,因为 aHash可以有一个初始化块。因此,如果我们将上面的内容更改为:

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 回答