clir-data_manager 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,784 @@
1
+ # Manuel français de `Clir::DataManager`
2
+
3
+
4
+
5
+ [TOC]
6
+
7
+
8
+
9
+ ## Présentation
10
+
11
+ Cette classe permet de gérer facilement les données des instances (et des classes) dans les applications en ligne de commande (donc les applications tournant dans le Terminal).
12
+
13
+ Il suffit, pour une classe donnée, de définir les propriétés de ses instances en respectant quelques règles pour pouvoir ensuite afficher, créer, éditer et supprimer n'importe quelle valeur de ces instances.
14
+
15
+ Par exemple :
16
+
17
+ On définit les données de la classe `MaClasse` :
18
+
19
+ ~~~ruby
20
+ require 'clir/data_manager'
21
+
22
+ class MaClasse
23
+ include ClirDataManagerConstants
24
+ DATA_PROPERTIES = [
25
+ {prop: :id, type: :integer, specs:REQUIRED|DISPLAYABLE}, valid_if: {uniq: true},
26
+ {
27
+ prop: :name,
28
+ type: :string,
29
+ specs:ALL_SPECS,
30
+ valid_if: {
31
+ not_empty:true,
32
+ min_length: 3,
33
+ max_length:40
34
+ }
35
+ }
36
+ ]
37
+
38
+ # On doit définir aussi la sauvegarde
39
+ @@save_system = :card # données sauvées dans des fiches
40
+ @@save_format = :yaml # au format YAML
41
+ @@save_location = "./data" # dans le dossier 'data'
42
+ end
43
+ ~~~
44
+
45
+ On instancie un manager de données pour la classe :
46
+
47
+ ~~~ruby
48
+ Clir::DataManager.new(MaClasse)
49
+ ~~~
50
+
51
+ Cela implémente automatiquement plein de méthodes utiles pour les données et notamment :
52
+
53
+ ~~~ruby
54
+ MaClasse.get(id)
55
+ # Retourne l'instance d'identifiant :id
56
+
57
+ <MaClasse>#create([{data}])
58
+ # Pour créer une instance (avec ou sans les données {data}
59
+
60
+ MaClasse.items
61
+ # => liste des instances
62
+
63
+ MaClasse.table
64
+ # => table des instances avec en clé leur identifiant
65
+ ~~~
66
+
67
+
68
+
69
+ Note : si la classe définissait ses données dans une autre constante que `DATA_PROPERTIES`, il faudrait donner cette constante en second argument de `Clir::DataManager.new`.
70
+
71
+ ---
72
+
73
+ On peut maintenant utiliser cette méthode pour éditer les valeurs d'une instance :
74
+
75
+ ~~~ruby
76
+ inst = MaClasse.new
77
+
78
+ inst.create
79
+ # => Permet de créer l'instance
80
+
81
+ inst.edit
82
+ # => permet d'éditer (de modifier) l'instance
83
+
84
+ inst.display
85
+ # => Affiche les données de l'instance
86
+
87
+ inst.remove
88
+ # => Détruit l'instance
89
+
90
+ inst.new?
91
+ # => true si c'est une création d'instance (i.e. une instance qui n'a
92
+ # pas encore été enregistrée
93
+
94
+ ~~~
95
+
96
+ ---
97
+
98
+ ## Méthodes `after_` et `before_`
99
+
100
+ On peut programmer pour l’instance des méthodes à appeler avant (`before_`) ou après (`after_`) les opérations ci-dessus. On trouve donc :
101
+
102
+ * **`before_create`** sera appelée avant la création si elle existe. Elle peut retourner les données à prendre en compte,
103
+ * **`after_create`** sera appelée (sans argument) après la création complète de l’instance,
104
+ * **`before_edit`** sera appelée (sans argument) avant l’édition si elle existe (noter que la création appelle aussi cette méthode),
105
+ * **`after_edit`** sera appelée (sans argument) après l’édition et l’enregistrement de l’instance,
106
+ * **`before_display`** sera appelée avec les options en premier argument (optionnellement) avant l’affichage. Elle retournera optionnellement les options modifiées,
107
+ * **`after_display`** sera appelée (sans argument) après que l’instance a été affichée (donc, quand elle est affichée,
108
+ * **`before_remove`** sera appelée (sans argument) avant la destruction de l’instance,
109
+ * **`after_remove`** sera appelée (sans argument) après la destruction de l’instance (“instance”, ici, signifie ses données enregistrées, car l’instance en tant qu’objet informatique abstrait existe toujours)
110
+
111
+ ---
112
+
113
+ <a name="data-properties"></a>
114
+
115
+ ## Données des propriétés d’instance (`DATA_PROPERTIES`)
116
+
117
+ Le bon fonctionnement du ***manager de propriété*** tient principalement à la bonne définition des propriétés, généralement (mais pas exclusivement) dans la constante **`DATA_PROPERTIES`**. Cette définition permet de tout savoir sur les données et de savoir comment les gérer.
118
+
119
+ ### Liste `Array`
120
+
121
+ Les données sont définies dans une liste (`{Array}`) afin de préserver l'ordre défini.
122
+
123
+ ### Nom de la constante
124
+
125
+ Par défaut, le gem s'attend à trouver la constante **`DATA_PROPERTIES`** définie par la classe à manager. Mais ces données peuvent être définies dans toute autre constante si elle est fournie en second argument de l'instanciation du manager :
126
+
127
+ ~~~ruby
128
+ class MaClasse
129
+ AUTRES_DATA = [...]
130
+ def self.manager
131
+ @@manager ||= Clir::DataManager.new(self, AUTRES_DATA)
132
+ end
133
+ ~~~
134
+
135
+ ---
136
+
137
+ <a name="attributs-property"></a>
138
+
139
+ ## Attributs d’une propriété
140
+
141
+ Comme nous venons de le voir, chaque ligne (chaque item) de [`DATA_PROPERTIES`](#data-properties) définit une propriété de l’instance, comme on les appelle en ruby. Nous allons voir les arguments que doivent ou peuvent comporter chaque *property*.
142
+
143
+ <a name="attributs-list"></a>
144
+
145
+ ### Liste des attributs
146
+
147
+ * [**`:prop`**](#attribut-prop)
148
+ * [**`:name`**](#attribut-name)
149
+ * [**`:short_name`**](#attribut-short-name)
150
+ * [**`:type`**](#attribut-type)
151
+ * [**`:specs`**](#attribut-specs)
152
+ * [**`:default`**](#attribut-default)
153
+ * [**`:values`**](#attribut-values)
154
+ * [**`:values_filter`**](#attribut-values_filter)
155
+ * [**`:itransform`**](#attribut-itransform) (transformation immédiate de la valeur entrée)
156
+ * [**`:mformat`**](#attribut-mformat) (transformation de la valeur pour affichage)
157
+
158
+ ---
159
+
160
+ <a name="attribut-prop"></a>
161
+
162
+ ### `:prop` : Nom de la propriété, requise
163
+
164
+ `:prop` définit le nom de la propriété, pour savoir si c’est l’identifiant, le nom, la date de début etc.
165
+
166
+ La première propriété, dans *Data-Manager*, doit obligatoirement définir la propriété `:id` :
167
+
168
+ ~~~ruby
169
+ DATA_PROPERTIES = [
170
+ {prop: :id ... }
171
+ ]
172
+ ~~~
173
+
174
+ #### Nom de propriété spéciale : `:<class min>_id`
175
+
176
+ Un nom de propriété spécial — et même magique — est constitué d’un nom minusculisé de classe connue suivi par `_id`. Quand un nom est composé de la sorte, cela signifie que la propriété concernant l’identifiant d’une autre instance dans la classe est minusculisée.
177
+
178
+ Par exemple :
179
+
180
+ ~~~ruby
181
+ class MaClasseManaged
182
+ DATA_PROPERTIES = [
183
+ # ...
184
+ {prop: user_id ,name:"Propriétaire" ,type: :id ...}
185
+ ]
186
+ end
187
+ ~~~
188
+
189
+ … signifie que chaque instance de cette classe peut être liée à une instance de la classe `User`.
190
+
191
+ *DataManager* aussitôt lui-même ce genre de propriété, en :
192
+
193
+ * utilisant la méthode générale `choose` pour définir cette donnée,
194
+ * implémentant la méthode-propriété `classe_min`qui retournera l’instance en question.
195
+
196
+ **Minusculisation du nom de la classe**
197
+
198
+ Si le nom de la classe contient des capitales, il sera décamélisé (`DataManager` => `data_manager` => `data_manager_id`).
199
+
200
+ Si le nom de la classe est composé, les doubles doubles-points seront remplacés aussi par des tirets plats (`Edic::Produit` => `edic_produit` => `edic_produit_id`
201
+
202
+ <a name="liste-elements"></a>
203
+
204
+ #### Propriété spéciale : `<classe min>_ids`
205
+
206
+ Similaire à la classe précédente, mais pour une liste d’éléments. Le type est alors `:ids`
207
+
208
+ ~~~ruby
209
+ DATA_PROPERTIES = [
210
+ # ...
211
+ {prop: espace_nom_user_ids, name: "Partitipantes", type: :ids, values_filter: {sexe: 'F'}, ...}
212
+ ]
213
+ ~~~
214
+
215
+ Le code ci-dessus permet de choisir une liste de femmes (de “participantes”).
216
+
217
+ ---
218
+
219
+ <a name="espace-de-nom"></a>
220
+
221
+ #### Espace de nom en question
222
+
223
+ Attention : si la classe relative se trouve dans un espace de nom, même si c’est le même espace de nom que la classe en question, il faut indiquer cet espace de nom dans `:prop` :
224
+
225
+ ~~~ruby
226
+ module EspaceNom
227
+ class User
228
+ # ...
229
+ end
230
+
231
+ class MaClasseManaged
232
+ DATA_PROPERTIES = [
233
+ # ...
234
+ {prop: espace_nom_user_id , name: "Propriétaire" ,type: :id ...}
235
+ ]
236
+ end
237
+ end
238
+ ~~~
239
+
240
+ De la même manière, il faut noter que la propriété-méthode qui sera créé, pour récupérer l’instance, comportera cet espace de nom. Donc, dans le programme il faudra utiliser :
241
+
242
+ ~~~ruby
243
+ instance.espace_nom_user
244
+ # => instance EspaceNom::User
245
+ ~~~
246
+
247
+ Pour simplifier le code, on peut définir la classe managée de cette manière :
248
+
249
+ ~~~ruby
250
+ require 'clir/data-manager'
251
+
252
+ class MaClasseManaged
253
+ DATA_PROPERTIES = [
254
+ # ...
255
+ {prop: espace_user_id: name: "Propriétaire", type: :id}
256
+ ]
257
+ end #/class
258
+
259
+ Clir::DataManager.new(MaClasseManaged)
260
+
261
+ class MaClasseManaged
262
+
263
+
264
+ alias :user :espace_user
265
+ # et : alias :user_id :espace_user_id
266
+
267
+ end
268
+ ~~~
269
+
270
+ De cette manière on pourra facilement utiliser :
271
+
272
+ ~~~ruby
273
+ item = MaClasseManaged.new
274
+ item.user
275
+ # => Instance User de l'item
276
+ ~~~
277
+
278
+
279
+
280
+ ---
281
+
282
+ <a name="attribut-name"></a>
283
+
284
+ ### Attribut `:name`
285
+
286
+ Nom humain de la propriété, qui sera utilisé en label pour afficher les données de l’instance. C’est forcément un `String` (TODO: pour le moment en tout cas, car on pourrait imaginer que ça soit une procédure à évaluer en fonction de l’instance).
287
+
288
+ ---
289
+
290
+ <a name="attribut-short-name"></a>
291
+
292
+ ### Attribut `:short_name`
293
+
294
+ Nom humain court de la propriété, spécialement désigné pour être utilisé dans l’affichage par table.
295
+
296
+ ---
297
+
298
+ <a name="attribut-type"></a>
299
+
300
+ ### Attribut `:type`
301
+
302
+ #### Liste des types
303
+
304
+ ~~~bash
305
+ :string Une chaine quelconque
306
+ :number Un nombre quelconque
307
+ :select Valeur dans une liste (fournie par :values)
308
+ :date Date de type JJ/MM/AAAA
309
+ :mail Un email valide
310
+ :id ID d’une instance connue
311
+ :ids Liste d’IDs d’instance connues
312
+ :prix Un prix (nombre flottant)
313
+ :people Une ou des personnes
314
+ :url Une URL existante
315
+ ~~~
316
+
317
+ ---
318
+
319
+ <a name="attribut-specs"></a>
320
+
321
+ ### Attribut `:specs`
322
+
323
+ Spécificité de la propriété, permet de savoir si elle est requise (`REQUIRED`), éditable (`EDITABLE`), affichable (`DISPLAYABLE`), supprimable (`REMOVABLE`) ou toutes ces propriétés en même temps (`ALL`).
324
+
325
+ ~~~bash
326
+ REQUIRED # La propriété doit impérativement être définie
327
+ EDITABLE # La propriété peut être éditée. L'ID par exemple,
328
+ # ne peut pas l'être. Il est défini programmatiquement
329
+ # et ne peut être modifié
330
+ DISPLAYABLE # La propriété est affichée dans l'affichage complet de
331
+ # la donnée
332
+ TABLEIZABLE # La propriété est affichée dans l'affichage de la donnée
333
+ # dans une table (Clir::Table). Dans ce cas, il peut être
334
+ # intéressant de définir :short_name pour le libellé de
335
+ # l'entête.
336
+ REMOVABLE # La propriété peut être détruite (remplacée par nil)
337
+ ALL # Toutes les précédentes
338
+ ~~~
339
+
340
+
341
+
342
+ ---
343
+
344
+ <a name="attribut-default"></a>
345
+
346
+ ### Attribut `:default`
347
+
348
+ Valeur par défaut. Elle peut être définie par :
349
+
350
+ * une valeur explicite (d’un type correspondant au type de la propriété),
351
+
352
+ * une procédure de calcul qui reçoit en premier argument l’instance courante,
353
+
354
+ ~~~ruby
355
+ {prop: first_job_year, ... , default: Proc.new { |inst| inst.naissance + 18 }
356
+ ~~~
357
+
358
+ Noter que cette procédure peut retourner un `Symbol` qui pourra alors être considéré (ou pas) comme méthode de l’instance ou de la classe.
359
+
360
+ * un `Symbol` renvoyant à une méthode de l’instance OU une méthode de la classe de l’instance :
361
+
362
+ ~~~ruby
363
+ {prop: first_job_year, ... , default: :year_majorite}
364
+ # En considérant que :year_majorite est une méthode d'instance de la
365
+ # classe, qui retourne l'année de majorité de l'individu
366
+ ~~~
367
+
368
+
369
+
370
+ ---
371
+
372
+ <a name="attribut-values"></a>
373
+
374
+ ### Attribut `:values`
375
+
376
+ S'emploie pour un type `:select`.
377
+
378
+ ---
379
+
380
+ <a name="attribut-values_filter"></a>
381
+
382
+ ### Attribut `:values_filter`
383
+
384
+ Filtre à utiliser pour les `:values` en fonction de l'instance.
385
+
386
+ Peut s'utiliser pour les types `:select` et les `:prop` de type `:<class min>_ids` (type `:ids`).
387
+
388
+ Cette valeur doit être une table (un dictionnaire) qui contient en clé les propriétés qui devront être filtrées et en valeur le filtrage à appliquer. Par exemple :
389
+
390
+ ~~~ruby
391
+ {values: clients, values_filter: {sexe: 'F'} ...}
392
+ ~~~
393
+
394
+ … filtrera uniquement les clientes, puisqu’on cherchera dans les clients les instances dont la valeur `:sexe` est à `“F"`.
395
+
396
+ La valeur de la propriété peut être aussi un `Symbol`. C’est alors une propriété de l’instance. Un cas typique est le cas d’une facture. Une facture est donc une instance qui rassemble plusieurs achats d’un client (donc plusieurs ventes). Pour pouvoir choisir les ventes, on ne doit afficher que les achats du client. On utilise donc toutes les ressources de *DataManager* en définissant la propriété qui va consigner les identifiants des achats de cette manière :
397
+
398
+ ~~~ruby
399
+ class Facture
400
+
401
+ DATA_PROPERTIES = [
402
+ {prop: client_id, type: id, name: "Client"},
403
+ {prop: vente_ids, type: ids: name: "Achats concernés",
404
+ values_filter: {client_id: :client_id}
405
+ ]
406
+
407
+ end
408
+ ~~~
409
+
410
+ **EXPLICATION**
411
+
412
+ La première propriété, `:client_id`, de type `:id`, permet de choisir automatiquement un client dans la liste des clients. Elle prend comme valeur l’identifiant dudit client.
413
+
414
+ La seconde propriété, `:vente_ids`, de type `:ids`, permet de consigner les ventes concernées par la facture. Pour ne pas afficher toutes les ventes, mais seulement celles du client, le filtre `{client_id: client_id}` signifie : “ne prendre que les ventes dont le `client_id` (clé du filtre) n’est égal à la valeur `:client_id` de la propriété de la facture.
415
+
416
+ La **clé** correspond dont à une propriété de la chose concernée (ici les ventes)
417
+
418
+ La **valeur** correspond à une propriété de l’instance en édition (ici la facture).
419
+
420
+ ---
421
+
422
+ <a name="attribut-valid_if"></a>
423
+
424
+ ### Attribut`:valid_if`
425
+
426
+ Définition une procédure de validation de la donnée particulière. Peut être :
427
+
428
+ * une **Procédure (`Proc`)**. Elle est appelée avec en premier argument la nouvelle valeur et en second argument l’instance de l’objet traité,
429
+ * un **`Symbol`** peut être :
430
+ * une méthode (prédicate) appliqué à la valeur elle-même. Par exemple `numeric?`,
431
+ * une méthode d’instance de l’objet traité. Elle reçoit en premier argument la nouvelle valeur testée,
432
+ * une méthode de classe de l’objet traité. Elle reçoit les mêmes argument que la procédure (nouvelle valeur, instance).
433
+
434
+ Toutes ces méthodes doivent retourner `nil` si la nouvelle donnée est valide, ou le message d’erreur en cas de problème.
435
+
436
+ ---
437
+
438
+ <a name="attribut-itransform"></a>
439
+
440
+ ### Attribut `:itransform`
441
+
442
+ Transformation immédiate de la valeur entrée par l'utilisateur. Peut-être une `Proc` ou un `Symbol`.
443
+
444
+ Cf. [Formatage des données](#formatage-affichage).
445
+
446
+ ---
447
+
448
+ <a name="attribut-mformat"></a>
449
+
450
+ ### Attribut `:mformat`
451
+
452
+ Définition de la méthode de transformation de la donnée à l'affichage. Peut-être une `Proc` ou un `Symbol`.
453
+
454
+ Répond aux mêmes critères que [`itransform`](#attribut-itransform) mais s'applique seulement à l'affichage de la donnée.
455
+
456
+ Cf. [Formatage des données](#formatage-affichage).
457
+
458
+ ---
459
+
460
+ <a name="attribut-if"></a>
461
+
462
+ ### Attribut `:if`
463
+
464
+ L’attribut `:if` permet de déterminer si la propriété doit appartenir à l’instance, doit être défini. Par exemple, pour un certain produit il peut exister des propriétés spéciales qui n’appartiennent qu’à lui.
465
+
466
+ Peut être :
467
+
468
+ * un procédure (`Proc`),
469
+ * une méthode de l’instance (`Symbol`),
470
+ * une valeur booléenne explicite.
471
+
472
+ Cet attribut peut être une `Proc`édure. Cette procédure reçoit en argument l’instance et doit retourner `true` ou `false`. Par exemple :
473
+
474
+ ~~~ruby
475
+ {prop: :cover, ... , if: Proc.new { |inst| inst.type == 'livre' }
476
+ ~~~
477
+
478
+ Dans le cas ci-dessus, la propriété `:cover` définissant la couverture ne sera défini (éditable, affichable) que si la propriété `type`du produit est ‘livre’.
479
+
480
+ L’attribut peut être également une méthode (prédicate certainement) de l’instance. C’est alors un `{Symbol}`.
481
+
482
+ ~~~ruby
483
+ {prop: :cover, ... , if: :livre?}
484
+ # => Seulement si <instance>.livre? retourne true
485
+ ~~~
486
+
487
+
488
+
489
+
490
+ ---
491
+
492
+ ### Nom humain utilisé pour l’affichage de l’instance
493
+
494
+ Il y a 5 façons différents pour définir le nom (`:name` dans `Tty-prompt`) qui sera utilisé pour désigner l’instance :
495
+
496
+ * **valeur par défaut**. Par défaut, il s’agit de la **deuxième propriété définie** (entendu que la première est l’identifiant),
497
+
498
+ * **valeur générale**. Dans la classe, si on définit la **méthode d’instance #name4tty**, c’est cette méthode qui sera appelée et devra retourner le nom à afficher. Typiquement, si la classe est `People`, qui définit les propriétés `:nom` et `:prenom`, on pourra avoir une méthode :
499
+
500
+ ~~~ruby
501
+ class MaClasseManaged
502
+ def name4tty
503
+ "#{prenom} #{nom}".strip
504
+ end
505
+ end
506
+ ~~~
507
+
508
+ * **nom de méthode**. `#name4tty` peut également définir la méthode à utiliser, ce sera alors un `Symbol` :
509
+
510
+ ~~~ruby
511
+ class MaClasseManaged
512
+ def name4tty
513
+ :patronyme
514
+ end
515
+
516
+ def patronyme
517
+ @patronyme ||= "#{prenom} #{nom}".strip
518
+ end
519
+ end
520
+ ~~~
521
+
522
+ Noter qu’on peut aussi le faire par alias :
523
+
524
+ ~~~ruby
525
+ class MaClasseManaged
526
+ alias :name4tty :patronyme
527
+ end
528
+ ~~~
529
+
530
+ * surclassement. Il est possible de redéfinir la méthode (de classe) utilisée pour définir l’attribut `:name4tty` dans les options utilisées. Par exemple avec la méthode de classe `::choose` :
531
+
532
+ ~~~ruby
533
+ class MaClasseManaged
534
+ def self.choose
535
+ data_manager.choose({name4tty: :patronyme})
536
+ end
537
+ end
538
+ ~~~
539
+
540
+ De cette manière, chaque fois que `MaClasseManaged.choose` sera invoquée, cette méthode sera utilisée, redéfinissant la propriété à utiliser.
541
+
542
+ Il est également possible de définir une procédure plutôt qu’un symbol. Elle sera alors évaluée sur l’instance (noter, ci-dessous, également, comment on peut garder des options qui seraient passées) :
543
+
544
+ ~~~ruby
545
+ class MaClasseManaged
546
+ def self.choose(options = nil)
547
+ options ||= {}
548
+ options.merge!({name4tty: Proc.new { |ins| "#{inst.prenom} #{inst.nom}".strip })
549
+ data_manager.choose(options)
550
+ end
551
+ end
552
+ ~~~
553
+
554
+
555
+
556
+ * **attribut d’option**. Enfin, il est possible, ponctuellement de définir la méthode qui doit être utilisée en renseignant les `:options` qui accompagnent souvent un affichage. Par exemple, pour la méthode de classe `::choose`, qui permet de choisir une instance, on peut utiliser :
557
+
558
+ ~~~ruby
559
+ class MaClasseManaged
560
+ def self.envoyer_carte
561
+ destinataire = choose({name4tty: :patronyme})
562
+ end
563
+ end
564
+ ~~~
565
+
566
+ Ici aussi on peut avoir en valeur soit un `Symbol`, pour une méthode, soit une procédure qui prendra en premier argument l’instance traitée.
567
+
568
+ Notez que ces méthodes sont présentées par ordre inverse de précédence. C’est-à-dire que c’est la dernière qui sera utilisée prioritairement et la première qui sera utilisée en dernier recours. Donc, dans l’ordre, l’application fera :
569
+
570
+ 1. attribut direct dans les options envoyées (définissant soit une méthode — `Symbol`— soit une procédure — `Proc`),
571
+ 2. redéfinition de la méthode générale de classe (par exemple `::choose`),
572
+ 3. à égalité : méthode d’instance `#name4tty` (retournant un string, la valeur à afficher, ou bien un symbol, la méthode à utiliser)
573
+ 4. la deuxième propriété définie dans les [données des propriétés][].
574
+
575
+ ---
576
+
577
+ <a name="conditional-property"></a>
578
+
579
+ ## Propriété conditionnelle
580
+
581
+ Cf. l'[attribut `:if`](#attribut-if)
582
+
583
+ ---
584
+
585
+ <a name="formatage-affichage"></a>
586
+
587
+ ## Formatage de l’affichage des données
588
+
589
+ La classe managée peut définir des méthodes de formatage d’affichage particulières pour chaque donnée. Par convention, ce nom doit être `f_<property name>`. Par exemple, si la propriété est **`name`** alors le nom de la méthode de mise en forme de sa valeur doit être par défaut **`f_name`**.
590
+
591
+ Par exemple :
592
+
593
+ ~~~ruby
594
+ class MaClasseManaged
595
+
596
+ def age
597
+ @age ||= data[:age]
598
+ end
599
+
600
+ def f_age
601
+ "#{age} ans"
602
+ end
603
+
604
+ end
605
+ ~~~
606
+
607
+ On peut cependant définir un nom de méthode propre en définissant la propriété **`:mformate`** dans les [données des propriétés][].
608
+
609
+ > Note : toutes les valeurs commençant par “`:m` dans la définition des propriétés concernent des méthodes.
610
+
611
+ Par exemple :
612
+
613
+ ~~~ruby
614
+ class MaClasse
615
+ DATA_PROPERTIES = [
616
+ {prop: :name, name:"Patronyme", type: :string, specs:ALL_SPECS, mformate: :formate_nom}
617
+ {prop: :sexe, name:'Sexe', type: :string, specs:ALL_SPECS}
618
+ ]
619
+
620
+ def name; @name ||= data[:name] end
621
+ def sexe; @sexe ||= data[:sexe] end
622
+ def formate_nom
623
+ "#{sexe == 'F' ? "Madame" : "Monsieur"} #{name}"
624
+ end
625
+ end #/class
626
+ ~~~
627
+
628
+
629
+
630
+ ### Transformation de la donnée en entrée ou en sortie
631
+
632
+ Quand on attend un nom (patronyme), on peut vouloir systématiquement le passer en capitale, quelle que soit l’entrée de l’utilisateur.
633
+
634
+ Pour ce faire, dans la donnée de la propriété dans `DATA_PROPERTIES`, on ajoute l’attribut `:itransform` (qui signifie “input transform” ou “transformation de la donnée entrée”) qui transforme la donnée entrée (donc la donnée qui sera enregistrée) ou l’attribut `:mformat` qui transforme la valeur enregistrée seulement à l’affichage.
635
+
636
+ Ces attributs peuvent avoir différents types de valeur :
637
+
638
+ * **une procédure** qui reçoit en premier argument l’instance et en second argument la valeur entrée
639
+ * **un symbol**. qui définit :
640
+ * soit une **méthode de la valeur** (p.e. `:upcase` pour pour un `String` ou `:round` pour un nombre)
641
+ * soit une **méthode de l’instance** — qui reçoit en premier argument la valeur.
642
+
643
+
644
+ ---
645
+
646
+ <a name="data-manager"></a>
647
+
648
+ ## Atteindre le manager de données (`#data_manager`)
649
+
650
+ Depuis la classe, on peut faire appel au manager de données à l’aide de `data_manager`. Par exemple :
651
+
652
+ ~~~ruby
653
+ MaClasseManaged.data_manager.save_format
654
+ # Retourne :yaml si le format défini (dans @@save_format) est :yaml
655
+ ~~~
656
+
657
+ La seule méthode du data manager qui est exposée publiquement d’office, c’est la propriété **`save_location`** qui retourne le chemin d’accès soit au fichier de données (si `@@save_system = :file`) soit au dossier des fiches (si `@@save_system = :card`). On peut l’atteindre par :
658
+
659
+ ~~~ruby
660
+ MaClasseManaged.save_location
661
+ # => /path/to/folder/de/sauvegarde/
662
+ ~~~
663
+
664
+ ---
665
+
666
+ ## Choisir un item
667
+
668
+ Parmi les méthodes les plus pratiques du manage de données, il y a la méthode `choose` (implémentée dans la classe) qui permet de choisir un élément parmi tous ceux existant, avec ou non un filtre :
669
+
670
+ ~~~ruby
671
+ MaClasseManaged.choose(options = nil)
672
+ # => retourne l'instance choisie
673
+ ~~~
674
+
675
+ Les options sont les suivantes :
676
+
677
+ ~~~
678
+ :question La question à poser
679
+ :multi [Boolean] Si true, on peut choisir plusieurs items
680
+ :create [Boolean] Si true, on peut créer un nouvel item
681
+ :complete [Boolean] Si true, on ajoute un menu "Finir" pour… finir
682
+ Utile lorsque le choix est appelé en boucle quand on ne
683
+ veut pas (ou ne peut pas) utiliser :multi (ci-dessus)
684
+ :finir [Boolean] idem
685
+ :filter [Hash] Le filtre des éléments (cf. ci-dessous)
686
+ :exclude [Array<Ids>] Liste des identifiants à exclure de la liste
687
+ :default [Array<Ids>] Quand :multi est true, liste des ids qui
688
+ doivent être sélectionnés par défaut.
689
+ :sort_key Propriété qui doit servir de clé de classement
690
+ :sort_dir Direction du classement, 'asc' (défaut) ou 'desc'
691
+ ~~~
692
+
693
+ ### Filtre pour `choose`
694
+
695
+ La propriété `:filter` permet à la méthode `#choose` de choisir seulement un type d’élément. C’est un table avec des clés qui sont les propriétés des items.
696
+
697
+ Il y a quelques valeurs spéciales :
698
+
699
+ ~~~
700
+ :periode [Periode] Définit la période dans laquelle l'item doit se trouver
701
+ Pour juger de la place de l'item, le filtre se sert d'une propriété
702
+ :date, :created_at ou :time qui doit donc exister.
703
+ ~~~
704
+
705
+ ### Grouper les éléments pour `choose`
706
+
707
+ On peut aussi grouper les éléments pour les choisir en deux temps. Cf. ci-après.
708
+
709
+ ---
710
+
711
+ ### Groupement des items (`@@group_by`)
712
+
713
+ Lorsqu'il y a beaucoup d’items, il peut être plus clair de les grouper pour voir les choisir plus facilement. Ou par exemple, ce peut être simplement une histoire de cohérence sémantique. Par exemple, si l’on veut choisir un livre, on peut vouloir dans un premier temps le choisir simplement par le titre, sans avoir pour chaque livre les versions de support (papier, numérique, …) ou les langues.
714
+
715
+ Pour ce faire, on définit la variable générale **`@@group_by`** qui déterminera que la classe sera toujours groupée par cette donnée. Elle peut se définir au même niveau que les variables `@@save_location` etc. Sa valeur est une clés définie dans `DATA_PROPERTIES`.
716
+
717
+ ~~~ruby
718
+ @@group_by = :<props>
719
+ ~~~
720
+
721
+ Par exemple :
722
+
723
+ ~~~ruby
724
+ class MaClasseManaged
725
+ DATA_PROPERTIES = [
726
+ {prop: :id, name: "ID", ....}
727
+ {prop: :livre, name: "Livre", ...}
728
+ {prop: :name, name: "Nom", ....}
729
+ {prop: :cate, name: "Catégorie", ...}
730
+ ]
731
+
732
+ @@save_location = "path/to/the/file"
733
+
734
+ @@group_by = :cate
735
+
736
+ end
737
+ ~~~
738
+
739
+ Si la propriété est une propriété de type *identifiant* (par exemple `livree_id`) alors les items seront rassemblé sous l’instance correspondante et le nom de cette instance sera utilisée pour l’affichage.
740
+
741
+ Si la propriété choisie est une propriété quelconque, alors c’est sa valeur qui est utilisée dans la liste affichée. Par exemple, s’il existe les catégorie (`:cate`) “Boisson”, “Entrée” et “Fromage”, alors tous les éléments dans la catégorie “Boisson” seront rassemblés sous ce nom unique, tous les éléments de catégorie “Fromage” seront rassemblés sous ce nom, etc.
742
+
743
+ ---
744
+
745
+ <a name="les-plus"></a>
746
+
747
+
748
+ ## Les petits plus
749
+
750
+ Le fait de travailler avec `Clir::DataManager` offre de nombreux avantages, comme on a pu le voir. Il existe cependant quelques petites astuces à connaitre.
751
+
752
+ ### Messages féminisés
753
+
754
+ Pour obtenir des messages adaptés au genre d’une classe, on peut définir la méthode de classe `::feminine?` qui reverra true dans le cas d’une classe féminine. Par exemple :
755
+
756
+ ~~~ruby
757
+ class PaireDeLunettes
758
+ def self.feminine?; true end
759
+ end
760
+
761
+ # Produira par exemple le message suivant à la création d'une nouvelle
762
+ # instance : "Nouvelle PaireDeLunettes créée avec succès !"
763
+
764
+ class SacAMain
765
+ def self.feminine?; false end # <= mais inutile, car par défaut
766
+ end
767
+
768
+ # Produira par exemple le message suivant à la création d'une nouvelle
769
+ # instance : "Nouveau SacAMain créé avec succès !"
770
+
771
+
772
+ ~~~
773
+
774
+
775
+
776
+ ### Filtrer la liste des propriétés
777
+
778
+ Quand la liste des propriétés de l’instance est affichée, par exemple pour l’éditer (aka la modifier), on peut atteindre très rapidement la propriété à modifier en tapant ses premières lettres (ou ses lettres caractéristiques). Cela filtre la liste des propriétés et n’affiche que les propriétés correspondant au filtre. Si la liste des propriétés est longue, on peut énormément se simplifier la vie avec cette astuce.
779
+
780
+
781
+
782
+
783
+
784
+ [données des propriétés]: #data-properties