【问题标题】:can you pass self to lambda in rails?你可以在rails中将self传递给lambda吗?
【发布时间】:2009-07-10 13:24:35
【问题描述】:

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

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

【问题讨论】:

  • 您能否澄清一下 - 这个 lamdba 是否被 has_attached_file 代码调用,因此不在您的控制范围内?因为你可以在调用 lamdbas 时将参数传递给它们,并且可以像传递其他任何东西一样轻松地传递“self”。

标签: ruby-on-rails ruby ruby-on-rails-plugins paperclip


【解决方案1】:

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

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

详细来说是rails中回形针插件的has_attached_file方法。我想为样式散列传递一个 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

【讨论】:

  • 感谢 Ryan,这正是我想要的。
  • @bwizzy - 你应该点击 Ryan 回答的复选标记,注意(1)他回答了你的问题,(2)奖励他这样做。
  • 谢谢,抱歉这是我的第一篇文章。感谢所有快速回复和详细信息。
  • 我正面临同样的问题,并使用@ryan-mcgeary 建议的方法解决了它。但是我还需要使用delayed_job,所以实例变量不起作用。我该如何解决这个问题?
【解决方案2】:

好吧,你不清楚。

ruby 中的局部变量以小写字母开头(如foobarsteve),并且是词法范围的(如C 变量)。它们与“类的实例”无关

ruby 中的实例变量以@ sigil 开头(如@foo@bar@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 关键字,使用 使块成为独立的,功能类似于对象(它们本身通常是 称为lambdas、procs 或Procs)。

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

【讨论】:

    猜你喜欢
    • 2020-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-21
    • 2020-09-25
    • 2015-02-13
    • 1970-01-01
    相关资源
    最近更新 更多