PeriodicTimer de EventMachine, le cron simplifié
EventMachine est une des meilleures librairies Ruby, tout en étant une des plus sous-estimées et mal connues.
J'ai donc commencé à regarder un peu dans quels cas utiliser EventMachine. La première application que j'en ai trouvé c'est tout simplement le PeriodicTimer.
Son utilisation la plus simple est le remplacement d'une cronjob.
Je m'explique : une cronjob est vraiment une très bonne chose. On peut définir très précisément l'heure à laquelle on souhaite qu'une tâche s'exécute. Celle-ci s'exécute alors à ce moment précis. Cron est vraiment un outil magnifique. Cependant, dans le cas d'une tâche récurrente qui doit être effectuée régulièrement, l'utilisation d'un cronjob peut avoir un gros inconvénient.
Cron n'a aucune gestion de queue. Ainsi on peut se retrouver très simplement avec une quantité astronomique de tâches essayant de s'exécuter en même temps. Votre « load » explose et il faut redémarrer votre serveur. Bien sûr cela n'arrive que si vous lancez une tâche plus fréquemment que son temps d'exécution.
La seule solution serait d'avoir une petite application - comme : jobq - qui gére pour vous un système de queue. Bien sûr, si le fait d'avoir une queue ne sert strictement à rien, cette solution peut être un peu overkill.
L'avantage de PeriodicTimer est que les tâches sont exécutées après un temps défini. Il n'y a aucune gestion d'heure de lancement. Il y a juste un lancement de la tâche après X secondes une fois que la précédente tâche est finie et qu'aucune autre tâche ne soit en cours d'exécution. Cela entraîne effectivement un glissement progressif de l'heure de lancement. Mais globalement, ça ne pose aucun problème. L'important est que la tâche soit exécutée régulièrement. C'est le cas, par exemple dans le cas de récupération de statistique.
Voici un exemple de code qui utilise PeriodicTimer
require 'eventmachine'
require 'timeout'
EventMachine.run {
EventMachine::PeriodicTimer.new(10) do
puts "#{Time.now} : I am 10"
sleep 10
end
EventMachine::PeriodicTimer.new(1) do
puts Time.now
end
}Voici la sortie qui est effectuée si on lance l'application pendant 50s.
$ ruby em_periodic.rb
Fri Mar 26 16:07:39 +0100 2010
Fri Mar 26 16:07:40 +0100 2010
Fri Mar 26 16:07:41 +0100 2010
Fri Mar 26 16:07:42 +0100 2010
Fri Mar 26 16:07:43 +0100 2010
Fri Mar 26 16:07:44 +0100 2010
Fri Mar 26 16:07:45 +0100 2010
Fri Mar 26 16:07:46 +0100 2010
Fri Mar 26 16:07:47 +0100 2010
Fri Mar 26 16:07:48 +0100 2010 : I am 10
Fri Mar 26 16:07:58 +0100 2010
Fri Mar 26 16:07:59 +0100 2010
Fri Mar 26 16:08:00 +0100 2010
Fri Mar 26 16:08:01 +0100 2010
Fri Mar 26 16:08:02 +0100 2010
Fri Mar 26 16:08:03 +0100 2010
Fri Mar 26 16:08:04 +0100 2010
Fri Mar 26 16:08:05 +0100 2010
Fri Mar 26 16:08:06 +0100 2010
Fri Mar 26 16:08:08 +0100 2010
Fri Mar 26 16:08:08 +0100 2010 : I am 10
Fri Mar 26 16:08:18 +0100 2010
Fri Mar 26 16:08:19 +0100 2010
Fri Mar 26 16:08:20 +0100 2010
Fri Mar 26 16:08:21 +0100 2010
Fri Mar 26 16:08:22 +0100 2010
Fri Mar 26 16:08:23 +0100 2010
Fri Mar 26 16:08:25 +0100 2010
Fri Mar 26 16:08:26 +0100 2010
Fri Mar 26 16:08:27 +0100 2010
Fri Mar 26 16:08:28 +0100 2010
Fri Mar 26 16:08:28 +0100 2010 : I am 10On peut constater que la tâche qui doit s'exécuter toutes les secondes le fait bien. Par contre la tâche qui s'exécute au bout de 10 secondes effectue un lock sur le thread. Ainsi la tâche qui s'exécute toutes les secondes attend que le lock soit libéré. Une fois terminée, l'exécution de la tâche toutes les secondes recommence. La tache qui doit s'exécuter toutes les 10 secondes attendra, quand à elle, 10 nouvelles secondes après avoir été terminée.
C'est là une grande puissance de PeriodicTimer. On a facilement une succession de tâche qui ne se marchent jamais sur les pieds. Désormais, je n'utilise plus que
cette solution pour créer des scripts de récupération de données.[...]
Pourquoi Ruby et Ruby On Rails dans le développement d'application web ?
Cette question a été posée dernièrement sur la Mailling List de RailsFrance. Effectivement, si on veut promouvoir Ruby dans le monde informatique français, il faut pouvoir argumenter pourquoi on trouve que c'est une bonne idée.
Tout le monde est en droit de se poser la question. Voici donc, pour moi, l'avantage de Ruby par rapport aux autres technologies à l'heure actuelle.
Ruby c'est fun
Matz, quand il a créé Ruby, a voulu faire un langage fun avec lequel il pourrait s'amuser à coder. Aujourd'hui tous les développeurs Ruby diront à peu près la même chose. Coder en Ruby est plaisant. L'avantage de ce point est qu'un développeur Ruby est plus détendu. Un développeur détendu est un développeur plus heureux. Un développeur plus heureux est motivant pour une équipe. Un développeur heureux essayera de faire son travail proprement. L'ambiance d'une équipe de développeur Ruby peut ainsi être agréable. Moins de stress sur un projet, c'est un projet qui part avec un peu plus de chances de réussir.
Ruby aime les tests
La communauté Ruby actuelle est convaincue de l'utilité des tests unitaires. Ainsi, chaque librairie est poussée par la communauté à avoir des tests. Beaucoup de développeurs, moi y compris, privilégierons une librairie avec des tests unitaires plutôt qu'une sans aucun tests.
Cette philosophie permet d'avoir un environnement de développement de test simplifié directement dans Rails. Pas besoin de se prendre la tête pour faire des tests. Si vous ne faites pas de tests de base : soit vous ne voulez pas en faire, ce que je ne recommande pas, soit vous le faites exprès.
Ruby aime le cloud
En ce moment, l'évolution logique du net est le Cloud Computing. L'idée du Cloud Computing est simple. Nos sites internet ne sont plus hébergés sur une seule machine. Les services sont séparés pour améliorer la scallabilité. Au sein de la communauté Ruby, les développeurs sont très attachés à cette idée. Ainsi beaucoup d'utilitaires pour gérer et utiliser son cloud commencent à voir le jour comme Chef. L'architecture même de Rails permet de facilement sortir de Ruby On Rails pour ainsi utiliser son Cloud. ActiveRessource en est la preuve.
Mais coder dans un Cloud implique de coder plusieurs briques. Le Ruby a des bindings pour tous ces utilitaires, comme les systèmes de queues ou les bases de Données Non-Relationnelle. Je ne dis pas que les autres ne peuvent pas le faire. Mais là encore c'est un effet de groupe. Chaque développeur ruby a envie de jouer avec un Cloud. On pense de plus en plus en cloud.
Les plugins de Rails
Ruby On Rails possède une très grande quantité de plugins. On peut ainsi facilement trouver la petite brique que l'on cherche pour sa propre utilisation. C'est plus que l'utilisation d'un module Drupal. Car là nous sommes dans un environement facilement modifiable et sans aucune limitation. Chose que Drupal peut vite avoir.
Les Ressources
Le plus grand reproche que l'on fait actuellement au choix de Ruby On Rails, c'est le manque de ressources. Effectivement, les ressources de développeurs Ruby sont plus faibles que pour d'autres langages. Mais ceci est selon moi un faux problème. L'apprentissage de Ruby et Ruby On Rails est assez simple si on connait la logique Objet. Ainsi, n'importe quel développeur Java peut tout à fait se mettre à Ruby et Ruby On Rails. Donc avec un expert Ruby/Rails, on peut former facilement une équipe complète et compétente.
Cet argumentaire n'engage bien sûr que moi et peut donc être sujet à caution.
[...]Pourquoi Rspec au lieu de Test::Unit?
Dernièrement, on m'a posé la question toute bête :
Pourquoi Rspec au lieu de Test::Unit ?
C'est vrai que je suis un adepte de Rspec et que je n'utilise que ça si c'est possible. mais je n'ai jamais expliqué ici clairement ce qui me pousse dans ce choix. Donc voici un résumé très bref de mon choix.
Les formateurs
Une des options méconnues de Rspec est la possibilité de définir son formateur. Il existe ainsi plusieurs formateurs base. On peux ainsi avoir un retour direct sous format HTML ou text avec les points ( classique avec Test::Unit). Mais certain formateur peuvent aussi calculer le temps que dure un exemple. Ainsi nous avons une facilité de détection des test les plus lent pour les refactorer et essayer d'améliorer le temps de réalisation de ses tests. Dans ce sens, j'ai moi même créer mon propre formateur Rspec.
Les tests partagés
Rspec dispose d'une possibilité sous estimés, les tests partagés. En effet, on peux définir certain jeux de test comme "partageable" on a ainsi la possibilité de les incorporer facilement dans divers tests. C'est une fonction vraiment pratique dans le cadre de test fonctionnel principalement. Plus besoin de copier/coller son code. :)
Compatibilité Test::Unit
Le passage a Rspec est assez aisé de part sa compatibilité avec Test::Unit. En effet, si vous n'êtes pas familier ou que vous n'aimez pas la formalation my_array.should be_empty, rien ne vous y oblige. Vous pouvez tout à fait utiliser la syntaxe Test::Unit assert my_array.empty?. La migration s'en trouve très largement aisée.
Open-notification v0.1.0 est sortie
Il y a maintenant un mois, j'ai commencé à jouer avec nanite. Pour essayer un peu cette technologie, j'ai réaliser une mini application en sinatra et un agent nanite qui envoi des notifications par Jabber.
De fil en aiguille, le code s'est agrémenté et est surtout passé à Merb avec un système de persistance en CouchDB.
L'idée de base est extrêmement simple, gérer plein de notification de tous type. Il existe déjà ce genre de système comme messagepub. Mais là ca sera open source.
Open notification sort donc en version 0.1.0 pour sa première version. Le code sera mis sous licence AGPLv3. Il est composé d'un agent nanite qui gére l'envoi des notifications jabber et d'une application avec Merb/CouchDB.
La méthode de déploiement est assez compliqué je l'avoue car il n'y a aucune documentation. Cela arrivera dans les prochaines versions.
Une version complête est utilisable par tout un chacun sur open-notification.com. Ce service n'a pour l'instant aucune pérennité. Mais on verra dans le futur ce que ca donnera.
[...]DM-sweatshop, le petit truc en plus par rapport à Factory Girl
En jouant avec Oupsnow, j'ai découvert une astuce sur DM-sweatshop qui est vraiment génial. Aucun exemple n'indique ça et pourtant, c'est selon moi révolutionnaire et même un atout face à Factory Girl.
dm-sweatshop
Pour ceux qui ne le savent pas dm-sweatshop est un gem de génération de donnée, inspiré de Factory Girl.
Factory Girl a été conçu pour éviter les fixtures dans Rails. Il a aussi l'avantage de permettre une meilleure vue des données présentes dans sa base de donnée durant son test. Je conseille tout à fait son utilisation et migre progressivement tous mes tests à l'utilisation de ce genre d'outil.
dm-sweatshop est le Factory Girl dédié à DataMapper, même si Factory Girl peux tout à fait être utilisable avec DataMapper.
Son petit plus
dm-sweatshop contrairement à Factory gril, génére un Hash d'attribut, qu'il surcharge avec les données fournis et génére un objet à partir de ce hash. Ceci entraine par exemple, l'impossibilité d'utiliser les méthodes d'association, il faut directement associer les identifiants.
Mais là où est son petit plus est que justement à cause de cette génération d'un Hash, il y a un Proc qui est généré et rien n'empêche de mettre du code ruby pour générer d'autre objets ou récupérer des objets déjà existant. On peux ainsi écrire l'exemple suivant :
Member.fixture { user = User.first(:login.not => 'admin') || User.gen! project = Project.first || Project.gen! not_project_id = [] while project.has_member?(user) not_project_id << project.id project = Project.first(:id.not => not_project_id) || Project.gen! end { :user_id => user.id, :project_id => project.id, :function_id => (Function.first ? Function.first.id : Function.gen.id),} }
Dans cette exemple, je crée un objet Membre qui est la liaison entre un Projet et un User. Mais au préalable, j'ai récupéré et créé des éléments Projet et User si rien n'existait.
Grâce à dm-sweatshop, c'est enfin fini, les problèmes de multiples liaisons qui avec Factory Girl ne peuvent être contourné (d'après mon expérience en tout cas) ce qui entrainait la création de méthode complète pour créer tout ça. Désormais tout est au même endroit.
Je ne saurais donc vous encourager à utiliser dm-sweatshop sur tout projet DataMapper à la place de Factory Girl.
[...]sortie de typo 5.2
A mon tour de vous annoncer la sortie de Typo 5.2. Cette sortie est la première sortie où je participe activement. En effet, depuis Août dernier, je suis contibuteur de Typo. J'ai d'abord commencé par faire la migration de Typo sur Rails 2.2 (avant même la sortie officiel de Rails 2.2). J'ai ensuite continué avec Frédéric à améliorer au maximum les performances et l'utilisabilité de Typo.
Aujourd'hui avec cette sortie de Typo, le travail est vraiment à la hauteur. Nous avons tout fait pour que cela soit optimum. Mais surtout nous n'avons pas fini. Nous avons ainsi énormément d'idée qui seront intégré dans Typo dans le futur. Nous allons aussi essayé de faire des releases plus régulièrement.
En bonus, voici mon fichier capistrano que j'utilise pour déployer Typo.
set :application, "typo" set :repository, "git://github.com/fdv/typo" set :domain, "shingara.fr" # If you aren't deploying to /u/apps/#{application} on the target # servers (which is the default), you can specify the actual location # via the :deploy_to variable: set :deploy_to, "/var/rails/blog-typo" # If you aren't using Subversion to manage your source code, specify # your SCM below: set :scm, :git set :git_enable_submodules, 1 set :runner, "rails" set :user, "rails" set :use_sudo, false set :thin_conf, "/etc/thin/typo.yml" role :app, domain role :web, domain role :db, domain, :primary => true task :update_config, :roles => [:app] do run "ln -s #{shared_path}/config/database.yml #{release_path}/config/database.yml" run "ln -s #{shared_path}/files #{release_path}/public/files" run "ln -s #{shared_path}/cache #{release_path}/tmp/cache" run "ln -s #{shared_path}/newrelic_rpm #{release_path}/vendor/plugins/newrelic_rpm" run "ln -s #{shared_path}/config/newrelic.yml #{release_path}/config/newrelic.yml" run "ln -s #{shared_path}/config/agent #{release_path}/config/agent" run "ln -s #{shared_path}/config/mail.yml #{release_path}/config/mail.yml" end task :dump_before, :roles => [:app] do run "pg_dump -U typoblog typo > #{shared_path}/typo#{Time::today.strftime('%Y-%m-%d')}.sql" end namespace :deploy do task :start, :roles => [:app] do run "thin -C #{thin_conf} start" end task :stop, :roles => [:app] do run "thin -C #{thin_conf} stop" end task :restart, :roles => [:app] do run "thin -C #{thin_conf} restart" end end after "deploy:update_code", :update_config before "deploy:migrations", :dump_before
Sortie de la première version de Oupsnow 0.1.0
Je suis assez content de vous présenter Oupsnow. En effet, après avoir participé à redmine et l'avoir utilisé, j'ai décidé de créer mon propre bug tracker. Je trouvais de plus en plus de défauts à Redmine qui n'était pas comblé. Il est très fortement inspiré de Lighthouse qui a l'avantage d'être vraiment simple d'utilisation.
Voici donc la première version qui sort après 2 mois de développement. Elle est encore loin d'être un produit complètement fini. Mais elle commence à avoir un début de fonctionnalité suffisante. De plus Oupsnow est un produit réalisé avec Merb. J'ai ainsi pu découvrir et approfondir Merb grâce à ce projet.
Dans la prochaine release, j'améliorerais un peu l'administration. Je créerais aussi un convertisseur de Redmine vers Oupsnow. Cela entrainera ma migration vers Oupsnow à la place de redmine pour ma plateforme de développement
J'ai mis en place une version de démonstration pour vous que ayez une idée de ce que Oupsnow permet.
En bonus, voici mon fichier deploy.rb qui m'a permis de déployer la version de démonstration de Oupsnow par capistrano
set :application, "oupsnow" set :repository, "git://github.com/shingara/oupsnow.git" set :domain, "shingara.fr" # If you aren't deploying to /u/apps/#{application} on the target # servers (which is the default), you can specify the actual location # via the :deploy_to variable: set :deploy_to, "/var/rails/oupsnow-demo" set :deploy_via, :remote_cache set :repository_cache, "#{application}-src" # If you aren't using Subversion to manage your source code, specify # your SCM below: # set :scm, :subversion set :scm, :git set :git_enable_submodules, 1 set :runner, "rails" set :user, "rails" set :use_sudo, false set :rack_up, "/etc/thin/oupsnow-demo.ru" set :merb_port, 46000 role :app, domain role :web, domain role :db, domain, :primary => true task :update_config, :roles => [:app] do run "ln -s #{shared_path}/config/database.yml #{release_path}/config/database.yml" end namespace :deploy do task :start, :roles => [:app] do run "merb -u #{user} -G #{user} -d -c 1 -p #{merb_port} -n #{application} -a thin -e production -m '#{deploy_to}/current/'" end task :stop, :roles => [:app] do run "merb -u #{user} -G #{user} -d -c 1 -K all -p #{merb_port} -n #{application} -a thin -e production -m '#{deploy_to}/current/'" end task :restart, :roles => [:app] do deploy.stop deploy.start end end after "deploy:update_code", :update_config
Les Merb pretty URL
Si comme moi vous être un fan des pretty URL, vous savez surement comment faire avec RubyOnRails. Il suffit de modifier le retour de la méthode to_params. Utilisant Merb, j'ai voulu faire de même avec Merb. J'ai bien sûr commencé par modifier la méthode to_params. Hélas, ce n'est pas du tout la bonne méthode à suivre avec Merb. Mais finalement, la méthode est encore plus simple. Ce qu'il faut c'est utiliser l'option :identify pour votre resources dans votre routeur. J'ai ainsi pu pour Oupsnow définir la méthode ticket_permalink comme méthode définissant un ticket. Je n'ai plus ensuite qu'a définir ce que je souhaite comme retour de permalink. Cette string de retour sera ainsi utilisée dans les URL générées par resource. En créant ensuite la méthode def self.get_by_permalink(ticket_permalink) que j'utilise à la place d'un Ticket.get(id). je peux facilement modifier mon permalink dans le temps. En mettant ma valeur de retour de permalink et de récupération de ticket par ce permalink à jour.
Ce qu'il faut par contre savoir, c'est que contrairement à Rails, le paramètre utilisé ne sera donc plus id et ticket_id dans les routes imbriquées. Ça sera obligatoirement ticket_permalink.
Gestion des dépendances avec Merb
Utilisant de plus en plus Merb sur mon temps personnel et dans mes projets open source, j'ai dû étudier un peu plus profondément le système de dépendances de Merb.
Merb est par essence un framework basé sur les gems. Dans cette logique il pousse à n'utiliser que des gems et uniquement ceux ci.
Utilisation des gems avec Merb
Pour utiliser un gem avec Merb, rien de plus simple car tout est prévu pour. Que ce soit avec votre répertoire local de gem ou alors avec un freeze du gems.
Utilisation de votre répository gem local
Si vous ne voulez utiliser que cette technique, alors rien de plus simple, modifier le fichier /config/dependencies.rb pour indiquer les gems à charger dans votre application. Voici par exemple le fichier dépendencies.rb d'une application installer de frais.
# dependencies are generated using a strict version, don't forget to edit the dependency versions when upgrading. merb_gems_version = "1.0.7.1" dm_gems_version = "0.9.8" # For more information about each component, please read http://wiki.merbivore.com/faqs/merb_components dependency "merb-action-args", merb_gems_version dependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_version dependency "merb-auth-more", merb_gems_version dependency "merb-auth-slice-password", merb_gems_version dependency "merb-param-protection", merb_gems_version dependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version dependency "merb_datamapper", merb_gems_version dependency "do_sqlite3" # If using another database, replace this
On y liste ainsi tous les gems de merb et Datamapper que l'on souhaite utiliser. Si on veux en utiliser de nouveau, il suffit d'en ajouter à cette liste. Vous pouvez aussi en supprimer de cette liste pour diminuer ainsi le nombre de gem chargé dans votre application.
Utilisation d'un repository de gems local à votre application
Voici un point qui différe grandement de RubyOnRails. Mais à mon sens, là encore il est beaucoup mieux conçu que Rails. En effet, l'idée est très simple. Rubygems permet de définir le dossier où se trouve vos gems. Pourquoi ce répertoire ne serait pas simplement dans votre application ?
Pour mettre en place cette technique, il faut au préalable créer le dossier /gems/ dans votre arborescence. Ensuite utiliser les tâches thor pour installer les gems que vous désirez directement dans votre nouveau répository gems. Ainsi pour installer merb-core et datamapper, il vous suffit de faire :
$ thor merb:gem:install merb-core dm-core
Une fois fait de même pour toutes vos dépendances. Vous avez freezé votre application avec tous les gems utilisés.
Si vous utilisez un gestionnaire de source, il faudra alors n'ajouter à votre repository que le dossier /gems/cache/ en effet, ce dossier contient les .gem que vous utilisez. Les autre dossiers seront générés. Ensuite si quelqu'un récupére vos sources, il lui suffira de réaliser la commande thor de redéploiement :
$ thor merb:gem:redeploy
Tous les gems dans le dossier cache seront ainsi installé dans votre dossier gems. Là où je trouve que ce système est meilleur que celui de Rails est que si vous avez un gem avec un extension C vous pouvez le freezer avec Merb. Chose que vous ne pouvez pas faire avec Rails.
Mais et si je veux utiliser les versions de développement ?
Voici selon moi le point faible de Merb face à Rails. En effet, avec rails, rien de plus simple. On met les sources dans le dossier /vendor/ soit /gems/ soit /plugins/ et roulez jeunesse, tout fonctionne sans changement. Un simple git-submodule permet de suivre l'évolution du tout. Mais Merb ne permet pas ça. Merb ne loadera que des gems et uniquement des gems.
Technique recommendée par la core-team
La technique que finalement la core-team recommende est encore une fois de se baser sur les gems. Pour cela, rien de plus simple, il suffit de créer le gem correspondant à votre version de développement, l'ajouter dans le dossier gems/cache et ainsi l'utiliser. Des tâches utilitaires existent pour aider à tout ça. Par exemple pour installer la version de développement de Datamapper, vous pouvez faire :
$ thor merb:source:install dm-core
Cette tâche créera un clone du repository dm-core dans votre dossier /src/, générera le gem et installera dans votre dossier /gem/. Vous pouvez aussi faire de même en installant les sources directement dans le dossier /src/ et utiliser le nom de votre dossier. Ca lancera la tâche rake package et installera le gem généré.
Ce que je trouve dommage avec cette technique est l'impossibilité de savoir réellement quel est la version utilisée. En effet, gem utilise un numéro de version fixe et on n'a pas forcement l'information du commit qui a généré ce gem. J'aime bien pouvoir juste en clonant un projet savoir exactement la source exact du gem que j'utilise. Donc soit sa version, soit son numéro de commit.
Ma technique pour permettre de contourner le problème
La technique que j'ai ainsi trouvé fonctionne, mais n'est hélas pas complétement optiminum. Dans mon fichier config/init.rb, j'ai créé une petite méthode :
def load_from_source(src) $:.unshift File.join(Merb.root, "src/#{src}/lib") require "src/#{src}/init.rb" end
Ensuite dans le callback before_app_loads, j'ai pu faire appel à ma méthode
Merb::BootLoader.before_app_loads do # This will get executed after dependencies have been loaded but before your app's classes have loaded. load_from_source('will_paginate') end
Avec un clone du repository will_paginate dans mon dossier /src/ will_paginate est ainsi chargé automatiquement avant le load de l'application et après le load de toutes les dépendances.
Par contre cette technique à un gros défaut que je n'ai pas réussi à palier sauf en proposant un patch à Merb (ce que je ferais peut-être, même si Merb en tant que tel est mort :'(). En effet, on ne peux loader à partir des sources qu'après le load des dependancies. Si vous voulez loader au milieu des dépendances, vous ne pouvez pas. Il faudra obligatoirement passer par le système des gems.
Ce qu'il ne faut pas faire
Je vous rassure, je l'ai fait, ce qui me permet de vous dire de ne pas le faire. Ainsi, il ne faut pas ajouter le require directement dans le dependencies.rb. En effet, le chargement des dépendences ne se fait pas à ce moment là. Ce n'est qu'un chargement d'une liste gems à charger. Si vous faites votre require à ce moment vous pourrez ainsi vous retrouver avec des problèmes d'ordre de chargement.
Envoyer un email dans un model DataMapper
Avec Merb-mailer, merb fourni un système d'envoi d'email. Par contre merb étant un framework web se voulant ORM agnostique, merb-mailer est considéré comme un controller et non comme un model. De ce fait l'utilisation de la méthode send_mail n'est disponible que dans les controllers. Mais alors comment utiliser la méthode send_mail dans son model ? C'est très simple. Là encore merb utilise un système entièrement ruby ce qui facilite le méchanisme
Après une courte recherche dans le code de merb-mailer, j'ai pu constater que que la commande send_mail était dans un mixin. l'inclusion de ce mixin suffit donc à avoir la commande pour l'utiliser.
class Member include DataMapper::Resource include Merb::MailerMixin property :id, Serial property :name, String def register // some code send_mail (UserMailer, :register, { :from => "no-reply@example.com", :to => person.email, :subject => "Please activate your account" } end