25-09-2011, 07:49 PM
(Modification du message : 25-09-2011, 07:58 PM par Sephi-Chan.)
Les méthodes de classe (aussi appelées méthodes statiques dans d'autres langages) sont à distinguer des méthodes d'instance.
Quelques méthodes de la classe ActiveRecord::Base : create, build, connection, uncached, etc.
Quelques méthodes d'instance de la classe ActiveRecord::Base : save, update_attribute, valid?, etc.
Ici, on définit une méthode de classe next_name_for. On sait que c'est une méthode de classe car elle est définie par self.next_name_for (on pourrait également la définir par Ant.next_name_for, mais c'est moins maintenable).
Un autre moyen de définir les méthodes de classe :
La méthode de classe uncached marche donc puisqu'on est dans le contexte de la classe Ant (qui hérite de ActiveRecord::Base). Si tu en as besoin ailleurs, tu n'as qu'à appeler Ant.uncached (ou n'importe quel modèle).
Concernant ta méthode product_ants!, voici ce que je propose. Ce n'est pas testé et tu devras peut-être retoucher, mais au moins tu auras le concept.
Pense à ne pas mettre de self inutiles comme self.ants ou self.user.
De même, même si ça ne coûte rien, n'utilise pas de variable d'instance (@foo) si ce n'est pas nécessaire.
Enfin, profite des méthodes avec un ! de Active Record, elles se chargeront de lancer des exceptions pour toi en cas de problème.
Quelques méthodes de la classe ActiveRecord::Base : create, build, connection, uncached, etc.
Quelques méthodes d'instance de la classe ActiveRecord::Base : save, update_attribute, valid?, etc.
class Ant < ActiveRecord::Base
PRODUCTION_DURATION = 6.seconds
# Quelque chose de plus cross-database ne serait-il pas plus intéressant ?
def self.next_name_for(user)
sequence = "fourmis_#{user.username}_id_seq"
query = "SELECT nextval('#{sequence}')"
uncached { ActiveRecord::Base.connection.select_value(query) }
end
end
Ici, on définit une méthode de classe next_name_for. On sait que c'est une méthode de classe car elle est définie par self.next_name_for (on pourrait également la définir par Ant.next_name_for, mais c'est moins maintenable).
Un autre moyen de définir les méthodes de classe :
class Ant < ActiveRecord::Base
PRODUCTION_DURATION = 6.seconds
class << self
# Quelque chose de plus cross-database ne serait-il pas plus intéressant ?
def next_name_for(user)
sequence = "fourmis_#{user.username}_id_seq"
query = "SELECT nextval('#{sequence}')"
uncached { ActiveRecord::Base.connection.select_value(query) }
end
end
end
La méthode de classe uncached marche donc puisqu'on est dans le contexte de la classe Ant (qui hérite de ActiveRecord::Base). Si tu en as besoin ailleurs, tu n'as qu'à appeler Ant.uncached (ou n'importe quel modèle).
Concernant ta méthode product_ants!, voici ce que je propose. Ce n'est pas testé et tu devras peut-être retoucher, mais au moins tu auras le concept.
class Ant < ActiveRecord::Base
PRODUCTION_DURATION = 8.hours
DELAY_BETWEEN_PRODUCTION = 6.seconds
def self.coming_after(time)
where('? < born', time)
# N'hésite pas à installer l'excellente gem Squeel pour pouvoir écrire (et plein d'autres trucs cools) :
# where { ? < born }
end
def self.next_name_for(user)
sequence = "fourmis_#{user.username}_id_seq"
query = "SELECT nextval('#{sequence}')"
uncached { ActiveRecord::Base.connection.select_value(query) }
end
end
class City < ActiveRecord::Base
# Add few ants to the production queue.
def product_ants!(quantity)
now = Time.current
running_ant = ants.coming_after(now).last
# If an ant is running, queue the new ones after it. Else it starts now.
next_ant_starts_at = if running_ant
running_ant.born + Ant:ELAY_BETWEEN_PRODUCTION
else
now
end
0.upto(quantity) do
# Increment the next_ant_starts_at by production duration of this ant.
#
# NOTE: On pourrait utiliser directement l'expression dans le hash,
# mais c'est plus clair et lisible de passer par une variable born_at.
born_at = (next_ant_starts_at += (Ant:RODUCTION_DURATION + Ant:ELAY_BETWEEN_PRODUCTION))
ants.create!(
sort: Ant::SORTS[rand(3)],
born: born_at,
name: Ant.next_name_for(user)
)
end
end
end
Pense à ne pas mettre de self inutiles comme self.ants ou self.user.
De même, même si ça ne coûte rien, n'utilise pas de variable d'instance (@foo) si ce n'est pas nécessaire.
Enfin, profite des méthodes avec un ! de Active Record, elles se chargeront de lancer des exceptions pour toi en cas de problème.