22-03-2020, 12:26 PM
(Modification du message : 22-03-2020, 03:00 PM par Sephi-Chan.)
Si ça peut t'aider à le concevoir, tu as le droit d'avoir des modèles qui ne correspondent pas à ta base de données. Généralement on appelle ça des service objects.
Je commence par écrire la tronche que je veux que ça ait, dans les tests (et donc aussi dans mon contrôleur quand je l'utiliserai vraiment) :
Ici, tu peux voir que je teste les valeurs de retour de la procédure (un value object tout simple dont j'aurais bien besoin pour construire la réponse à ma requête), puis le comportement de cette procédure vis-à-vis du système, c'est à dire qu'une fois qu'on a sacrifié 5 œufs sur les 10 créés, on ne peut plus en sacrifier 6 (ça pète).
Plutôt que de tester le stock de la colonie, je pourrais aussi effectuer une démarche qui nécessite d'avoir les ressources en question.
Le code est simple et trivial à suivre parce que je fais appel à quelques méthodes aux noms très explicites.
Tu noteras que je fais appel à de nombreuses méthodes statiques. Je trouve que dans les jeux, passer des ID a souvent de bien meilleurs résultats que des objets complets (souvent issus de l'ORM) qui nécessitent plein d'appels inutiles à la base. Ça colle parfaitement avec ta volonté d'utiliser ta base de données directement plutôt qu'à travers l'ORM.
Je commence par écrire la tronche que je veux que ça ait, dans les tests (et donc aussi dans mon contrôleur quand je l'utiliserai vraiment) :
describe TestEggSacrifice do
before do
@corwin = Player.create(name: "Corwin")
@montpellier = @corwin.cities.create(name: "Montpellier")
end
it "should explode if player doesn't exist" do
assert_raises Player:layerNotFound do
EggSacrifice.new(nil, nil, 42)
end
end
it "should explode if city doesn't belong to player" do
mandor = Player.create(name: "Mandor")
lyon = mandor.cities.create(name: "Lyon")
assert_raises City::CityNotFound do
EggSacrifice.new(@corwin.id, lyon.id, 42)
end
end
it "should explode when city doesn't have enough eggs" do
assert_raises EggSacrifice::NotEnoughEggs do
EggSacrifice.new(@corwin.id, @montpellier.id, 42)
end
end
it "should kill 5 eggs and add X resources to the city" do
City.spawn_eggs(@montpellier.id, 10)
result = EggSacrifice.new(@corwin.id, @montpellier.id, 5).execute()
assert results.city_id == @montpellier.id
assert results.remaining_eggs == 5
assert results.added_resources == ... # Whatever the rules say.
assert results.new_resources == ... # Whatever the rules say.
assert_raises EggSacrifice::NotEnoughEggs do
EggSacrifice.new(@corwin.id, @montpellier.id, 6).execute()
end
assert City.resources(@montpellier.id) == ... # Whatever the rules say.
end
end
Ici, tu peux voir que je teste les valeurs de retour de la procédure (un value object tout simple dont j'aurais bien besoin pour construire la réponse à ma requête), puis le comportement de cette procédure vis-à-vis du système, c'est à dire qu'une fois qu'on a sacrifié 5 œufs sur les 10 créés, on ne peut plus en sacrifier 6 (ça pète).
Plutôt que de tester le stock de la colonie, je pourrais aussi effectuer une démarche qui nécessite d'avoir les ressources en question.
class EggSacrifice
def initialize(city_id, count)
@city_id = city_id
@count = count
raise City::CityNotFound unless current_player.cities.exists?(city_id)
raise City::NotEnoughEggs unless City.has_enough_eggs?(city_id, count)
end
def execute()
City.transaction do
result = Egg.remove_n_from_city(@city_id, @count)
new_resources = City.add_resources(@city_id, result.resources)
{
city_id: @city_id,
remaining_eggs: result.remaining_eggs,
added_resources: result.resources,
new_resources: new_resources
}
end
end
end
Le code est simple et trivial à suivre parce que je fais appel à quelques méthodes aux noms très explicites.
Tu noteras que je fais appel à de nombreuses méthodes statiques. Je trouve que dans les jeux, passer des ID a souvent de bien meilleurs résultats que des objets complets (souvent issus de l'ORM) qui nécessitent plein d'appels inutiles à la base. Ça colle parfaitement avec ta volonté d'utiliser ta base de données directement plutôt qu'à travers l'ORM.