Sans plus attendre, voici les résultats en image :
Comme Ruby a des performances bien en dessous de Go et Crystal, voici le même graphique en le retirant :
Plus de détails sur les tests par la suite.
Comme premier programme, j’ai choisi de faire un benchmark avec un programme comptant selon la suite de Fibonacci. N’étant pas un développeur Go, j’ai trouvé le code ici et l’ai modifié pour faire ce que je souhaite, c’est-à-dire m’afficher uniquement le résultat selon la valeur passée en paramètre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Voici une implémentation similaire en Ruby :
1 2 3 4 5 6 7 8 9 |
|
Et la voici avec Crystal :
1 2 3 4 5 6 7 8 9 |
|
Ne cherchez pas de différence entre Ruby et Crystal, il n’y en a pas. Dans ce cas, Ruby et Crystal sont totalement similaires.
Voici les résultats :
1 2 3 4 5 |
|
1 2 3 4 5 |
|
1 2 3 4 5 |
|
On peut voir que Go gagne suivi de Crystal et de Ruby. Cependant, dans ce test, les temps d’exécution sont tellement rapides que beaucoup d’autres facteurs peuvent entrer en jeu dans le temps d’exécution.
Pour poursuivre les tests, j’ai voulu augmenter la valeur à calculer. Malheureusement, pour Go et Crystal, la taille des entiers n’est pas suffisante. Il a donc été nécessaire de modifier le code pour utiliser des BigInt.
Voici le code Go modifié :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Et la même chose avec Crystal :
1 2 3 4 5 6 7 8 9 10 11 |
|
Et voici les résutats :
1 2 3 4 5 |
|
1 2 3 4 5 |
|
1 2 3 4 5 |
|
Le résultat est un peu décevant. L’utilisation du BigInt en Crystal est lente. La raison de cette lenteur viens du faire que Crystal utilise GMP qui est manifestement plus lent que l’implémentation de Go. Vous pouvez voir une discussion sur la lenteur de Crystal ici.
Je n’ai pas voulu me laisser abattre au premier test et j’ai décidé d’en faire d’autres. J’ai trouvé ce projet. Malheureusement, les codes proposés ne fonctionnent pas toujours avec les dernières versions des langages. Je me suis contenté de faire une comparaison avec les scripts fonctionnant sans modifications.
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
On peut voir que Crystal est deux fois plus rapide que Go. J’émets une petite réserve sur le code fait avec Crystal. Celui-ci a été fait à partir de celui fait en Python et ne respect pas la “Crystal way”.
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
Encore une fois, Ruby est en retrait. Les performances entre Go et Crystal sont comparables bien que Go est légèrement plus rapide.
Pour conclure, je dirais que Crystal, bien que très jeune, offre des performances intéressantes. Le futur nous dira s’il s’agit d’un projet éphémère ou s’il fera ses preuves.
J’espère pouvoir faire d’autres tests sur Crystal très bientôt. Je pense essayer de faire une API. Suivez-moi sur Twitter pour rester au courant.
]]>Cet article se base sur l’article précédent. L’application testée, le code ainsi que les pages testées sont les mêmes que celles utilisées pour faire la comparaison entre Digital Ocean et Scalingo.
Je ne suis toujours pas administrateur système. Si vous constatez des irrégularités dans mes tests, n’hésitez pas à m’en faire part et je les corrigerais. Cependant, je pense utiliser une méthode simple se basant sur des faits afin d’avoir une analyse pertinente.
Les Collectionneurs Associés est une application Rails qui n’a pas été conçue uniquement pour des tests. Cela nous donne une meilleure idée du comportement des serveurs dans la réalité.
L’offre la plus petite de Clever Cloud coûte 14,40€ par mois. Pour ce prix, nous avons 1 CPU et 1Gb de RAM. Pour le même prix, chez Scalingo, nous avons une machine avec 1CPU et 512Mb de RAM. Cependant, avec Scalingo, il est possible de descendre plus bas avec 256Mb pour 7,20€. J’ai choisi de prendre l’offre à 14,40€ dans les deux entreprises. Les deux serveurs se trouvent en France.
Le serveur à partir duquel je fais mes tests est un serveur fourni par Digital Ocean situé à Londres. Il est donc représentatif des clients de l’Europe de l’Ouest, incluant la France, évidemment.
Le code n’est probablement pas le meilleur que j’ai écrit de ma vie, mais est largement suffisant pour faire ce que je souhaitais.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Ce code permet d’entrer une adresse et de simuler un certain nombre de visites concurrentes faisant un certain nombre de requêtes. À la fin de l’exécution du programme, le temps de réponse de chaque processus est affiché. Ce code ne prend pas en compte le chargement des images et des assets. Dans mon cas, ces éléments sont stockés sur un CDN et sont donc indépendants du serveur où l’application se trouve.
Ce premier test affiche une page simple avec 11 requêtes. Voici les temps de réponse :
Scalingo :
126.364873
126.482741
126.609597
126.741594
126.857076
126.988266
127.107123
127.235066
127.356753
127.469629
Clever Cloud :
127.890590
128.021063
128.147565
128.274117
128.400284
128.528602
128.655974
128.777820
128.897637
129.032317
Pour ce test, on s’aperçoit que les résultats sont quasiment identiques. La différence de 2 secondes est, pour moi, négligeable.
Pour continuer, j’ai réexécuté le programme sur une page plus complexe. Voici le résultat :
Scalingo :
762.209817
762.938170
763.748126
764.493016
765.395998
766.176983
767.026748
767.746600
768.578646
769.388282
Clever Cloud :
766.513019
767.324059
768.050523
768.837619
769.624915
770.354487
771.162319
771.894288
772.703604
773.544202
Encore une fois, la différence des temps de réponse est négligeable. Pour le moment, les performances de l’application hébergée sur les serveurs des deux entreprises sont équivalentes.
J’ai voulu poursuivre avec la page d’inscription, plus complexe.
Scalingo :
415.211191
415.640399
416.049001
416.437286
416.847855
417.337447
417.744488
418.145630
418.528678
418.999209
Clever Cloud :
356.496478
356.825021
357.213072
357.562785
357.917548
358.310875
358.650679
358.991046
359.396792
359.738898
Clever Cloud est légèrement plus rapide et, pour moi, ces résultats permettent de rattraper les légers retards enregistrés précédemment.
Voici une conclusion très ennuyante pour un article comparatif : les performances des deux offres sont équivalentes. Ces résultats ne me permettent pas de trancher entre l’un et l’autre.
Les deux entreprises offrent un très bon support, leurs interfaces d’administration sont très bien faites et l’utilisation est deux systèmes est très simple. Pour choisir, il n’y a pas d’autre solution que de tester vous-même et de faire votre propre opinion, kit à vous baser sur des notions plus subjectives.
Malgré tout, à la fois Scalingo et Clever Cloud sont plus performants qu’Heroku et Digital Ocean. Je leur donne une position qui serait décevante aux JO, la médaille d’or exequo.
]]>Voici l’application testée : https://www.associatedartcollectors.com/fr. Il s’agit d’une application Rails optimisée, avec un système de mise en cache, des assets sur un CDN, etc. Il s’agit d’une vraie application (encore en développement). J’ai préféré l’utiliser plutôt que d’en faire une uniquement pour des tests afin de mieux représenter la réalité.
Le serveur Digital Ocean est situé à Francfort et coûte 10$ par mois pour 1Go de RAM. Scalingo, quant à lui, est situé en France et coûte 14.40€ pour 512Mb. Scalingo est donc légèrement plus cher pour moins de puissance.
Scalingo étant beaucoup plus facile à gérer, je pense que le temps économisé au niveau de la gestion du serveur vaut largement le léger surplus de prix. En effet, dans son utilisation, Scalingo ressemble davantage à Heroku qu’a Digital Ocean. J’ai choisi de ne pas comparer Scalingo à Heroku, car, selon de précédents tests, Digital Ocean offre une meilleure performance. J’ai voulu comparer Scalingo au meilleur.
Je ne suis pas administrateur système. Il est probablement possible de faire mieux que ce que j’ai fait sur Digital Ocean. Ceci dit, je suis probablement dans la même situation qu’un bon nombre de développeurs. L’intérêt des systèmes similaires à Heroku ou Scalingo est également ne pas s’occuper du système. On créer l’application en 2 minutes, on pousse l’application et ça marche. C’est tout.
La plus grosse part de marché de Scalingo est située en France. J’ai souhaité commencer mes tests proches de la France. Pour cela, j’ai choisi de créer un droplet Digital Ocean à Londres.
Mon objectif a été de simuler un certain nombre de requêtes et ceux de manière concurrente. J’ai donc créé un code qui lance plusieurs processus simultanément. Chaque processus effectue un certain nombre de requêtes. Le temps effectué par chaque processus permet de comparer la performance. Plus les requêtes se sont complétées rapidement, plus le serveur est efficace.
Le code n’est probablement pas le meilleur que j’ai écrit de ma vie, mais est largement suffisant pour faire ce que je souhaitais.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Ce code permet d’entrer un nombre de processus simultané et le nombre de requêtes fait pour chaque processus. De plus, il permet d’indiquer la page appelée par le code. Pour les tests, j’ai choisi des pages différentes qui sont représentatives de l’application.
Le code permet également d’afficher le statut de la requête envoyé, afin de s’assurer qu’elles sont toutes valides, ainsi que le numéro de la requête effectuée, juste pour voir la progression.
J’ai eu un problème avec le protocole SSL sur Digital Ocean. Pour le test, j’ai simplement désactivé la vérification.
Le premier test consiste uniquement à visiter une page. Pour l’affichage de cette page, 11 requêtes SQL sont exécutées.
Digital Ocean :
Threads : 10
Requests per thread : 100
Temps de réponse :
163.755285
163.906003
164.070419
164.222361
164.379131
164.520751
164.717367
164.894695
165.059584
165.245484
Scalingo :
Threads : 10
Requests per thread : 100
Temps de réponse :
110.868637
110.982509
111.086731
111.195775
111.377322
111.482194
111.590034
111.697870
111.821420
111.938271
Ce premier test montre clairement que Scalingo offre un temps de réponse plus rapide. Scalingo est quasiment deux fois plus rapide.
Dans ce deuxième test, j’ai voulu tester le comportement avec plus de requêtes concurrentes.
Digital Ocean :
Threads : 100
Requests per thread : 10
Temps de réponse :
150.062375
150.365437
150.532612
165.780870
165.941798
166.111019
Scalingo :
Threads : 100
Requests per thread : 10
Temps de réponse :
92.787676
92.877818
92.959175
...
102.423316
102.525122
102.622236
Encore cette fois, Scalingo est nettement supérieur. Pendant que les tests s’effectuaient, je suis allé sur les deux sites avec mon navigateur. Même si cela ne se reflète par sur les résultats du benchmark, j’ai constaté une grande dégradation de performance pour les deux fournisseurs.
Par la suite, j’ai souhaité tester une autre page un peu plus complexe. Sur cette page, plus de requêtes SQL sont effectuées et il y a plus de code à exécuter.
Digital Ocean :
Thread : 10
Request : 100
Temps de réponse :
204.311959
204.509295
204.683958
204.864534
205.055946
205.240614
205.440767
205.621924
205.807302
206.020173
Scalingo :
Thread : 10
Request : 100
Temps de réponse :
143.032002
143.170704
143.322095
143.458987
143.596800
143.742877
143.877174
144.017422
144.161915
144.303397
Sans surprise, le résultat est le même dans ce cas.
Enfin, j’ai voulu tester une page encore plus complexe. Il s’agit du formulaire d’inscription d’un utilisateur. J’utilise des gems comme SimpleForm pour gérer le formulaire. Les listes des services, des pays et des régions sont disponibles ce qui correspond à autant de requête SQL et de donnée à traiter.
Digital Ocean :
Thread : 10
Request : 100
Temps de réponse :
420.542306
420.998163
421.375207
421.724786
422.231918
422.618054
422.983907
423.442330
423.815438
424.163915
Scalingo :
Thread : 10
Request : 100
Temps de réponse :
669.174683
669.800881
670.571744
671.256995
671.949939
672.582819
673.282322
673.983594
674.634513
675.310771
Cette fois, Digital Ocean est plus rapide. Je pense que c’est là que la différence de puissance du serveur se fait sentir. Étant donné qu’il y a plus de code a exécuter, le traitement nécessite plus de ressource, et donc, Digital Ocean est avantagé. Ceci dit, cette page n’est pas l’une des plus utilisées et je pense que la performance est plus importante pour les pages permettant uniquement l’affichage.
Je pense que Scalingo gagne le duel. En plus d’offrir une performance plus qu’honorable, la gestion du serveur est grandement simplifiée. Je conseille vivement d’essayer cet outil si vous n’avez pas besoin d’avoir un accès complet à la machine. Comme moi, les développeurs ne sont pas toujours des administrateurs systèmes. Je trouve beaucoup plus simple de laisser la gestion du serveur à quelqu’un d’autre. En plus d’être probablement mieux configuré que ce que j’ai fait, c’est également plus sécuritaire, car, si une faille de sécurité est découverte, le problème sera géré par des professionnels dans le domaine.
Pour cet article, mes tests ont été effectués depuis Londres afin de représenter la clientèle européenne. Pour ma part, je me situe au Québec. Dans une seconde étape, je vais tester depuis des serveurs en Amérique du Nord et à partir d’autres endroits dans le monde pour observer le comportement selon les lieux.
Dans un prochain article, je vais faire la comparaison entre Clever Cloud et Scalingo. Surveillez mes prochains articles pour connaitre les résultats de mon prochain benchmark.
]]>ActiveRecord::Base
ainsi
que le noyau d’ActiveRecord.
Comme dans les articles précédents, je vais utiliser Pry pour explorer. Je vous conseille fortement de lire le code en même temps que l’article.
Pour commencer, je vais reprendre le code servant à créer des issues dans ActiveRecord en y plaçant un breakpoint afin de pouvoir étudier le comportement du modèle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Commençons par le commencement. Lorsque l’on charge la gem activerecord
, le fichier
activerecord.rb est inclus.
Comme on peut le voir en lisant le code, celui-ci commence par chargé ses
dépendances.
1 2 3 4 5 6 7 |
|
On comprend donc que ActiveRecord
dépend d’ActiveSupport
, ActiveModel
et
Arel
. ActiveSupport
est un module fournissant des fonctions utiles dans les
autres modules de Rails. ActiveModel
contient les informations spécifiques au
modèle du design pattern MVC
sans contenir les informations relatives à la
base de données. Arel
, quant à lui, permet de créer des requêtes SQL en Ruby.
Les premières lignes du module sont les suivantes :
1 2 3 |
|
Plutôt que de faire un require
, ActiveRecord
utilise le autoload
d’ActiveSupport
. ActiveSupport
permet de déterminer le nom du fichier à
charger en incluant le nom du module. Si l’on appelle autoload :Concern
depuis
le module ActiveSupport
, “active_support/concern” est déterminé. Cette chaîne
de caractère est ensuite envoyée à la fonction
autoload de
Ruby. De cette façon, les modules ne sont chargés qu’au moment où ils sont
appelés. C’est uniquement lorsque l’on utilisera ActiveSupport::Concern
dont
le fichier sera chargé. Il s’agit d’un procédé permettant d’économiser la
mémoire puisque l’on ne charge pas ce qui est inutile.
On continuant la lecture du code, on peut s’apercevoir que certain des modules
sont chargé dans un block eager_load
. Les chargements automatiques sont mis
dans un tableau. Les modules contenus dans ce tableau seront chargés quand la
fonction ActiveSupport.eager_load!
sera appelée.
Certains des éléments sont chargés dans block autoload_under
. Il s’agit,
simplement, d’un block ajoutant un dossier au chemin chargé.
Dans le bas du fichier, la fonction eager_load!
est définie. Celle-ci permet
de forcer le chargement de sous-modules. Cette fonction à été introduit dans
ce commit.
D’après ce que je comprends, ce code peut-être utile quand on fait du
multithreading ou pour être “CoW friendly”. J’avoue ne pas savoir ce que veut
dire “CoW”. Si quelqu’un peut m’éclairer, ça me plairait!
La dernière partie consiste en deux “hook”.
1 2 3 4 5 6 7 |
|
Ces appels vont activer des événements stockés dans des tableaux puis stock à nouveau pour une utilisation ultérieure.
Tous les développeurs utilisant Ruby on Rails ont probablement fait un modèle
héritant de ActiveRecord::Base
. Quand on regarde le fichier
base.rb,
on s’aperçoit qu’il s’agit uniquement d’un fichier en incluant d’autres et
faisant des include
et des extend
d’autres modules. Deux lignes sont
intéressantes, ActiveSupport.run_load_hooks(:active_record, Base)
et include
Core
.
L’avant-dernière ligne exécute les événements stockés précédemment.
ActiveRecord
est donc indiqué comme étant le moteur par défaut de Arel
.
Voici donc le coeur d’ActiveRecord. Je vais tenter de poursuivre ma lecture du code et j’exposerai mes découvertes dans de prochains articles.
]]>belongs_to
afin de comprendre le mécanisme complexe.
Dans cet article, je vais utiliser Pry pour explorer. Je vous conseille de suivre l’exécution du code tout en lisant l’article. Cela vous permettra d’avoir accès aux détails à votre guise. J’utilise la version la plus récente lors de l’écriture de cette article, le 15 février 2015. La version de Rails à la date de votre lecture peut être différente. Vous devez spécifier le commit “6a7ac40dab” si vous faite un clone de Rails et ce lien pour explorer sur Github.
Comme point de départ, je vous propose de reprendre le code servant à créer des issues dans ActiveRecord légèrement modifié.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
Au lancement du programme, Pry stop l’exécution. Nous pouvons ainsi entrer dans
le code d’ActiveRecord par le module ActiveRecord::Associations
. Plus de 1000 lignes de
commentaires sont disponibles pour expliquer les utilisations possibles de la
méthode.
1 2 3 4 5 6 |
|
Cette méthode de seulement deux lignes peut paraître simple mais la complexité
est cachée dans la méthode build
, de classe
ActiveRecord::Associations::Builder::HasMany
. Tout les arguments lui sont
transmis avec, en plus, self
, correspondant à la classe du modèle, Post
dans
notre cas.
La classe ActiveRecord::Associations::Builder::HasMany
fournit des
informations basiques sur l’association, c’est-à-dire son nom et les options
disponibles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Les informations fournies sont utiles pour les classes parentes. En effet, la
classe hérite de
ActiveRecord::Associations::Builder::CollectionAssociation::CollectionAssociation
,
qui hérite de ActiveRecord::Associations::Builder::Association
.
Le coeur du traitement se trouve dans la méthode build
de la classe
ActiveRecord::Associations::Builder::Association
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Pour la suite des explications, je vais passer en revue la méthode build
ligne
par ligne. Commençons par la première.
La première fonction appelée, model.dangerous_attribute_method?
vérifie si le
nom de l’association n’est pas une fonction existante dans ActiveRecord, par
exemple, il n’est possible de créer une association comme belongs_to :save
.
Le nom de l’association ne peut également pas être similaire à une méthode
relative aux IDs, belongs_to :id
par exemple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Comme on peut le voir, build
accepte un block qui est transmis à
define_extensions
. Le block est passé depuis la définition de l’association
comme celui-ci :
1 2 3 4 5 |
|
Il sera donc possible d’utiliser l’association comme ainsi :
post.comments.recents
. Pour plus de détails dans la
doc officiel.
Les extensions sont définies comme suit :
1 2 3 4 5 6 7 8 9 |
|
La fonction créer un module avec le block qui est fourni. Comme
son nom l’indique, extension_module_name
contient le nom du module. Celui-ci
est composé du nom du modèle et de celui de l’association. Dans l’exemple, nous
obtenons “PostCommentsAssociationExtension”. Le nouveau module créé grâce à
Module.new(&Proc.new)
. Celui-ci est ensuite ajouté au code de l’application
afin d’être inclus par la suite. Ce module a pour but de recevoir les méthodes
qui vont être créées par la suite. Je trouve que c’est un moyen très astucieux
pour isoler ces méthodes.
Nous pouvons donc passer à la suite.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Je pense que la méthode create_reflection
est la plus compliquée. Celle-ci
a pour but de créer un objet qui va contenir les informations relatives à l’association.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
La première ligne renvoi une erreur si l’on essai de créer une collection avec
autre chose qu’un Symbol
.
Par la suite, on remplace les options par le scope
si c’est un Hash
. Cette partie semble être uniquement un hack. Si scope
est
un Hash
, c’est qu’il s’agit des options. Lors de la création de
l’association, il n’est pas spécifié quel argument représente le scope ou les
options. Il est donc nécessaire de faire ce petit tour de passe-passe pour
les distinguer.
validate_options
permet de s’assurer que les options passées à l’association
sont valides. On ne peut donc pas faire une association comme has_many
:comments, something_invalid: :test
. Une erreur sera retournée.
Comme son nom l’indique, build_scope construit le scope.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Par défaut, le scope est gradé tel quel. S’il y a un scope et que celui-ci ne possède par d’argument, il est transformé pour être exécuté par l’instance plutôt que la classe.
S’il y a une extension, celle-ci est “wrapper” comme ceci :
1 2 3 4 5 6 7 8 9 |
|
Grâce à cette méthode, le module créé précédemment est inclus lors de l’appel de la méthode. Cette partie est plutôt complexe et demanderait un article au complet. Je vais donc laisser cette partie pour le moment.
Après cet interlude, revenons à l’exécution de create_reflection
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
La macro (:has_many
), celui de l’association (:comments
), le scope, les
options et le modèle sont passés à ActiveRecord::Reflection#create
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Premièrement, on trouve la classe correspondant à la macro, HasManyReflection
dans notre cas. Par la suite, cette classe est instanciée, avec le nom de
l’association, le scope, les options et le modèle. Les informations seront
passées à l’objet créé. HasManyReflection
est un objet représentant
l’association. Les informations y sont stockées afin d’y être utilisées dans votre
code ou dans des gems. Pour accéder à la liste des reflections du modèle, il
est possible de faire Post.reflect_on_all_associations
. Vous obtiendrez ce
résultat :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
On peut comprendre aisément l’utilité d’une telle liste.
À la fin de l’exécution, nous nous retrouvons dans la méthode build
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Voici une fonction des plus intéressante. Grâce à la magie de la métaprogrammation avec Ruby, cette fonction va définir les accesseurs.
1 2 3 4 5 6 7 8 |
|
Comme nous pouvons le voir, le modèle est passé en argument ainsi que
reflection
contenant toutes les informations sur l’association.
Voici la première fonction appelée.
1 2 3 4 5 6 7 8 9 |
|
Comme on peut le voir, un nouveau module GeneratedAssociationMethods
est créé
puis inclus. Ce nouveau module est ensuite retourné pour une utilisation
ultérieure.
Si l’on continue l’exécution de define_accessors
, on trouve l’appel de
define_readers
et define_writers
. Ces deux méthodes reçoivent, comme
argument, le nouveau module ainsi que le nom de l’association. Voici la
première :
1 2 3 4 5 6 7 8 9 10 11 |
|
Et voici la méthode de la classe parente :
1 2 3 4 5 6 7 8 9 |
|
J’aime beaucoup ces méthodes. Elles définissent des fonctions dans le module créé plus tôt. Cela revient à écrire :
1 2 3 4 5 |
|
Comme le module a été inclus à la classe du modèle, la fonction est disponible
pour celui-ci. Il est donc possible de faire un appel comme post.comments
.
Cela revient à écrire post.association(:comments).reader
.
Vous avez probablement remaqué ceci <<-CODE, __FILE__, __LINE__ + 1
. Ce code
permet de spécifier une ligne et un fichier au code généré. Il est donc
possible def faire ceci :
1 2 |
|
La méthode define_writers
suit exactement le même principe.
Nous revoici donc à la méthode build
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Les méthodes define_callbacks
et define_validations
ont des noms qui parlent
d’eux même. Chacune de ces méthodes mérite un article à elle même. Je ne
m’éterniserais donc pas sur le sujet.
Nous pouvons donc revenir à la méthode supérieure.
1 2 3 4 5 6 |
|
La prochaine méthode est simplement un ajout de reflection
à la liste connu des reflections.
Nous avons maintenant compris comment ActiveRecord construisait l’association,
mais pas la méthode utilisée pour rechercher les enregistrements. Pour ce
faire, je vais explorer post.comments
.
Comme prévu, la première méthode est la suivante :
1 2 3 |
|
Il s’agit de celle qui a été créée ci-haut. L’association est ensuite cherchée
dans le cache et la méthode reader
lui est appliquée.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Si le rechargement n’est pas forcé ou si le modèle n’est pas “vicié”, le modèle
n’est pas rechargé. Je me suis cassé les dents sur la méthode stale_target?
.
Je vous invite à lire cette issue
pour plus de détails. Le proxy est également mémorisé. La classe
CollectionProxy
hérite de ActiveRecord::Relation
qui est chargée de la
relation avec la base de données. L’interface se fait grâce à Arel, mais je
pense réserver les explications pour un prochain article.
Si l’on fait post.comments.class.name
on peut voir que c’est bien la classe
ActiveRecord::Associations::CollectionProxy
qui est utilisée.
Explorer le code de Rails est un exercice assez difficile, mais toujours très intéressant. Cette partie est probablement l’une des plus complexes de Rails. L’exploration permet de comprendre Rails, de mieux l’utiliser de s’en inspirer si l’on souhaite recréer un code similaire.
]]>Tout d’abord, voici un exemple de classe à laquelle ActiveModel::Dirty est inclus :
1 2 3 4 5 6 7 8 9 |
|
Rien de bien particulier ici, mis à part l’appel de la fonction
define_attribute_methods
. Cette méthode doit être appelée à chaque fois que des
préfix, suffixe ou affixe
sont utilisés avec, en argument, les attributs correspondants. En effet, comme
nous allons le voir, lors de l’inclusion de ActiveModel::Dirty
, plusieurs
préfixe et suffixe sont créés.
Comme indiqué ci-haut, ActiveModel::Dirty
permet de vérifier si des valeurs d’un objet ont changés.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Dans cet exemple, un nouvel objet est instancié et la valeur de name
est
renseignée. Initialement, aucun changement n’est indiqué. Il est nécessaire de
spécifier que l’attribut va changer en utilisant la méthode
name_will_change!
. Suite à cet appel, nous pouvons demander si l’objet à
subit un changement grâce à la méthode changed?
. name_changed?
permet
d’afficher l’information spécifique à l’attribut name
. Il est également
possible de vérifier l’état précédent et suivant des valeurs en ajoutant les
arguments from
et to
à name_changed?
. Enfin, il est possible de voir
quelles valeurs on changés grâce à changed
, quels ont été les changements grâce
à changes
et quel était la dernière valeur grâce à name_was
.
Il est possible de vider la liste des changements grâce à la fonction changes_applied
.
1 2 3 |
|
La fonction change_applied
est une méthode privée. Il est donc nécessaire
d’utiliser send
pour effectuer l’appel. Comme on peut le voir dans
l’exemple, changes
renvoi maintenant un Hash vide. Cependant, les
changements sont encore conservés, comme on peut le voir lors de l’appel de
previous_changes
.
Afin de vider complètement l’historique des changements, il est possible
d’appeler la fonction clear_changes_information
.
1 2 3 |
|
Comme on peut s’y attendre, le module offre la possibilité de revenir en
arrière, et ceux grâce à la fonction restore_attributes
1 2 3 4 5 |
|
Le code du module
est assez simple. Lors de l’appel de name_will_change!
, la fonction est exécutée
attribute_will_change
. Cette fonction enregistre la valeur
courante dans le hash changed_attributes
. Cet appel doit être effectué avant
le changement de valeur. Dans le cas contraire, l’information serait perdue par
la suite.
Les autres fonctions sont uniquement des traitements faits sur le hash, comme la fonction #changed?.
1 2 3 |
|
Et voilà! Tout en restant simple, ce module peut être utile dans certains cas. Il est notamment utilisé par ActiveRecord pour conserver toutes les modifications faites aux modèles. Nous étudirons le comportement d’ActiveRecord dans un prochain article.
Comme souvent, en prenant quelques minutes pour lire le code source, on se rend compte que le fonctionnement est simple. À nouveau, la magie de Rails est démystifiée.
]]>Dans cet article, j’utilise la version 4.2 d’ActiveRecord.
Avant de commencer, il est important de comprendre comment explorer avec Pry. Je vous invite à lire sur le sujet car ce sont ces techniques que je vais utiliser ici.
Comme code de départ, je vais prendre celui de mon article sur ActiveRecord sans Rails légèrement modifié :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
À l’exécution du programme, nous avons la liste des noms de colonnes avec les types associés. ActiveRecord à donc trouver la structure de la table.
1
|
|
Grâce à Pry, il est possible d’entrer dans la fonction User#columns
. Voici
cette méthode :
1 2 3 4 5 6 7 |
|
Explorons donc certaines variables ou méthodes qui sont utilisées.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Plusieurs éléments sont intéressants ici. Premièrement, les colonnes ne sont pas
encore connues par la classe étant donné que la variable @columns
est nulle.
table_name
est une méthode retournant la variable @table_name
qui contient
déjà le nom de la table, “users” dans ce cas. connection.schema_cache
semble contenir diverses
informations sur la base de données.
Avant d’exécuter connection.schema_cache.columns(table_name)
, schema_cache
ne contient pas les informations sur la structure de la table “users” tandis
qu’elles sont présentes par la suite.
1 2 3 4 5 6 7 |
|
Les informations sont donc stockées dans un cache et ne sont cherchées qu’une fois si le cache n’est pas vidé.
Pour aller plus loin, il est nécessaire de comprendre comment fonctionne la méthode de recherche des colonnes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
Nous voilà à une fonction des plus intéressante. ActiveRecord execute la requête PRAGMA table_info("users")
.
1 2 3 4 5 6 |
|
Avec SQLite, cette requête renvoie les informations sur la table et c’est à partir de ces informations qu’ActiveRecord reconstitut les données.
Cette requête fonctionne pour SQLite mais il en existe une similaire pour Postgres.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Il en est de même pour MySql.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Je pense que cette brève inspection nous suffit pour comprendre ce qu’il se passe sous le capot. Le fonctionnement est assez simple et serait facile à reproduire pour d’autres cas d’utilisation. Pry nous a permis, en quelques minutes, de comprendre le code et de passer au travers l’apparente magie de Rails.
Si vous voulez avoir d’autres explications sur le fonctionnement de Rails, dites-le-moi et je ferais un processus similaire pour essayer de comprendre.
]]>Pry offre énormément de possibilités, comme éditer le code ou voir la
documentation de fonctions. Pour afficher la liste des commandes disponibles,
vous pouvez exécuter la fonction help
dans Pry et il est possible d’exécuter
chaque commande avec l’argument --help
pour plus de détails.
Cet article n’a pas pour but d’expliquer le fonctionnement interne de Pry ou d’explorer toutes les commandes, mais se concentre uniquement sur la navigation.
Voici un premier code d’exemple permettant de jouer avec Pry :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
À l’exécution du programme, Pry s’arrête à la ligne suivant require 'pry'; binding.pry
.
1 2 3 4 |
|
La commande ls
, très similaire à celle de Linux pour explorer les fichiers et
dossiers, nous offre la possibilité d’explorer les objets que contexte dans
lequel nous nous trouvons.
1 2 3 4 |
|
Il est possible de lister uniquement les méthodes avec l’argument -m.
1 2 |
|
L’argument -i
permet de lister les variables d’instances.
1 2 |
|
-G
, correspondant à la fonction grep, permet de rechercher dans les noms. ls
-G d
permet de chercher toutes les méthodes ou les variables contenant un “d”
comme ici :
1 2 3 4 |
|
Il est possible de lister les méthodes d’une variable en la spécifiant à la
commande. Tous ces éléments peuvent être combinés. Il est donc possible de
regarder toutes les méthodes contenant “cap” dans la variable @name
.
1 2 |
|
La commande cd permet de changer le contexte afin d’explorer une variable.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Grâce à pry-byebug, il est possible d’exécuter pas à pas un programme. Avec quelques commandes, je vais tenter de comprendre comment ActiveRecord communique avec la base de données pour lister des enregistrements. Voici un exemple de code, tiré de mon dernier article sur ActiveRecord, qui donne un point de départ à l’inspection :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
À l’exécution du programme, nous avons cet affichage :
1 2 3 4 5 6 7 8 |
|
À partir de là, il est possible de continuer l’exécution en entrant dans la méthode User#all
en utilisant la commande step
.
1 2 3 4 5 6 7 8 9 10 |
|
La méthode est maintenant affichée. Pour continuer l’exécution en restant dans
la même méthode, il est possible d’utiliser la commande next
.
1 2 3 4 5 6 7 8 9 10 |
|
Afin d’aller plus en profondeur et voir le fonctionnement interne d’ActiveRecord, j’ai tout simplement exécuté plusieurs fois la commande step
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
La navigation avec Pry nous a donc permis de comprendre qu’ActiveRecord utilise
Arel. Pour reprendre l’exécution normale du programme, il est possible
d’utiliser la commande continue
.
La commande finish
permet de continuer l’exécution jusqu’a ce qu’il y ait un changement
de fenêtre. Malheureusement, je la trouve très peu utile et elle me perd dans le
code plutôt qu’elle ne m’aide.
Toutes les commandes de navigations possèdent des alias. Il est possible de uniquement le premier caractère pour exécuter la commande et utiliser s
, n
, f
et c
.
L’affichage peut rapidement devenir chargé. Pour revoir la position actuelle du curseur, il est possible d’exécuter la commande whereami
.
Afin de répéter la commande précédemment utilisée en appuyant sur la touche “Entrée”, il est possible d’ajouter, dans le fichier ~/.pryrc
, ce code :
1 2 3 |
|
Cet ajout va permettre de rendre beaucoup plus agréable la navigation.
Pry nous offre la possibilité d’ajouter des points d’arrêts. Il est possible d’arrêter l’exécution en spécifiant un fichier et une ligne.
1
|
|
En exécutant la fonction continue
, le programme s’arrête donc à la ligne spécifiée.
1 2 3 4 5 |
|
Dans ce cas, il se peut que l’on souhaite voir le comportement de la méthode
merge
qui est appelée à la variable retournée par la méthode relation
.
Premièrement, il est nécessaire de savoir de quels classe ou module fait la
méthode merge
.
1 2 |
|
À partir de là, il est possible de placer un point d’arrêt directement sur cette méthode.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
À la prochaine exécution, pry s’arrêtera au début de l’exécution de cette méthode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Il est possible de lister les points d’arrêts définis grâce à la commande breaks
.
1 2 3 4 5 6 |
|
Il est également possible de désactiver temporairement un point d’arrêt.
1 2 3 4 5 6 |
|
On peut les désactiver tous.
1 2 3 4 5 6 |
|
De la même manière, il est possible de supprimer un ou plusieurs points d’arrêts.
1 2 3 |
|
Il est existe d’autres possibilités fournies par Pry à ce niveau, comme l’arrêt suivant des conditions. Je vous invite à consulter la documentation pour en savoir plus.
Pry est un outil essentiel dans le développement avec Ruby et devient plus puissant que les outils fournis par des IDE quand on sait l’utiliser de manière efficace. Dans les prochains articles, je vais utiliser ces techniques pour comprendre le fonctionnement de Rails.
]]>Cet article est piloté par l’erreur. Je vais commencer par utiliser ActiveRecord comme je l’aurais fait avec Rails. Je vais ensuite implémenter le code en fonction des erreurs obtenues.
Sans plus attendre, voici une classe permettant de créer le modèle, insérer un enregistrement et de les lister :
1 2 3 4 5 |
|
Évidemment, Ruby nous envoi l’exception uninitialized constant ActiveRecord (NameError)
. Pour corriger l’erreur, il suffit d’inclure le fichier active_record
.
1 2 3 4 5 6 7 |
|
L’exception suivante inscrit le message No connection pool for User (ActiveRecord::ConnectionNotEstablished)
. Il est nécessaire d’établir une nouvelle base de données.
1 2 3 4 5 6 7 8 9 |
|
Cette fois, l’erreur provient de SQLite et indique que la table users
n’existe pas. Comme avec Rails, il est possible de faire une migration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Ça marche! Et non, ça ne marche pas tout à fait. La première exécution retourne
bien l’enregistrement, mais la seconde retourne une erreur provenant de SQLite
indiquant que la table existe déjà. Il est nécessaire de créer la table
uniquement si elle n’existe pas encore. Il est possible de le faire en
utilisant la fonction #table_exists?
de la classe User
.
Voici donc le résultat final :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Toutes les informations contenues dans cet article peuvent être trouvées sur le Github de rails. Comme dirait Avdi Grimm, Happy Hacking!
]]>Unity 3d est un outil simple pour créer des environnements 3d, même sans avoir de formation en modélisation. Oculus fournit un package nous permettant de nous immerger dans un monde virtuel créé avec Unity. En moins de 10 minutes, il est possible d’intégrer Oculus dans un tel univers et d’y inclure un objet réel. J’ai choisi de présenter la marche à suivre de manière détaillée et d’essayer de faire en sorte que l’article soit le plus compréhensible possible.
Il existe une version d’Unity 3d pour Mac OS X et Windows. Malheureusement, il n’existe pas de version pour Linux. Pour ma part, j’ai dû quitter mon OS préféré et aller sur Windows. Il est possible de télécharger l’application ici. Le package d’intégration d’Oculus est disponible ici. Faites attention à bien télécharger le fichier “Unity 4 Integration”. L’installation se fait comme n’importe quel autre logiciel.
La marche à suivre pour cette étape et la suivante est disponible sur Youtube, en anglais.
Il est possible de créer un environnement à partir de zéro. À des fins de test, on peut également télécharger un modèle existant grâce à l’Asset Store d’Unity en quelques étapes faciles. Pour cela, il faut lancer l’Asset Store.
Il existe des modèles gratuits comme “Bootcamp” que l’on peut trouver en cliquant sur “Complete Projects” et cliquer sur “Bootcamp”.
Une fois la page descriptive ouverte, il est possible de télécharger le projet et de l’importer.
Après un moment, vous pouvez voir que la structure du projet à été créée.
Vous pouvez alors cliquer sur “Bootcamp” pour le lancer. Et voilà, la scène virtuelle est prête. Vous pouvez lancer le jeu en cliquant sur le bouton play dans le menu du haut.
Pour ajouter le module Oculus au monde créé précédemment, il suffit de décompresser le package téléchargé sur le site d’Oculus puis faire un Drag & Drop du fichier “OculusUnityIntegration.Unitypackage”.
Cet écran vous sera affiché :
Il suffit de cliquer sur “import” et d’attendre quelques secondes.
Le répertoire OVR sera alors créé.
Naviguez dans “OVR/Prefabs”, vous verrez alors le fichier “OVRPlayerController”. Un simple Drag & Drop du navigateur vers la scène suffit pour l’ajouter au projet. Vous pouvez positionner la caméra comme bon vous semble. La position choisie correspondra à votre position de départ lors du lancement du jeu.
Pour plus d’aisance, vous pouvez supprimer le soldat mis en place dans Bootcamp en faisant un clic droit sur “Soldier_Locomation” et choisir “Delete”.
Vous pouvez d’ores et déjà lancer le jeu et utiliser Oculus!
L’étape d’intégration d’un modèle 3d se fait de manière tout aussi simple. J’ai choisi d’importer le modèle créé dans l’article précédent. Il s’agit de la table basse de mon salon reconstituée grâce à des photos. Je suis allé télécharger le modèle en format .obj sur 123D catch. Pour l’intégrer, il faut faire un clic droit sur l’explorateur d’assets et cliquer sur “Show in Explorer”.
Vous pouvez ensuite placer les fichiers dans le répertoire. Unity détectera les nouveaux fichiers. Vous pouvez ensuite trouver votre fichier obj dans l’explorateur et faire à nouveau un Drag & Drop ce qui ajoutera le modèle 3d dans l’environnement. Vous pouvez ensuite redimensionner l’objet pour lui donner l’aspect souhaité. Voici donc ma table basse dans l’environnement :
Finalement, vous pouvez générer l’application pour la partager.
Quand j’ai acheté mes lunettes Oculus, je ne pensais pas être capable de faire de telles créations. Heureusement, Unity nous simplifie grandement la vie et nous donne un résultat impressionnant en quelques étapes simples.
]]>À la base, mon objectif était de faire la cartographie 3d avec mon drone en suivant les étapes indiquées ici. La cartographie 3D utilise la technique de reconstruction 3d à partir d’images (image based 3d reconstruction). C’est cette technique que je vous propose d’approfondir dans cet article.
Il existe plusieurs types de scanneurs 3d. Ceux-ci produisent généralement des modèles de meilleure qualité que ceux obtenus en utilisant des images. Ceci est dû au fait qu’ils utilisent différents types de données comme des lasers ou des sonars en plus des images. La raison pour laquelle j’ai tout de même préféré utiliser la reconstitution 3d à partir d’image est tout simplement le prix. En effet, un scanneur 3d coûte entre 300$ et 100000$ ou plus. Bien qu’il soit possible d’en avoir à prix réduit si l’on est prêt à faire un peu de bricolage, j’ai choisi de rester dans la simplicité pour le moment.
Le principe est à la fois simple et compliqué. Un algorithme complexe va trouver des points similaires entre les différentes images et ainsi pouvoir les replacer dans un environnement. Par triangulation, l’algorithme est capable de reconstituer un nuage de point représentant l’objet. Il va ensuite reconstituer la surface puis appliquer une texture avec les images.
Il existe plusieurs logiciels de ce type et j’en ai essayé quelques-uns :
Selon mon expérience, j’ai eu les meilleurs résultats avec 123d catch. De plus, tous les calculs se font sur leurs serveurs, il n’y a donc pas besoin de bloquer un PC pendant une heure, car le taux d’utilisation du CPU est élevé, contrairement à VisualSFM ou Photoscan. Enfin, Autodesk possède une gamme d’outils très complète dans le domaine. 123d catch est un outil à la fois simple et puissant.
Toutefois, je n’exclus pas encore Trnio. Jan-Michael Tressler, le PDG de Trnio, m’assure que le rendu va être amélioré prochainement. Je fais d’ailleur parti des beta-testeurs de l’application.
Pour ce qui est de VisualSFM, je pense que le fait d’avoir une carte graphique NVidia Cuda peut aider comme indiquer sur leur site.
J’ai choisi de télécharger l’application iPhone de 123d catch étant donné que cela évite les problèmes qu’il peut y avoir avec les différences entres les appareils photo. Si Autodesk a fait fonctionner leur application avec un iPhone, ça devrait fonctionner avec le mien.
Il faut ensuite placer un objet et en faire le tour. L’application va vous dire comment vous placer pour prendre vos photos. L’application va ensuite vous permettre d’uploader vos images puis va passer par plusieurs phases avant d’avoir un modèle. Environ une demi-heure plus tard, vous aurez une notification indiquant que le traitement et fini. Vous pouvez ensuite visualiser et publier votre modèle.
Il est très important de faire attention à l’éclairage. Le choix du type d’objet est également important. Il est nécessaire que l’objet ait des détails sur lesquels le logiciel peut se fier pour faire son traitement. Il faut éviter les objets avec trop de trous et de creux.
Voici un exemple de modélisation:
123d propose de télécharger le modèle sous 3 formats, obj, 3dp ou stl. À vous de voir selon vos besoins.
Et voilà! Nous avons maintenant un modèle 3d d’un objet réel. Dans le prochain article, je vous présenterais une méthode simple pour intégrer le modèle 3d dans une scène créée avec Unity afin d’utiliser Oculus Rift.
]]>L’Open/Closed Principle est le O des principes SOLID. Il a été introduit par Bertrand Meyer (un français). Si l’on suit ce principe, un élément dans un programme doit être “Ouvert pour l’extension, mais fermé pour la modification”. On doit donc pouvoir ajouter des fonctionnalités à une classe, un module, une fonction, etc. sans pour autant la modifier.
Cet article est piloté par l’exemple, beaucoup de code et peu de blabla. Il est écrit en Ruby, mais est utilisable dans n’importe quel autre langage.
L’article est le deuxième d’une série concernant les principes SOLID. Le premier porte sur SRP. Dans cet article, vous trouverez des références au SRP. Si ce n’est pas déjà fait, je vous conseille de lire le premier article avant celui-ci.
Sans plus attendre, voici un premier exemple de code ne respectant pas OCP :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Comme on peut le voir, la classe TwitterNotifier
est instanciée dans la
méthode #call
de UserSubscription
. Si l’on souhaite ne plus envoyer de
notifications via Twitter, mais via Facebook, il va falloir rouvrir la classe
UserSubscription
et remplacer la classe appelée.
Voici un exemple faisant la même chose, mais suivant le principe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
L’appel de TwitterNotifier.new
se fait à un niveau supérieur et il est
possible de changer le comportement de UserSubscription
sans changer son
implémentation.
Si un changement au programme est demandé pour que la notification soit différente selon le type d’utilisateur, sans suivre OCP, nous pouvons écrire ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
À chaque nouveau type d’utilisateur, il va falloir rouvrir la classe
UserSubscription
. Il est plus facile de modifier la classe comme ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
La responsabilité du choix du message à afficher a été reportée à la classe
de l’objet @user
. En plus du fait de rendre cette fonction plus adaptable, cette
modification suit le Single Responsability Principle. Ce n’est pas à la classe
s’occupant du processus d’inscription de savoir quel message affiché à
l’utilisateur.
Plutôt que de faire une seule notification, nous allons modifier le code pour en envoyer plusieurs. Encore une fois, voici le code qui ne suit pas OCP :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Dans ce cas, à chaque fois que l’on souhaite ajouter un type de notification,
il faut rouvrir la classe UserSubscription
. Voici la version corrigée :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Dans cette version, un nombre indéfini de méthodes est appelé après la création
de l’utilisateur. Il est facile d’ajouter un comportement en modifiant la
classe de plus haut niveau. De plus, la méthode appelée est toujours la même,
call
. Cela permet d’ajouter n’importe quel type de traitement après la
création, pas seulement les notifications.
Dans le cas où nous souhaitons effectuer des traitements particuliers si une notification échoue, il est possible d’extraire les observateurs dans une classe indépendante comme ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Cet exemple est un peu plus complexe, mais permet une meilleure gestion de la file.
Plusieurs avantages sont à noter. Le premier, et le plus évident, est la rapidité de réaction au changement. Il va être plus facile d’ajouter des comportements lors de l’exécution d’une fonction.
Comme souvent, l’utilisation des bonnes pratiques simplifie les tests. Dans un test, il est possible de passer un tableau vide comme observers.
Les principes SOLID se renforcent mutuellement. Une bonne utilisation d’OCP est une technique qui encourage l’application du SRP. Utiliser l’ensemble de ces techniques permet de faire un code plus clair et plus résistant aux changements.
Malheurseument, il existe au moins un inconvénient à OCP. Les comportements sont toujours remis au niveau supérieur. Ceci rend complexe le plus au niveau, principalement le controller. Il est possible d’ajouter des fichiers de configuration, mais l’implémentation est plus compliquée.
Comme toujours dans l’utilisation de Design Patterns, il n’y a pas de règle exacte pour l’application d’OCP. Le choix se fera selon les cas d’utilisation et le bon sens. Il y a, cependant, quelques indications que l’on peut suivre :
J’espère que cet article vous sera utile, et que vous avez appris un nouveau principe ou que vous avez eu un bon rafraîchissement. J’écrirais, bientôt, des articles sur les autres principes SOLID.
Si vous avez des commentaires sur le fond ou la forme de l’article, n’hésitez pas à m’en faire part.
]]>Le Single Responsability Principle (SRP) est le S des principes SOLID. Il a été introduit par Robert C. Martin, alias Uncle Bob. D’après celui-ci, une classe doit avoir une, et une seule, responsabilité.
Le langage utilisé dans cet article est le Ruby, mais le principe est applicable dans tous les langages orientés objet.
Commençons par un exemple ayant plus d’une responsabilité.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Si l’on se demande quels sont les buts de cette classe, on comprend que celle-ci s’occupe de la persistance des données, de la génération des tokens, de l’envoi d’email après la création à l’administrateur et à l’utilisateur. Chacun de ces buts est une responsabilité.
Cet exemple est très simple, mais est représentatif d’une classe qui grossit et gagne en complexité rapidement. Il en va souvent de même pour les modèles, le M du principe MVC. Ceux-ci sont des aspirateurs à responsabilité. Ceci est dû au principe “Fat model, skinny controller” prôné par Ruby on Rails entre autres.
Pourquoi changer cette classe? Il existe plusieurs bénéfices résultant de l’utilisation du Single Responsability Principle.
Premièrement, le fait de segmenter les fonctions en plusieurs classes permet de clarifier le code. Les classes vont être moins longues et donc, plus lisibles. Si l’on suit les principes de Sandi Metz, une classe ne devrait pas dépasser cent lignes. Plus c’est court, plus c’est facile à comprendre.
Il sera plus facile d’instancier et d’utiliser ces classes. Une classe avec une seule responsabilité aura besoin de moins d’arguments dans son constructeur. Avec des classes simples, il devient facile de les instancier à partir de n’importe quelle autre classe ou depuis la console.
Étant donné que les classes sont plus courtes, possèdent moins de fonctions et sont plus faciles à instancier, elles sont plus faciles à tester. Un élément difficile à tester est un symptôme, mais pas le problème. Utiliser le SRP permet d’avoir des tests plus efficace, plus courts et plus compréhensibles.
Les classes suivant ce principe sont également plus faciles à changer. Les classes sont isolées les unes des autres, ce qui signifie qu’il en résulte moins de “code spaghetti”. Le changement d’une responsabilité est moins susceptible d’en impacter une autre.
Enfin, une classe avec une seule responsabilité favorise l’utilisation du Duck
Typing étant donné qu’il peut y avoir une seule méthode. Il est possible de
nommer cette méthode de la même façon, call
par exemple. Ce qui fait qu’il
sera toujours possible d’appeler la méthode ainsi object.call
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
Le premier modèle a été décomposé en plusieurs classes plus petites et plus simples.
La principale différence est au niveau de la class User
. Celle-ci est
beaucoup plus simple et n’a qu’un seul but, celui de représenter la donnée.
UserCreator
s’occupe uniquement du processus de création d’un utilisateur et
UserCreationNotifier
s’occupe uniquement de la notification suite à la
création. TokenGenerator
, comment son nom l’indique, génère des tokens. Il
s’agit d’une classe très réutilisable, pouvant servir dans de nombreux cas.
Robert C. Martin décrit une responsabilité comme une raison de changer. Une
classe ou un module doit avoir seulement une raison de changer. Si l’on prend
la première version de la classe User
, celle-ci devra changer si l’on décide
de générer des token différemment, ou encore si l’on change les notifications à
envoyer, ou si l’on change le type de persistance des données. Bref, cette
classe a définitivement plus d’une raison de changer.
Si l’on prend la proposition de refactoring en y pensant en terme de raison de
changement. La classe User
changera uniquement si l’on décide de changer les
données à sauvegarder. La classe UserCreator
changera si l’on modifie le
processus de création d’un utilisateur et UserCreationNotifier
changera en
fonction des notifications que l’on veut effectuer lors de la création d’un
utilisateur. Enfin, TokenGenerator
change si l’on souhaite changer la
manière de générer des tokens.
Il faut bien garder en tête que c’est la responsabilité qui doit avoir une raison de changer et non le code. On peut imaginer dix mille raisons de changer de code dans une classe, par exemple, changer le nom d’une fonction ou d’une variable, mais ces changements sont acceptables. Tandis que, si l’on change la responsabilité de la classe, on change sa raison d’être et il faut que ça soit justifié.
Un des moyens pouvant être utilisés pour déterminer si une classe a plus d’une responsabilité peut être de la personnifier et de lui demande quelles sont ces responsabilités. Un petit exemple de dialogue :
Vous – Bonjour, madame User
, que faites-vous dans la vie?
La classe – Je m’occupe de la persistance des données ET de la notification ET du changement des tokens.
Le mot clé est “ET”. S’il y en a un dans la réponse à la question que vous avez posé à la classe, c’est qu’elle à trop de responsabilités.
Le Single Responsability Principe est un concept très important à comprendre. Ceci dit, il faut l’utiliser avec du bon sens. Il y a une limite au bénéfice du principe et il faut faire attention à ne pas tomber dans l’excès.
Chacun des principes se renforce mutuellement. Si l’on suit celui-ci, il sera plus facile d’en utiliser un autre. Cependant, parfois, ils peuvent être mutuellement exclusifs. Il n’existe pas de règle magique et il faut faire preuve de bon sens. Avec le temps et l’expérience, tout cela devient plus naturel.
]]>Commençons par la base. Le Raspberry Pi est un petit ordinateur possédant un processeur ARM. Dans la même catégorie, il existe Arduino et BeagleBone. Chacun possède ses forces comme indiqué dans cet article. Raspberry utilise une distribution de Linux. La plus utilisé est Raspbian, une version dérivée de Debian pour Raspberry. C’est celle que j’utilise dans cet article.
Dans mon cas, j’ai acheté le Raspberry Pi Ultimate Starter Kit.
Mes notions d’électroniques remontent au collège et avaient besoin d’être dépoussiérées. Le principe est simple, l’électricité est émise par le Raspberry et suit un chemin du positif vers le négatif. Entre ces deux étapes, j’ai placé une LED, pour la lumière, et une résistance, pour s’assurer que le voltage ne dépasse pas une certaine puissance.
Le Raspberry Pi possède différents ports d’entrées et de sorties. Les signaux électriques sont envoyés par des GPIOs. Un GPIO (General Purpose Input/Output) est un port sans but prédéfini. Il est possible de choisir s’il s’agit d’un port d’entrée ou de sortie ainsi que la puissance émise par ce port.
Il est facile de se perdre dans le numéro des GPIO et des ports. Les deux possèdent une numérotation différente et sont souvent utilisées dans les articles. Voici une image indiquant la liste des ports et les GPIOs correspondants :
Le circuit électrique que j’ai fait correspond à celui de cet article.
Voici le schéma suivi et adapté pour utiliser les pièces du kit :
Tout d’abord, pour vérifier que le circuit fonctionne correctement, je l’ai connecté au port du 3.3v comme montré sur cette image :
Le chemin doit terminer par la mise à la terre (ground). Par convention, le positif est représenté par des fils rouges et le négatif est représenté par des fils noirs. Tout fonctionne, je peux passer à la création du programme.
Le premier choix à faire lors du commencement d’un programme est celui du langage. J’ai toujours pensé qu’un projet avec une composante électrique devait avoir un langage comme le C, ou plus bas niveau.
Étant donné que Raspberry Pi fonctionne avec un Linux, il est possible d’installer n’importe quel langage, mais je crois qu’il est bon de rester sur les deux installés de base tout simplement parce que ce sont ceux qu’utilise la communauté Raspberry. Ces deux langages sont le C et le Python.
Après un comparatif de ces langages, j’ai finalement choisi le Python. Bien que le langage en lui-même soit plus lent, il est tellement plus rapide de programmer avec celui-ci qu’il reste une option intéressante. Je crois qu’une bonne pratique est de faire un programme en Python et, si la rapidité devient un point problématique, refaire le même programme en C.
De toute façon, de nombreux autres éléments peuvent influencer de manière plus importante la rapidité, comme la vitesse du réseau par exemple.
Dans cet exemple, nous allons utiliser le GPIO 17. Le choix est totalement arbitraire et n’a aucune importance. Avant de faire un programme permettant de se connecter à GMail, j’ai commencé par faire clignoter la LED. Voici le circuit avec le GPIO changé :
Et voici le programme :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Pour lancer l’application, il suffit de faire sudo python myfile.py
. Pour quitter, il faut appuyer sur CTRL+C.
Comme vous pouvez le constater, j’ai choisi d’utiliser RPIO. Une bibliothèque simple et efficace.
Deux modes permettent d’indiquer le type de numérotation que l’on souhaite.
Si l’on choisit RPIO.BOARD
, il faudra indiquer le numéro du port et pour RPIO.BCM
celui
du GPIO. Les deux fonctionnent aussi bien et le choix est à votre convenance.
RPIO.setmode
permet de choisir un mode ou l’autre.
RPIO.setup
permet de configurer le port pour faire en sorte qu’il s’agisse
d’une entrée ou d’une sortie. Dans ce cas, il s’agit d’une sortie.
RPIO.output
permet d’indiquer le signal à envoyer au port. Il est possible de
lui envoyer un booléen, 0, GPIO.LOW
, 1 ou GPIO.HIGH
.
Finalement, lorsque le programme est interrompu, les GPIOs sont remis dans leurs états initiaux.
Pour connecter le programme avec GMail, j’ai utilisé imaplib. Voici le résultat final :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Et voilà! Quand un nouveau email arrive, la LED s’allume. Si l’email n’est plus marqué comme lu, celle-ci s’éteind.
L’application utilisée comme exemple n’est pas la plus utile du monde, mais permet de voir les concepts de base du développement avec Raspberry Pi.
]]>Le choix d’un système d’exploitation est une première étape obligatoire. Comme beaucoup, j’ai commencé par Windows. Pour le développement, ce n’est vraiment pas l’idéal. Premièrement, dans mon langage de programmation favori, Ruby, beaucoup d’outils sont d’abord faits pour Linux et Mac avant d’être adaptés, ou pas, pour Windows. Ensuite, le terminal est un outil ultra important. L’interface graphique permet d’utiliser des fonctions qui sont en fait des lignes de commandes dans le système. Utiliser l’interface, c’est se limiter a ce qu’on a voulu de nous proposer. Linux et Mac sont vraiment plus orientés pour l’utilisation du terminal.
Linux vs Mac : Il n’y a pas de bonnes ou de mauvaises réponses. Je n’utilise pas Mac, car il ne correspond pas à ma philosophie. Je n’ai pas envie d’être contrôlé par Apple et j’adore l’Open Source. Si vous préférez payer, libre a vous.
Pourquoi Ubuntu? Tout simplement parce que c’est la distribution la plus connue.
Zsh est un remplaçant de Bash. Zsh offre un meilleur affichage et une navigation plus rapide en ne tenant pas compte des majuscules dans les noms de fichiers et de dossiers. Une meilleure intégration avec git est disponible. Si votre répertoire est versionné, vous verrez quelle est la branche courante. Oh-My-Zsh offre des configurations préfaites pour certains outils comme git ou rails.
Il est possible de configurer Zsh. Si l’on n’aime pas le fait d’ignorer la
casse, une seule ligne suffit dans le fichier .zshrc
pour changer le
comportement.
Choisir un éditeur de texte est crucial. Certains préfèrent de gros IDE comme Eclipse. D’autres préfèrent des outils plus simples comme Sublime Text ou Notepad++. J’ai testé beaucoup de solutions avant de choisir. J’ai finalement choisi Vim et j’en suis bien content.
J’encourage tout le monde à essayer Vim. Les premiers pas sont difficiles, mais le jeu en vaut la chandelle. Cela fait plus d’un an que je n’utilise que cet éditeur. Je pense avoir augmenté ma productivité, mais je sais qu’il me reste encore des milliers de trucs à apprendre. Vim, c’est tout un monde.
La philosophie de Vim vise a éviter l’utilisation de la souris. En effet, le temps passé à bouger la main entre la souris le clavier peut paraitre anodin, mais on le fait des centaines de fois et il s’agit d’une totale perte de temps. L’apprentissage de Vim demande de casser les habitudes.
Vim est un éditeur modal. Six modes sont disponibles :
Le fait d’utiliser des modes permet d’avoir plus de possibilités, car chaque mode posséder ces propres raccourcis et fonctionnalités.
Il existe de nombreux plug-ins Vim. Voici une liste de mes préférés :
Vim est hautement personnalisable. L’édition du fichier ~/.vimrc
permet
d’ajouter de la configuration, mais également de faire vos propres bouts de
code. Il est également possible d’ajouter des raccourcis et de configurer les
combinaisons du clavier pour exécuter plus rapidement certaines actions.
Tmux permets d’exploiter plusieurs terminaux au sein d’un seul écran. On peut créer des panneaux, qui vont séparer l’écran en plusieurs parties, chaque partie étant un terminal où il est possible d’exécuter des commandes. Il est possible de regrouper les panneaux en fenêtres. La liste des fenêtres est affichée en bas de l’écran. Il est également possible de créer des sessions qui sont un regroupement de fenêtres. À tout instant, il est possible de naviguer entre les différents éléments à l’aide de raccourcis clavier.
Mon truc préféré avec Tmux est de le faire parler avec Vim. J’ai ajouté le
plug-in tslime, qui permet d’envoyer une commande dans un panneau.
Je l’utilise pour exécuter les tests. Lorsque j’appuie sur les touches ,t
,
Vim lance l’exécution de Rspec, avec le fichier et la ligne courante, et lance
le test dans un panneau séparé. Cela me permet d’exécuter les tests rapidement
tout en restant asynchrone et en me permettant d’afficher et de garder la trace
de l’erreur lorsque je modifie mon code.
J’utilise également Tmuxinator, qui permet d’enregistrer des configurations Tmux
et de démarrer une session avec un certain affichage et exécuter certaines
commandes au démarrage. Je l’ai configuré pour accéder rapidement aux projets
sur lesquels je travaille. La commande tmuxinator c
, me mettra dans le dossier
du projet et créera trois panneaux, le principal avec Vim, le second lancera le
serveur et le dernier est libre pour pouvoir exécuter des commandes. J’ai créé
un raccourci avec Zsh pour simplement taper ti c
.
Utiliser les bons outils ne sert à rien si l’on tape avec deux doigts. J’ai suivi un cours en ligne pour apprendre à taper. Cela fait vraiment une grosse différence. Le fait de taper de manière plus fluide permet non seulement d’aller plus vite, mais de garder plus facilement le fil de ses idées. En suivant des entrainements, même les personnes tapant déjà rapidement peuvent s’améliorer.
Taper vite est utile quand on code, mais pas seulement. C’est également un avantage lors de l’écriture d’un email, d’un document, ou d’un article de blog.
Taper vite avec les bons outils ne sert à rien si l’on tape n’importe quoi. Je suis inscrit à plusieurs sites permettant de m’améliorer dans ma pratique. J’ai également lu des livres le développement. Enfin, écrire des articles de blog sur des articles que je connais, en plus de me permettre de partager ma connaissance, me donne une rigueur qui me permet d’approfondir certaines connaissances.
Le plus important est de ne jamais arrêter de coder. C’est avec la pratique qu’on s’améliore. Il faut également prendre des temps pour regarder de plus près ce que l’on fait et se poser les bonnes questions.
Petit conseil pour les unilingues francophones : N’ayez pas peur de l’anglais. C’est dur au début, mais avec le temps ça va mieux. De plus, quand on lit des livres sur le développement, c’est possible de comprendre le sens en lisant le code.
Les outils que j’utilise sont à mon goût et je suis certain que je vais en découvrir d’autres avec le temps. Je crois que l’important est de tester et de faire ses propres choix.
Vous pouvez voir mes fichiers de configuration sur le repo GitHub.
]]>Cet article est une brève présentation des Design Principles, ou Principes de Conception, et des Design Patterns, ou Patrons de Conception. Le but de cet article n’est pas de détailler chacun de ces concepts, mais de donner un bref aperçu de ceux-ci. D’autres articles suivront pour rentrer plus dans les détails.
Un des principes fondamentaux, en Ruby comme dans d’autres langages, est la Programation Orienté Objet (POO). Il est parfaitement possible d’écrire une application en code en procédural, mais il est vite difficile de la maintenir efficacement. Afin de faire une séparation logique du code, il est possible de le regrouper en classes et en méthodes. Pour faire une bonne séparation, il est important de bien comprendre quels sont les principes existants. De plus, la POO est bien plus que mettre des bouts de cote dans des catégories. Il s’agit d’une manière de penser le code.
Dans le livre Practical Object-Oriented Design in Ruby: An Agile Primer, Sandi Metz définit qu’une application doit être TRUE :
Pour nous aider dans cette démarche, Robert C. Martin a défini cinq Design Principles communément acceptés. Une application doit être SOLID, l’acronyme pour :
Ce qu’il y a d’intéressant avec ces principes, c’est qu’ils se renforcent mutuellement. Si l’on en utilise certains, les autres vont être plus évidents ce qui encouragera une meilleure qualité.
Rails prône également les principes de Convention Over Configuration, qui vise a simplifier le développement en utilisant des conventions de nommage plutôt que des fichiers de configuration, et Don’t Repeat Yourself, qui dit que l’on ne devrait jamais dupliquer du code.
Le plus discutable prôné par Rails est Fat models, skinny controllers. Celui-ci vise à garder le contrôleur le plus simple possible et à ajouter la logique dans le modèle. Pourquoi discutable? Parce que je crois, comme d’autres, que les modèles deviennent trop complexes et difficiles à tester.
Il n’est pas toujours facile de faire l’unanimité sur des techniques. Heureusement, Rails is Omakasee. Le framework est suffisamment souple pour nous laisser le choix des techniques à utiliser. La “Rails Way” est de faire du “Fat models, skinny controllers” mais il est tout à fait possible d’introduire d’autres concepts et de créer de bons vieux purs objets Ruby.
Les Design Patterns sont des types de solutions répondant à certains problèmes communs. En programmation web, je crois que le plus connu est MVC. Celui-ci veut qu’une requête HTTP soit traitée par un routeur qui instancie un contrôleur qui a pour but d’instancier les modèles et de renvoyer une vue.
Il existe bien d’autres Design Patterns. Avec Rails, mes préférés sont :
Lorsque l’on s’attaque à un problème, il est intelligent de se renseigner d’abord sur les solutions qui existent. Une fois appliqué, on apprend à reconnaitre les patterns et l’on applique plus vite les solutions. Le temps passé à faire la recherche initiale est largement rentabilisé avec le temps.
Savoir quand utiliser un Design Pattern, peut paraitre anodin, mais il y a un autre principe à suivre Keep It Simple and Stupid (KISS). Ils doivent être utilisés quand il y a un problème ou en prévision d’un problème. Parfois, il ne sert à rien de se casser la tête. Si le programme est propre et facile a modifier, il ne sert à rien d’utiliser des techniques complexes.
Il est parfois difficile de choisir quel Design Pattern utilisé. D’autant plus que certains peuvent se contredire. Par exemple, il est difficile d’utiliser Tell Don’t Ask dans un contrôleur quand vient le temps de savoir quelle vue retourner. Malheureusement, il n’y a pas de règles simples. Il faut savoir utiliser le bon sens et prendre des décisions.
Je crois que l’impact psychologique des principes et des Design Patterns est non négligeable. C’est beaucoup plus agréable de travailler sur un projet bien structuré que de modifier du code sans savoir quels vont être les répercussions ou devoir modifier une grosse partie du code. Ruby est un langage fait pour rendre le développeur heureux. Chacun sait qu’un développeur moins stressé sera plus productif.
De plus, une bonne qualité de code fera de vous fier de ce que vous faite. C’est super encourageant de rentrer tous les matins au travail en sachant que l’on fait du bon travail.
Ces sujets sont beaucoup trop complexes pour être écrit en un seul article. Ils mériteraient des livres. D’ailleurs, il y en a un paquet. Parmi ceux-ci, on peut citer Clean Code: A Handbook of Agile Software Craftsmanship de Robert C. Martin, Practical Object-Oriented Design in Ruby: An Agile Primer, de Sandi Metz ou Head First Design Patterns de Eric Freeman, Elisabeth Robson, Bert Bates et Kathy Sierra.
Comme indiqué ci-dessus, je vais bientôt écrire plusieurs articles et Design Pattern, so Follow me on Twitter.
]]>Avant d’aller plus loin, j’ai voulu comprendre le concept de dyno. Avant de connaitre Heroku, ce que je connaissais qui s’en rapprochait le plus c’était ça :
Pour Heroku, un dyno n’a rien à voir avec notre ami Denver. Un dyno est un processus qui va répondre à une requête. Plus on a de dyno, plus on va être capable de répondre à plusieurs requêtes en même temps.
Comment dans l’article précédent, j’ai effectué les tests avec cette application. J’ai également utilisé les mêmes outils et les mêmes scénarios qu’auparavant. Encore une fois, il s’agit de la même application pour DigitalOcean et pour Heroku.
Les tests en quelques chiffres : 50 visiteurs simulés utilisant 3 scénarios différents pendant 5 minutes.
Petit rappel sur les chiffres obtenus avec DigitalOcean :
Un temps de réponse plutôt correct.
Avec DigitalOcean, j’utilise un swapfile de 2go et voici ma config Unicorn :
worker_processes 3
timeout 120
preload_app true
Et voilà le moment tant attendu. Voici les mêmes tests faits avec 2 dyno de 512mo :
Quoi?????? Je ne m’attendais vraiment pas à ça. J’ai essayé avec 1 dyno de 1go et 2 dyno de 1go et j’ai toujours sensiblement le même résultat. Le temps offert par Heroku est vraiment désastreux. Comme on peut le voir, le temps est majoritairement passé dans la queue.
Pour en savoir plus, j’ai contacté le support Heroku. Tout d’abord, pour avoir de meilleur log au niveau de la mémoire, ils m’ont conseillé d’utiliser log-runtime-metrics. J’ai fait un test en allant sur 5 pages de mon site et voici le résultat :
2014-04-12T11:27:41.246260+00:00 heroku[web.1]: State changed from starting to up
2014-04-12T11:27:44.647222+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#memory_total=229.98MB sample#memory_rss=229.96MB sample#memory_cache=0.01MB sample#memory_swap=0.00MB sample#memory_pgpgin=64372pages sample#memory_pgpgout=5498pages
2014-04-12T11:27:52.633087+00:00 app[web.1]: Started GET "/fr/pages/accueil" for 208.114.164.25 at 2014-04-12 11:27:52 +0000
2014-04-12T11:27:56.361888+00:00 app[web.1]: Started GET "/fr/pages/accueil" for 208.114.164.25 at 2014-04-12 11:27:56 +0000
2014-04-12T11:28:03.386496+00:00 app[web.1]: Started GET "/fr/pages/acheter-vendre" for 208.114.164.25 at 2014-04-12 11:28:03 +0000
2014-04-12T11:28:04.772151+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#memory_total=423.66MB sample#memory_rss=423.34MB sample#memory_cache=0.33MB sample#memory_swap=0.00MB sample#memory_pgpgin=113959pages sample#memory_pgpgout=5501pages
2014-04-12T11:28:08.154309+00:00 app[web.1]: Started GET "/fr/pages/services-aux-collectionneurs" for 208.114.164.25 at 2014-04-12 11:28:08 +0000
2014-04-12T11:28:13.728267+00:00 app[web.1]: Started GET "/fr/pages/faq" for 208.114.164.25 at 2014-04-12 11:28:13 +0000
2014-04-12T11:28:25.099729+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#load_avg_1m=0.00
2014-04-12T11:28:25.100040+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#memory_total=444.60MB sample#memory_rss=444.18MB sample#memory_cache=0.42MB sample#memory_swap=0.00MB sample#memory_pgpgin=119319pages sample#memory_pgpgout=5501pages
2014-04-12T11:28:44.717755+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#load_avg_1m=0.00
2014-04-12T11:28:44.718017+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#memory_total=445.48MB sample#memory_rss=445.06MB sample#memory_cache=0.42MB sample#memory_swap=0.00MB sample#memory_pgpgin=119550pages sample#memory_pgpgout=5507pages
2014-04-12T11:29:04.898773+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#load_avg_1m=0.00
2014-04-12T11:29:04.899057+00:00 heroku[web.1]: source=web.1 dyno=heroku.16040091.5565eaf7-72e9-4184-b1d8-46a1612f8e49 sample#memory_total=445.48MB sample#memory_rss=445.06MB sample#memory_cache=0.42MB sample#memory_swap=0.00MB sample#memory_pgpgin=119550pages sample#memory_pgpgout=5507pages
Comme on peut le voir, juste après le démarrage, 230mo étaient utilisés. J’ai visité les 5 pages et la mémoire c’est chargée à 444mo. Ensuite, j’ai eu rapidement des Memory quota exceeded. Je n’ai aucun problème similaire avec DigitalOcean.
Le support m’a ensuite indiqué que mon application était gourmande en mémoire. Ils m’ont conseillé d’utiliser la gem oink. Cette gem permet d’indiquer quels sont les actions les plus utilisées. Je n’ai pas appris plus grand-chose de plus qu’avec NewRelic.
Il y a effectivement plusieurs éléments qui peuvent prendre de la mémoire. Tout d’abord, c’est une application Rails. Rails n’est certainement pas ce qu’il y a de plus léger au monde. Ensuite, j’utilise beaucoup de cache. J’imagine qu’il y a beaucoup d’éléments stockés en mémoire dans ces cas-là. Ensuite, il s’agit d’une application d’une bonne taille avec plusieurs requêtes SQL par page maire rien d’extrême.
Comme je l’ai dit plus tôt, j’ai comparé les deux serveurs avec la même application. Si j’avais vraiment un problème de mémoire dans celle-ci, j’imagine que les problèmes auraient dû être les mêmes. Or, je n’ai aucune erreur de mémoire dans Digital Ocean.
Je crois qu’il est assez clair que Heroku perd la bataille. Si quelqu’un à d’autres résultats ou aimerai m’indiquer comment corriger d’éventuels problèmes.
Un autre avantage que je trouve à DigitalOcean est le fait d’avoir une plus grande accessibilité au système. Au début, le fait d’avoir un environnement prêt en quelques secondes avec Heroku est génial, mais lorsque l’on veut aller plus loin, je trouve que l’on rencontre beaucoup de limites. Avec DigitalOcean, nous avons le plein contrôle de l’OS. De plus, si l’on suit un bon guide, déployer une application n’est pas bien compliqué.
Pour finir, la chanson que j’ai dans la tête depuis le début de l’écriture de l’article :
]]>L’application utilisée dans ce cas est une application Ruby on Rails standard utilisant Rails 3.2.17, Ruby 2.1.0 et Unicorn 4.8.2.
Pour mesurer les performences du serveur, j’ai utilisé New Relic. Il s’agit d’un excellent utilitaire qui indique en détail les performences d’une application ainsi les différents points sensibles.
J’ai également utilisé Load Impact, un outil au fonctionnement assez simple. Il suffit d’enregistrer un scénario pour pouvoir le rejouer en simulant un certain nombre de visiteurs. Load Impact fait en sorte d’augmenter peu à peu le nombre de visiteurs sur le site comme on peu le voir sur cette image :
Grâce à cet outil, j’ai pu créer des scénarios et les jouers en simulant 50 visiteurs. L’application comporte 3 types d’utilisateurs, j’ai donc créé un scénario dans chacun de ces cas.
J’ai utilisé l’offre à 10$ par mois, la plus populaire, de Digital Ocean pour effectuer ce test et voici le résultat :
Comme on peut le voir, le temps de réponse est de moins de 800ms. Le temps de traitement est partagé entre Ruby et la base de données. Je crois qu’il s’agit d’un comportement normal et d’un bon temps de réponse.
J’ai répété exactement le même test avec Heroku et voici le résultat :
On voit clairement qu’Heroku est le grand perdant du duel. Je me doutais déjà du résultat, mais je ne pensais pas que la différence serait aussi probante. Le temps de réponse est supérieur à 25 secondes. La majeure partie du temps est prise dans la queue de traitement. On peut également voir qu’Heroku a eu du mal à se remettre du test sur ce graphique :
Avant 10h, il s’agit du trafic normal. La pointe verte, entre 10h et 10h30 représente le pic lors du test effectué. On peut voir qu’après cette pointe, tous les temps sont très longs et la situation n’estpas revenue à la normale.
De part cet exemple, on peut voir assez clairement que DigitalOcean est plus rapide qu’Heroku.
Les résultats obtenus ici ne montrent pas définitivement qu’Heroku est moins efficace que DigitalOcean. Premièrement, je suis loin d’avoir fait des tests exhaustifs. Pour aller plus loin, il serait nécessaire de faire des essais avec les offres payantes d’Heroku ainsi qu’en essayant d’autres scénarios et cas d’utilisation. De plus, Heroku reste génial comme environnement de développement et de test.
Comme d’habitude, je ne prétend pas détenir la vérité. Si vous avez d’autres arguments permettant de confirmer ou d’infirmer ma conclusion, s’il vous plaît, laissez vos commentaires.
]]>Avant de commencer, voici une courte vidéo montrant l’utilisation de PyLeapMouse et Sbire.
Mon objectif principal pour cette application est de contrôler l’OS avec LeapMotion. En tant qu’adepte de l’Open-Source, j’ai premièrement cherché s’il existait un projet le permettant. Je suis tombé sur LeapMouse, écrit en C. J’ai rapidement vu les problèmes existants pour diriger la souris. Pour ma part, je mettais plus de 10 secondes pour réussir à fixer un bouton ou une icône. Sur ce point, l’utilisation classique de la souris est beaucoup plus efficace. De plus, il est nécessaire de maintenir la main à une certaine distance du contrôleur. On doit donc la maintenir en hauteur ce qui est rapidement fatigant.
Le fait d’effectuer des commandes en fonction des mouvements m’a paru une méthode plus efficace et plus précise. Tout d’abord, une commande doit être actionnée par un mouvement court. Il n’est donc pas nécessaire de maintenir la main en suspension pendant une longue période. De plus, le besoin de précision est moindre ce qui rend l’utilisation plus efficace.
Il existe plusieurs SDK pour LeapMotion. Malheureusement pour moi, Ruby, mon langage préféré, n’est pas disponible officiellement. J’avais donc l’embarras du choix des langages.
LeapMouse étant écrit en C, je me suis demandé pourquoi choisir ce langage plutôt qu’un autre. Il est vrai que ce langage possède l’avantage d’être plus bas niveau. La plupart des outils disponibles dans les autres langages sont des dérivés. Également, le C est très avantageux au niveau de la performance. Ces deux points constituent des avantages non négligeables pour l’utilisation du C.
Cela faisait longtemps que je n’avais pas utilisé le C. Étant habitué à Ruby, j’avais un peu d’aprioris à utiliser un langage moins moderne. Je pensais avoir plus de mal avec la syntaxe, mais finalement c’est la difficulté à inclure des librairies qui m’a repoussé. Grâce à bundler, Ruby possède une manière très simple de gérer les dépendances d’un projet. En C, c’est parfois aussi simple de refaire des bouts de code. Réinventer la roue n’étant pas ce que je préfère, j’ai abandonné le C et pris la résolution de l’utiliser uniquement si je n’ai pas le choix. Évidemment, c’est une conclusion très personnelle. Libre à chacun de choisir autre chose.
Python est un langage tout aussi moderne que Ruby. La gestion des dépendances se fait également très bien avec Pip. Bien que la philosophie soit différente que celle de Ruby, il est très facile de passer de l’un à l’autre. De plus, c’est toujours agréable de découvrir autre chose et de changer un peu.
Ensuite, j’ai choisi de modifier openleap/PyLeapMouse. Tout d’abord PyLeapMouse possède déjà un bon nombre de fonctionnalités. De plus, il est relativement populaire et possède déjà une bonne communauté. Ce choix fut assez rapide. Je crois que c’est le projet le plus abouti concernant le contrôle de la souris avec LeapMotion.
Malgré le fait que PyLeapMouse soit fonctionnel, l’utiliser pour déplacer le pointeur de la souris est très fastidieux. Ceci est dû aux limites expliquées dans cet article.
J’ai donc fait un fork du projet pour ajouter la possibilité d’exécuter des commandes en fonctions des mouvements effectuées. Le fonctionnement est assez simple. PyLeapMouse va lire un fichier commands.ini
comme celui-ci :
[screentap]
[keytap]
[swiperight]
1finger: rhythmbox-client --next
[swipeleft]
1finger: rhythmbox-client --previous
[clockwise]
1finger: sbire start
2finger: sbire start
3finger: sbire start
4finger: sbire start
5finger: sbire start
[counterclockwise]
1finger: sbire stop
2finger: sbire stop
3finger: sbire stop
4finger: sbire stop
5finger: sbire stop
À chaque changement détecté par LeapMotion, le programme recherche dans une liste d’objets avec des classes comme celle-ci :
1 2 3 4 5 6 |
|
Chaque commande possède un nom, permettant de l’identifier, et une fonction applicable
permettant de voir si les conditions sont réunies pour exécuter la commande en fonction de la variable frame
représentant les données passées par LeapMotion.
Ensuite, il va rechercher dans la configuration quelle commande il doit exécuter en fonction du nombre de doigts reconnus et du mouvement. Pour plus de détails, vous pouvez voir le code complet sur GitHub.
Cette amélioration rend l’utilisation plus efficace, mais n’est pas fiable à 100%. Il y a très souvent des imperfections dans les mouvements détectés.
Afin d’augmenter la précision, il existe des projets comme LeapTrainer. Celui-ci fait en sorte que le système apprend les mouvements. Il est ensuite possible d’exporter le mouvement appris puis de l’importer dans d’autres outils. C’est effectivement un très bon projet, mais cela manque encore d’efficacité.
Malrgès le fait que lier les commandes aux mouvements soit plus précis, il reste encore beaucoup d’erreurs. C’est pour cette raison qu’il est mieux de le coupler avec Sbire. Sbire est plus précis et les possibilités sont plus vastes. LeapMotion possède une petite liste de mouvements prédéfinis. Sbire, quant à lui, peut reconnaitre un nombre illimité de phrases pour chercher les commandes à exécuter.
Je pense que le couplage des deux outils est plutôt bien. Cependant, je trouve qu’utiliser un raccourci clavier pour démarrer et arrêter Sbire est encore une solution plus efficace. Je crois que LeapMotion a encore du travail à faire pour rendre l’outil 100% fonctionnel.
À sa décharge, LeapMotion est encore en développement et les développeurs se font une priorité d’augmenter la précision dans les prochaines versions. Je suis encore plein d’espoir pour cet outil.
]]>Il s’agit d’un programme écrit en Ruby. Celui-ci fait appel à sox pour faire un enregistrement audio. Il fait ensuite appel à Google voice en y joignant le fichier et y récupère le résultat en format JSon. Ce résultat est ensuite traité et la commande trouvée est exécutée.
Évidemment, je suis ouvert à toute remarque et suggestion. Les contributions sont également les bienvenues. Vous pouvez les soumettes sur Github.
Pour des informations sur l’installation, l’utilisation et la configuration, rendez-vous sur la page Github du projet.
Si vous avez d’autres questions, contactez moi par email, à guirec.corbel@gmail.com, ou sur twitter.
]]>