我想定义一个可以访问局部变量的类方法.因此,对于每个类的实例,这将是不同的.我知道你可以使用lambda创建一个类方法动态,就像你在named_scope中使用它一样.但是,这可以针对特定于实例的值完成吗?
详细说明,它是rails中paperclip插件的has_attached_file方法.我想为样式哈希传递lambda,以便图像样式可以基于存储在DB中的对象的属性.这可能吗?
免责声明:首先,问题(你能将自己传递给lambda吗?)和你试图解决的问题(使用回形针的动态样式)并不完全匹配.我不会回答原来的问题,因为它与你的问题并不完全相关,而且猖獗地对它进行了勇敢的抨击.
我会回答你的回形针问题.
详细地说,它是
has_attached_file
轨道中的回形针插件的方法.我想为样式哈希传递lambda,以便图像样式可以基于存储在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
好的,你还不清楚.
在红宝石局部变量以小写字母开头(如foo
,bar
或steve
),并词法范围(如C
变量).它们与"一个类的实例"无关
ruby中的实例变量以@
sigil(如@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
是一个类方法.
如果你想要一个(类或实例)方法触发更改或从当前范围获取值,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
.
lambda
ruby中的单词通常表示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
在这种情况下,您可能最好在中间散列中缓存请求.这很容易,因为a 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"]