você pode passar auto de lambda em trilhos?
-
12-09-2019 - |
Pergunta
Eu quero definir um método de classe que tem acesso a uma variável local. Portanto, este seria diferente para cada instância da classe. Eu sei que você pode fazer uma dinâmica método de classe com lambda como quando você usá-lo com named_scope. Mas isso pode ser feito para valores que são específicos para uma instância?
Em detalhe, é o método has_attached_file para o plugin de clipe de papel em trilhos. Eu quero passar um lambda para o hash estilos para que os estilos de imagem pode ser baseado fora de atributos do objeto armazenado no DB. Isso é possível?
Solução
Disclaimer: Primeiro, a questão (? Você pode passar self para lambda ) e o problema que você está tentando resolver (estilos dinâmicos com clipe de papel) não coincidem totalmente para cima. Eu não vou responder a pergunta original, porque ele não está totalmente relacionado com o seu problema, e rampion levou uma facada valente para ele.
eu vou vez responder à sua pergunta clipe de papel.
Em detalhe, é o método
has_attached_file
para o plugin de clipe de papel em trilhos. Eu quero passar um lambda para o hash estilos para que os estilos de imagem pode ser baseado fora de atributos do objeto armazenado no DB. Isso é possível?
Sim, é possível. No clipe, a opção :styles
pode tomar um Proc. Quando a fixação é inicializado, se foi utilizado um Proc, a própria ligação é passado para o Proc. O anexo tem uma referência para o objeto ActiveRecord associado, então você pode usar isso para determinar seus estilos dinâmicos.
Por exemplo, sua declaração has_attached_file
poderia ser algo como isto (assumindo um cenário de usuário e avatar, onde o usuário pode personalizar o tamanho do seu avatar):
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
Outras dicas
Ok, você está sendo claro.
As variáveis ??locais em ruby ??começar com uma letra minúscula (como foo
, bar
, ou steve
), e possuem escopo léxico (como variáveis ??C
). Eles não têm nada a ver com "uma instância de uma classe"
As variáveis ??de instância em Ruby começar com uma sigilo @
(como @foo
, @bar
, ou @carl
), e estão no escopo sempre que o valor atual de self
é o objeto que eles são armazenados em.
Se você quer um método que pode acessar as variáveis ??de instância de um objeto diretamente, que é chamado um método de instância. Por exemplo, battle_cry
e initialize
são os dois métodos de instância:
class Character
def initialize(name)
@name=name
end
def battle_cry
@name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
Um método de classe, ao contrário, é um método para um objeto Class
, e não tem acesso a qualquer uma das variáveis ??de instância desse objeto. No exemplo acima,
default
é um método de classe.
Se você quiser um método (classe ou instância) que desencadeia uma mudança no ou obtém um valor do escopo atual, rubi usa um tipo de retorno de chamada chamado de bloco.
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"
No exemplo acima, attack
usa a palavra-chave yield
para chamar o bloco passado
a ele. Quando chamamos attack
, então, o num_attacks
variável local ainda é
no escopo no bloco que passá-lo (delimitado aqui por do ... end
), para que possamos
incrementará. attack
é capaz de passar valores para o bloco, aqui
eles são capturados na variável saying
. O bloco também passa valores
de volta para o método, que aparece como o valor de retorno de yield
.
A palavra lambda
em ruby ??geralmente significa a palavra-chave lambda
, que é usado
para fazer blocos em monobloco, função como objetos (que em si são normalmente
referido como lambda
s, proc
s, ou Proc
s).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
Então eu acho que você está perguntando é se você pode passar um Proc
no lugar de um Hash
para um argumento para um método. E a resposta é "depende". Se o método só
já usa o método #[]
, então sim:
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)
No entanto, pode usar alguns outros métodos Hash
específicos, ou ligue para a mesma tecla várias vezes,
que pode produzir resultados estranhos:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
Nesse caso, você pode ser melhor fora cache os pedidos em um hash intermediária. Isto é bastante fácil,
uma vez que um Hash
pode ter um bloco inicializador. Então, se nós mudar o acima para:
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
Os resultados são armazenados em cache no hash
Para ver mais sobre initializers bloco Hash
, consulte 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"]