pouvez-vous passer à l'auto lambda dans des rails?
-
12-09-2019 - |
Question
Je veux définir une méthode de classe qui a accès à une variable locale. Donc, ce serait différent pour chaque instance de la classe. Je sais que vous pouvez faire une méthode de classe dynamique avec lambda comme quand vous l'utilisez avec named_scope. Mais cela peut être fait pour les valeurs qui sont spécifiques à une instance?
Dans le détail, il est procédé de la has_attached_file pour le plugin trombone dans des rails. Je veux passer un lambda pour les styles de hachage de sorte que les styles d'image peuvent être basé sur des attributs de l'objet stocké dans la base de données. Est-ce possible?
La solution
Disclaimer: Tout d'abord, la question ( Pouvez-vous passer à l'auto lambda ?) Et le problème que vous essayez de résoudre (styles dynamiques avec paperclip) ne le font pas entièrement correspondre. Je ne vais pas répondre à la question initiale, car il est pas tout à fait lié à votre problème, et raiponce a pris un coup de poignard vaillant dans ce domaine.
Je vais répondre à votre question au lieu de paperclip.
Dans le détail, il est la méthode
has_attached_file
pour le plug-in dans des rails paperclip. Je veux passer un lambda pour les styles de hachage de sorte que les styles d'image peuvent être basé sur des attributs de l'objet stocké dans la base de données. Est-ce possible?
Oui, il est possible. Dans paperclip, l'option :styles
peut prendre Proc. Lorsque la fixation est initialisé, si un Proc a été utilisé, la fixation elle-même est transmis à la Proc. La pièce jointe est une référence à l'objet ActiveRecord associé, vous pouvez donc l'utiliser pour déterminer vos styles dynamiques.
Par exemple, votre déclaration de has_attached_file
pourrait ressembler à ceci (en supposant un scénario utilisateur et avatar où l'utilisateur peut personnaliser la taille de leur 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
Autres conseils
Ok, tu es pas clair.
Les variables locales commencent à Ruby par une lettre minuscule (comme foo
, bar
ou steve
), et sont une portée lexicale limitée (comme les variables de C
). Ils ont rien à voir avec « une instance d'une classe »
Les variables d'instance en rubis commencent avec un sceau de @
(comme @foo
, @bar
ou @carl
), et sont dans la portée chaque fois que la valeur courante de l'objet est self
ils sont stockés dans.
Si vous voulez une méthode qui peut accéder aux variables d'instance d'un objet directement, ce qu'on appelle une méthode d'instance. Par exemple, battle_cry
et initialize
sont les deux méthodes d'instance:
class Character
def initialize(name)
@name=name
end
def battle_cry
@name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
Une méthode de classe, en revanche, est une méthode pour un objet Class
, et n'a pas accès à l'une des variables d'instance de cet objet. Dans l'exemple ci-dessus,
default
est une méthode de classe.
Si vous voulez une méthode (classe ou instance) qui déclenche un changement ou obtient une valeur de la portée actuelle, Ruby utilise un type de rappel appelé bloc.
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"
Dans l'exemple ci-dessus, attack
utilise le mot-clé yield
pour appeler le bloc passé
à elle. Lorsque nous appelons attack
, puis, la num_attacks
variable locale est toujours
portée dans le bloc que nous transmettons (délimité ici par do ... end
), afin que nous puissions
incrémenter. attack
est capable de transmettre des valeurs dans le bloc, ici
ils sont capturés dans la variable saying
. Le bloc passe également des valeurs
retour à la méthode, qui apparaissent comme la valeur de retour de yield
.
Le mot lambda
en rubis signifie généralement le mot-clé lambda
, qui est utilisé
pour faire des blocs en pose libre, fonctionnent comme des objets (qui sont eux-mêmes habituellement
dénommé lambda
s, proc
s ou Proc
s).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
Je pense donc que vous demandez est de savoir si vous pouvez passer un Proc
en place d'un Hash
pour un argument d'une méthode. Et la réponse est « ça dépend ». Si la seule méthode
utilise toujours la méthode #[]
, alors oui:
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)
Cependant, il peut utiliser d'autres méthodes spécifiques de Hash
, ou appeler les mêmes moments clés multiples,
qui peut produire des résultats étranges:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
Dans ce cas, vous pouvez être mieux mise en cache des requêtes dans un hachage intermédiaire. Ceci est assez facile,
étant donné qu'un Hash
peut avoir un bloc d'initialisation. Donc, si nous changeons ci-dessus à:
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
Les résultats sont mises en cache dans le hachage
Pour en savoir plus sur initializers de bloc Hash
, voir 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"]