si può passare a sé lambda a rotaie?
-
12-09-2019 - |
Domanda
voglio definire un metodo di classe che ha accesso a una variabile locale. Quindi questo sarebbe diverso per ogni istanza della classe. So che si può fare un metodo di classe dinamico con lambda come quando lo si utilizza con named_scope. Ma questo può essere fatto per i valori che sono specifici a un'istanza?
Nel dettaglio è il metodo has_attached_file per il plugin graffetta in Rails. Voglio passare un lambda per gli stili di hash in modo che gli stili di immagine possono essere in base al largo di attributi dell'oggetto memorizzato nel DB. È possibile?
Soluzione
Avviso: In primo luogo, la questione ( Can You Pass auto per lambda ?) E il problema si sta cercando di risolvere (stili dinamici con graffetta) non lo fanno completamente abbinare. Non voglio rispondere alla domanda iniziale, perché non è del tutto legato al tuo problema, e raperonzolo ha preso una pugnalata valente a questo.
Io invece rispondere alla tua domanda graffetta.
In dettaglio è il metodo
has_attached_file
per il plugin graffetta in Rails. Voglio passare un lambda per gli stili di hash in modo che gli stili di immagine possono essere in base al largo di attributi dell'oggetto memorizzato nel DB. È possibile?
Sì, è possibile. In graffetta, l'opzione :styles
può prendere un Proc. Quando l'allegato viene inizializzato, se è stato utilizzato un Proc, l'allegato stesso viene trasmesso al Proc. L'attacco ha un riferimento all'oggetto ActiveRecord associato, in modo da poter utilizzare che per determinare gli stili dinamici.
Per esempio, la vostra dichiarazione has_attached_file
potrebbe essere simile a questa (supponendo un utente e avatar scenario in cui l'utente può personalizzare la dimensione del loro 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
Altri suggerimenti
Ok, sei stato poco chiaro.
Le variabili locali in rubino iniziano con una lettera minuscola (come foo
, bar
o steve
), e sono con scope lessicale (come le variabili C
). Non hanno nulla a che fare con "un'istanza di una classe"
variabili istanza rubino iniziano con un sigillo @
(come @foo
, @bar
o @carl
), e sono in ambito ogni volta che il valore corrente della self
è l'oggetto sono memorizzati in.
Se si desidera un metodo che può accedere alle variabili di un oggetto direttamente esempio, che si chiama un metodo di istanza. Ad esempio, battle_cry
e initialize
sono entrambi metodi di istanza:
class Character
def initialize(name)
@name=name
end
def battle_cry
@name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
Un metodo di classe, al contrario, è un metodo per un oggetto Class
, e non ha accesso a qualsiasi delle variabili di quell'oggetto istanza. Nell'esempio di cui sopra,
default
è un metodo della classe.
Se si desidera un metodo (classe o istanza) che innesca un cambiamento o ottiene un valore dall'ambito corrente, rubino utilizza un tipo di callback chiama un blocco.
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"
Nell'esempio precedente, attack
utilizza la parola chiave yield
per chiamare il blocco passato
ad esso. Quando chiamiamo attack
, quindi, il num_attacks
variabile locale è ancora
portata nel blocco passiamo esso (delimitato da qui do ... end
), in modo che possiamo
incrementarlo. attack
è in grado di passare i valori nel blocco, qui
sono catturati nella variabile saying
. Il blocco passa anche valori
torna al metodo, che mostrano come il valore restituito yield
.
La parola lambda
in Ruby di solito significa la parola lambda
, che viene utilizzato
fare blocchi in autoportante, funzionano come oggetti (che si sono solitamente
denominati lambda
s, proc
s o Proc
s).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
Quindi penso che quello che stai chiedendo è se è possibile passare un Proc
al posto di un Hash
un argomento a un metodo. E la risposta è "dipende". Se il metodo solo
sempre utilizza il metodo #[]
, allora sì:
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)
Tuttavia, potrebbe utilizzare alcuni metodi specifici altra Hash
, oppure chiamare lo stesso tasto più volte,
che può produrre risultati strani:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
In questo caso, si può essere meglio cache le richieste in un hash intermedio. Questo è abbastanza facile,
poiché un Hash
può avere un blocco di inizializzazione. Quindi, se si cambia la precedenza a:
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
I risultati vengono tenuti nella hash
Per saperne di più sui initializers blocco Hash
, vedi 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"]