Langages de scripts, automatisation de commandes et
production de documents structurés :
3. Langages de scripts et le patron de conception MVC
gilles.hunault "at" univ-angers.fr
Table des matières cliquable
Il est possible d'afficher toutes les solutions via ?solutions=1.
1. MVC, MVP, MVT ou BVC ?
Il arrive qu'on distingue les patrons de conception MVC, MVC-Model 2, MVP, MVT et BVC. De quoi s'agit-il exactement ? Est-ce applicable aux applications et projets de développement Web ?
Solution : masquer la solution
Solution détaillée volontairement non communiquée.
La réponse a été fournie en cours.
2. Des mini-MVC en Python
Réindenter mini_mvc1_py.txt, mini_mvc2_py.txt et mini_mvc3_py.txt avant de vérifier que ces programmes s'exécutent en Python 2. Les convertir ensuite en Python 3. Que peut-on en déduire sur MVC ?
# mini_mvc1_py.txt class Model: def get_post(self): return {"title":"A test","body":"An example.."} class View: def display(self,items): print 'Title:',items['title'],'\n'+'Body:',items['body'] class Controller: def __init__(self): self.model = Model() self.view = View() def main(self): post = self.model.get_post() self.view.display(post) mvc = Controller() mvc.main()# mini_mvc2_py.txt class Model(Control): users=dict(one='Bob',two='Michael',three='Dave') class View(): def user(self,users): print(users.find('two')) class Control: def find(self,user): return self._look(user) def _look(self,user): if user in self.users: return self.users[user] else: return 'The data class ({}) has no {}'.format(self.userName(),user) def userName(self): return self.__class__.__name__.lower() def main(): users=Model() find=View() print('--> The user two\'s "real name" is:\n') find.user(users) if __name__=="__main__": main()# mini_mvc3_py.txt # Source : http://tkinter.unpythonic.net/wiki/ToyMVC ## Some points to mention... ## ## The model knows nothing about the view or the controller. ## The view knows nothing about the controller or the model. ## The controller understands both the model and the view. ## ## The model uses observables, essentially when important data is changed, ## any interested listener gets notified through a callback mechanism. ## ## The following opens up two windows, one that reports how much money you ## have, and one that has two buttons, one to add money and one to remove ## money. ## ## The important thing is that the controller is set up to monitor changes ## in the model. In this case the controller notices that you clicked a ## button and modifies the money in the model which then sends out a ## message that it has changed. The controller notices this and updates ## the widgets. ## ## The cool thing is that anything modifying the model will notify the ## controller. In this case it is the controller modifying the model, but it ## could be anything else, even another controller off in the distance ## looking at something else. ## ## The main idea is that you give a controller the model and view that it ## needs, but the model's can be shared between controllers so that when ## the model is updated, all associated views are updated. -Brian Kelley ## ## following is a Tkinter approximation of the original example. import Tkinter as tk class Model: def __init__(self): self.myMoney = Observable(0) def addMoney(self, value): self.myMoney.set(self.myMoney.get() + value) def removeMoney(self, value): self.myMoney.set(self.myMoney.get() - value) class View(tk.Toplevel): def __init__(self, master): tk.Toplevel.__init__(self, master) self.protocol('WM_DELETE_WINDOW', self.master.destroy) tk.Label(self, text='My Money').pack(side='left') self.moneyCtrl = tk.Entry(self, width=8) self.moneyCtrl.pack(side='left') def SetMoney(self, money): self.moneyCtrl.delete(0,'end') self.moneyCtrl.insert('end', str(money)) class Controller: def __init__(self, root): self.model = Model() self.model.myMoney.addCallback(self.MoneyChanged) self.view1 = View(root) self.view2 = ChangerWidget(self.view1) self.view2.addButton.config(command=self.AddMoney) self.view2.removeButton.config(command=self.RemoveMoney) self.MoneyChanged(self.model.myMoney.get()) def AddMoney(self): self.model.addMoney(10) def RemoveMoney(self): self.model.removeMoney(10) def MoneyChanged(self, money): self.view1.SetMoney(money) class Observable: def __init__(self, initialValue=None): self.data = initialValue self.callbacks = {} def addCallback(self, func): self.callbacks[func] = 1 def delCallback(self, func): del self.callback[func] def _docallbacks(self): for func in self.callbacks: func(self.data) def set(self, data): self.data = data self._docallbacks() def get(self): return self.data def unset(self): self.data = None class ChangerWidget(tk.Toplevel): def __init__(self, master): tk.Toplevel.__init__(self, master) self.addButton = tk.Button(self, text='Add', width=8) self.addButton.pack(side='left') self.removeButton = tk.Button(self, text='Remove', width=8) self.removeButton.pack(side='left') if __name__ == '__main__': root = tk.Tk() root.withdraw() app = Controller(root) root.mainloop()Est-ce long de réécrire ces mini-MVC en php, en ruby ? Est-ce difficile de réécrire ces mini-MVC en php, en ruby ?
Solution : masquer la solution
Les textes correctement indentés sont nommés respectivement mini_mvc1.py, mini_mvc2.py et mini_mvc3.py. Comme indiqué, ils ne fonctionnent qu'avec Python 2.
Pour Python 3, voir mini_mvc1.py3, mini_mvc2.py3 et mini_mvc3.py3. En cas de problème d'installation, utiliser apt-get pour installer python3-tk et importer ensuite tk au lieu de Tk (donc sans majuscule sur le t) dans le code-source. Il y a un programme nommé 2to3 qui fait partie de la distribution de Python dont le but est d'essayer de convertir automatiquement de Python 2 vers Python 3.
Ce n'est ni long ni difficile de réécrire ces mini-MVC en php et en ruby. Voir par exemple mini_mvc1.rb.
3. Dans MVC, il y a M
On voudrait disposer d'un mini framework php avec MySql que l'on nommera minifp dont voici le comportement :
$gh>minifp --help minifp, syntaxe : minifp --help | --model optmodel | --data optmodel - le paramètre --help affiche cette aide - les options du paramètre --model sont tbl [ chmp:typ [chmp:typ]* | str ] où tbl désigne la table, chmp est un nom de champ, typ est le type du champ (chaine ou entier) str affiche la structure de la table $gh>minifp --model demo nom:chaine age:entier table demo crée avec deux champs $gh>minifp --model exemple nom:chaine prenom:chaine table exemple crée avec deux champs $gh>minifp --model demo prenom:chaine table demo mise à jour, trois champs désormais $gh>minifp --data demo nom:_CURIE age:30 prenom:_Marie Valeurs nom, age, prenom insérées dans la table demoEst-ce raisonnable d'utiliser cette syntaxe pour définir un nombre important de champs dans une table ? Quelle autre solution pourriez-vous proposer ?
Remarque : le caractère souligné (qui permet d'ajouter une donnée textuelle au lieu de numérique) devra être enlevé lors de la finalisation du programme, le programme devant être capable de détecter le type des données après consultation du schéma de la table. Il manque aussi une création automatique des id...
Recommencer ensuite avec minifr (mini framework en Ruby) et minifpy (mini framework en Python).
Si le texte d'aide est long, quelle solution peut-on envisager ?
Solution : masquer la solution
Voir minifp_v1.php (solution étudiante laissée telle quelle, à améliorer). Si on doit définir un nombre important de champs, il vaut mieux passer par un fichier texte, par exemple formaté en YAML. Par, exemple, au lieu de
$gh>minifp --model demo nom:chaine prenom:chaine age:entier adresse:chaine $gh>minifp --model demo cours:chaine id:entier section:entierOn pourait utiliser les paramètres --model-file def.yaml où le fichier cité est :
--- table: demo nom: chaine prenom: chaine age: entier adresse: chaine cours: chaine id: entier section: entierDe même pour saisir des données, un fichier CSV peut être une bonne solution, mais un générateur aléatoire n'est pas à écarter.
Si le texte d'aide est long, une solution "propre" est d'écrire une page Web d'aide et d'afficher son URL dans la console, car avec un CTRL-click la page s'affiche dans le navigateur. Démonstration en TP via le script preLefse.pl qui affiche l'URL suivante :
http://forge.info.univ-angers.fr/~gh/Metaseed/preLefse.phpCette aide est accessible ici.
4. Dans MVC, il y a V
Compléter minifp, minifr et minifd pour obtenir le comportement suivant :
$gh>minifp --help minifp, syntaxe : mini --help | --model optmodel | --data optmodel | --view optview - le paramètre --help affiche cette aide - les options du paramètre --model sont tbl [ chmp:typ [chmp:typ]* | str ] où tbl désigne la table, chmp est un nom de champ, typ est le type du champ (chaine ou entier) str affiche la structure de la table - les options du paramètre --view sont tbl [champ/valeur] [--mode optmode ] table affiche toute la table dont le nom est passé en paramètre ou seulement les enregistrements correspondant au filtre induit par un ET entre les arguments et où les options --mode sont show URI | go URI ; URI est une ressource écrite en REST ; show affiche l'URL associée alors que go l'affiche avec firefox. $gh>minifp --view tbl demo 0001 CURIE 30 Marie $gh>minifp --view tbl demo --mode show http://.../tbl/demo $gh>minifp --view tbl demo --mode go # lancer firefox http://.../tbl/demo $gh>minifp --view tbl demo nom/Dupont ... # personnes de nom Dupont $gh>minifp --view tbl demo nom/Dupont/age/20 ... # personnes de nom Dupont ayant 20 ans $gh>minifp --view tbl demo nom/Dupont/age/20+/ville/Angers ... # personnes de nom Dupont ayant 20 ans ou plus dont la ville est AngersRemarque : pour afficher une table, on pourra utiliser catb_rex.txt ou affichage avec l'option -v -v -v de Mysql (le triplement de -v est volontaire).
Solution : masquer la solution
Solution volontairement non communiquée.
Des indications sont données en T.P.
5. Dans MVC, il y a C
Si on voulait compléter nos minif* pour en faire des vrais MVC, que faudrait-il ajouter ? Définir le comportement à l'aide d'exemples et implémenter cette partie C de MVC.
On pourra s'inspirer de TPLONG.
Solution : masquer la solution
Solution volontairement non communiquée.
Code-source php de cette page ; code javascript utilisé. Retour à la page principale du cours.
Retour à la page principale de (gH)