2009-07-10 3 views
2

Я хочу определить метод класса, который имеет доступ к локальной переменной. Таким образом, это будет отличаться для каждого экземпляра класса. Я знаю, что вы можете создать динамический метод класса с лямбдой, например, когда вы используете его с named_scope. Но можно ли это сделать для значений, специфичных для экземпляра?Можете ли вы передать себя лямбда в рельсах?

Подробнее это метод has_attached_file для плагина paperclip в рельсах. Я хочу передать лямбду для хэша стилей, чтобы стили изображений могли основываться на атрибутах объекта, хранящегося в БД. Это возможно?

+0

Вы можете уточнить - это ли lamdba время вызываемый код has_attached_file и, следовательно, из ваших рук? Потому что вы можете передавать параметры в lamdbas, когда вы их называете, и можете передавать «я» так же легко, как и все остальное. – Matt

ответ

13

Отказ от ответственности: Во-первых, вопрос (Можете ли вы передать себя лямбда?), и проблема, которую вы пытаетесь решить (динамические стили с помощью paperclip), не полностью совпадает. Я не буду отвечать на исходный вопрос, потому что он не полностью связан с вашей проблемой, и панпионод принял на него доблестный удар.

Вместо этого я отвечу на ваш вопрос о скрепке.

Более подробно это has_attached_file метод скрепку плагин в рельсах. Я хочу передать лямбду для хэша стилей, чтобы стили изображений могли основываться на атрибутах объекта, хранящегося в БД. Это возможно?

Да, это возможно. В скрепке, опция :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 
+0

Спасибо, Райан, это именно то, что я искал. – bwizzy

+0

@bwizzy - вы должны щелкнуть галочкой по ответу Райана, чтобы отметить, что (1) он ответил на ваш вопрос и (2), чтобы вознаградить его за это. – rampion

+0

Спасибо, извините, это мой первый пост. Спасибо за все быстрые ответы и большие подробности. – bwizzy

9

Хорошо, вы не поняли.

Локальные переменные в рубине начинаются со строчной буквы (например, foo, bar или steve), и лексическую область видимости (как C переменные). Они не имеют ничего общего с «экземпляром класса»

Переменные в рубине начинаются с @ сигилы (как @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 в рубина, как правило, означает 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"] 
+0

Ничего себе, спасибо за все подробности, этот отклик потрясающий! – bwizzy

Смежные вопросы