Introduction
Partie 3 : Encapsulons les méthodes liées à la persistance des données dans une classe.
C'est parti.
Etat des lieux
Pour l'instant, nous gérons nos accès à la persistance via 2 méthodes écrites sur le Top level
.
def add_task(item : String)
file = File.open(Config::TODO_FILE_PATH, "a")
file.puts item
file.close
end
def task_list : Array(String)
File.read_lines(Config::TODO_FILE_PATH)
end
Vision Objet.
Si nous regardons ça sous la vision d'un objet, notre persistance est un Repository
que l'on initialise avec le chemin d'accès. Puis nous aurons besoin des méthodes d'ajout d'un élément au dépôt et de lister ce que contient le dépôt.
Passons aux tests
Pour l'initialisation, nous voulons passer un chemin en argument. La vérification sera que le chemin est bien renseigné et que le fichier existe. Puis nous supprimerons le fichier pour la ré-utilisabilité du test.
describe "#new" do
it "initialize Repository with given path" do
path = File.join(Dir.current,"spec", "fixtures", "repo", "new.txt")
repo = Repository.new(path: path)
repo.path.should eq path
File.exists?(path).should eq true
File.delete(path)
end
end
Pour la méthode de remontée de la liste des éléments, nous allons créer un fichier all.txt
dans ./spec/fixtures/repo
. Nous valorisons le fichier avec 3 lignes.
test
all
method
et le test comme suit
describe "#all" do
it "return a list for a given file" do
path = File.join(Dir.current,"spec", "fixtures", "repo", "all.txt")
repo = Repository.new(path)
repo.all.should eq ["test","all","method"]
end
end
Enfin, pour l'ajout, nous initialisons un repo vide. Nous appelons notre méthode d'ajout. Puis vous vérifions que notre repo contient bien notre élément.
describe "#add" do
it "store todo into the given file" do
path = File.join(Dir.current, "spec", "fixtures", "repo", "add.txt")
repo = Repository.new(path)
repo.add("test add method")
repo.all.should eq ["test add method"]
File.delete(path)
end
end
Le tout dans le fichier ./spec/repository_spec.cr
, dans un describe
global.
En résumé :
require "./spec_helper"
describe Repository do
describe "#new" do
it "initialize Repository with given path" do
path = File.join(Dir.current,"spec", "fixtures", "repo", "new.txt")
repo = Repository.new(path: path)
repo.path.should eq path
File.exists?(path).should eq true
File.delete(path)
end
end
describe "#all" do
it "return a list for a given file" do
path = File.join(Dir.current,"spec", "fixtures", "repo", "all.txt")
repo = Repository.new(path)
repo.all.should eq ["test","all","method"]
end
end
describe "#add" do
it "store todo into the given file" do
path = File.join(Dir.current, "spec", "fixtures", "repo", "add.txt")
repo = Repository.new(path)
repo.add("test add method")
repo.all.should eq ["test add method"]
File.delete(path)
end
end
end
Implémentation de notre Repository
Si nous lançons les tests en tant que tel, nous aurons plein d'erreur de méthodes et de classes non définies.
Écrivons le code minimum pour que les tests se lancent
class Repository
getter path : String
def initialize(@path)
end
def all
end
def add(element : String)
end
end
La commande crystal spec
s'executent maintenant sans erreurs et tous nos tests sont en échec.
crystal spec
.....FFF
Failures:
1) Repository #new initialize Repository with given path
Failure/Error: File.exists?(path).should eq true
Expected: true
got: false
# spec/repository_spec.cr:9
2) Repository #all return a list for a given file
Failure/Error: repo.all.should eq ["test","all","method"]
Expected: ["test", "all", "method"]
got: nil
# spec/repository_spec.cr:17
3) Repository #add store todo into the given file
Failure/Error: repo.all.should eq ["test add method"]
Expected: ["test add method"]
got: nil
# spec/repository_spec.cr:25
Finished in 416 microseconds
8 examples, 3 failures, 0 errors, 0 pending
Failed examples:
crystal spec spec/repository_spec.cr:5 # Repository #new initialize Repository with given path
crystal spec spec/repository_spec.cr:14 # Repository #all return a list for a given file
crystal spec spec/repository_spec.cr:21 # Repository #add store todo into the given file
On voit ici l'importance du nommage des tests
Implémentation de l'init
Nous souhaitons que le chemin passé en argument corresponde à un fichier qui existe. Dans le cas contraire, nous le créons.
class Repository
...
def initialize(@path)
unless File.exists?(@path)
file = File.new(@path,"wb")
file.close
end
end
...
Le mode wb
permet de créer le fichier et de s'assurer qu'il soit vide.
Implémentation de la méthode all
Nous avons besoin d'ouvrir le fichier correspondant au path
et d'en lister le contenu. Nous pouvons reprendre le code d'origine et l'adapter à notre nouveau contexte.
class Repository
...
def all
File.read_lines(@path)
end
...
Implémentation de la méthode add
Comme ci dessus, nous adaptons l'existant.
class Repository
...
...
Execution des tests.
crystal spec
........
Finished in 473 microseconds
8 examples, 0 failures, 0 errors, 0 pending
Tout va bien.
Refactoring.
Etape 1 : Mettre notre nouvelle classe dans un fichier à part.
Nous prenons le code de notre class pour le mettre dans le fichier ./src/repository.cr
et nous le remplaçons par require "./repository"
dans notre fichier principal.
Etape 2 : Nous remplaçons les anciennes méthodes par notre classe.
items = task_list
devient
items = Repository.new(Config::TODO_FILE_PATH).all
et
add_task(item)
devient
Repository.new(Config::TODO_FILE_PATH).add(item)
Etape 3 : Nettoyage
Nous supprimons le code des anciennes méthodes.
Conclusion.
Nous avons vu comment créer une classe et la tester.
Dans la prochaine partie, nous allons encapsuler la partie serveur HTTP.
Top comments (0)