<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3454033395947617111</id><updated>2011-11-28T01:54:35.016+01:00</updated><category term='linux'/><category term='ruby'/><category term='pharo'/><category term='développement'/><category term='python'/><category term='php'/><category term='squeak-vm'/><category term='rails'/><category term='smalltalk'/><category term='video'/><category term='pomodoro technique'/><category term='seaside'/><category term='livres'/><category term='musique'/><title type='text'>-[ MaGaLoMa ]-</title><subtitle type='html'>Blog public à usage privé</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>66</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-762966930914568050</id><published>2011-10-07T09:16:00.001+02:00</published><updated>2011-10-07T11:53:14.229+02:00</updated><title type='text'>Emacs and J2EE on OSX notes</title><content type='html'>Here's how I've managed to setup Emacs for J2EE / Tomcat / Maven application development on OSX.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Install Emacs&lt;/h2&gt;I use Emacs 23.3 from &lt;a href="http://emacsforosx.com/"&gt;Emacs for OSX&lt;/a&gt;.&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;Malabar mode&lt;/h2&gt;&lt;a href="https://github.com/espenhw/malabar-mode"&gt;Malabar mode&lt;/a&gt; offers easy code navigation, Maven, JUnit and Groovy integration. &lt;a href="https://github.com/espenhw/malabar-mode/zipball/malabar-1.4.0"&gt;Download release 1.4&lt;/a&gt; and extract in a directory to build it.&lt;br /&gt;&lt;br /&gt;As the build will invoke emacs in batch mode, be sure that Emacs 23.3 is in your path (instead of using the one packaged with OSX).&lt;br /&gt;&lt;br /&gt;What works on my machine:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;unzip espenhw-malabar-mode-malabar-1.4.0-0-g35e1e5e.zip&lt;br /&gt;cd espenhw-malabar-mode-1daaee4&lt;br /&gt;export PATH=/Applications/Emacs.app/Contents/MacOS:$PATH&lt;br /&gt;mvn package -DskipTests=true&lt;/code&gt;&lt;/pre&gt;Now in target directory there's a malabar-1.4.0-dist.zip. Unzip it where you put all your emacs librairies:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;cd ~/.emacs.d/&lt;br /&gt;unzip /path/to/malabar/target/malabar-1.4.0-dist.zip&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;Configure Emacs for Malabar&lt;/h2&gt;Open your .emacs and add:&lt;br /&gt;&lt;pre style="font-size: 0.9em;"&gt;&lt;code&gt;(add-to-list 'load-path (expand-file-name "~/.emacs.d/malabar-1.4.0/lisp/")) &lt;br /&gt;&lt;br /&gt;(require 'cedet)&lt;br /&gt;(require 'malabar-mode)&lt;br /&gt;(setq malabar-groovy-lib-dir "/path/to/malabar/lib")&lt;br /&gt;(add-to-list 'auto-mode-alist '("\\.java\\'" . malabar-mode))&lt;br /&gt;&lt;br /&gt;(semantic-mode 1)&lt;br /&gt;(require 'malabar-mode)&lt;br /&gt;(setq malabar-groovy-lib-dir "~/.emacs.d/malabar-1.4.0/lib"&lt;br /&gt;      malabar-load-source-from-sibling-projects t&lt;br /&gt;      malabar-extra-source-locations&lt;br /&gt;      '( "/absolute/path/to/project1/src" &lt;br /&gt;        "/absolute/path/to/project2/src" ) ) ;; you need to setup this &lt;br /&gt;&lt;br /&gt;(add-to-list 'auto-mode-alist '("\\.java\\'" . malabar-mode))&lt;br /&gt;&lt;br /&gt;(add-hook 'malabar-mode-hook&lt;br /&gt;     (lambda () &lt;br /&gt;       (add-hook 'after-save-hook 'malabar-compile-file-silently&lt;br /&gt;                  nil t)))&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now open a .java file within Emacs.&lt;br /&gt;&lt;br /&gt;Try &lt;b&gt;malabar-jump-to-thing&lt;/b&gt; on one of your class and it should open the corresponding file.&lt;br /&gt;&lt;br /&gt;Open a test file and try &lt;b&gt;malabar-run-test&lt;/b&gt;. It should run maven test for this file. If all tests OK but finished with "BUILD FAILURE", then see next section &lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;Java troubleshooting&lt;/h2&gt;Malabar should use java from jdk, not jde. I've tried setting JAVA_HOME and configure malabar without success. So quick dirty hack is:&lt;br /&gt;&lt;pre style="font-size: 0.9em;"&gt;&lt;code&gt;&lt;br /&gt;cd /usr/bin&lt;br /&gt;mv java java.jre&lt;br /&gt;ln -s  /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;&lt;h2&gt;Ressources&lt;/h2&gt;&lt;a href="http://tkj.freeshell.org/emacs/java/"&gt;Entreprise Java Development With Emacs&lt;/a&gt;&lt;h2&gt;Debugging Tomcat&lt;/h2&gt;Install &lt;a href="jdibug.googlecode.com"&gt;JDIBug&lt;/a&gt;.In jdibug-expr.el, change the line (and to forget to byte-recompile or remove .elc):&lt;pre&gt;&lt;code&gt;(require 'semantic-java)&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;(require 'semantic/java)&lt;/code&gt;&lt;/pre&gt;Configure emacs:&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(add-to-list 'load-path "~/.emacs.d/jdibug-0.5")&lt;br /&gt;(setq jdibug-connect-hosts (quote ("localhost:8000"))&lt;br /&gt;      jdibug-use-jde-source-paths nil&lt;br /&gt;      jdibug-source-paths&lt;br /&gt;      (list&lt;br /&gt;			 "/path/to/project1/src" &lt;br /&gt;			 "/path/to/project2/src"  ))&lt;br /&gt;(require 'jdibug)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Start Tomcat like with debugger enabled:&lt;pre&gt;&lt;code&gt;./bin/catalina.sh jpda start&lt;/code&gt;&lt;/pre&gt;Then try jdibug-connect to attach Emacs to tomcat.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-762966930914568050?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/762966930914568050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/10/emacs-and-j2ee-on-osx-notes.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/762966930914568050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/762966930914568050'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/10/emacs-and-j2ee-on-osx-notes.html' title='Emacs and J2EE on OSX notes'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5272777556958187126</id><published>2011-07-12T23:49:00.000+02:00</published><updated>2011-07-12T23:49:15.002+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>TWM: Docking Windows</title><content type='html'>Using Pharo 1.4 and latest TWM packages, you can now group windows as tabs.&lt;br /&gt;&lt;br /&gt;Use:&lt;pre&gt;&lt;code&gt;(ConfigurationOfTilingWindowManager project &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;version: #bleedingEdge) load&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;iframe src="http://player.vimeo.com/video/26346973?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="640" height="400" frameborder="0"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5272777556958187126?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5272777556958187126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/07/twm-docking-windows.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5272777556958187126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5272777556958187126'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/07/twm-docking-windows.html' title='TWM: Docking Windows'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7469119079357256288</id><published>2011-06-28T08:01:00.002+02:00</published><updated>2011-10-16T09:37:18.084+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>My Pharo image</title><content type='html'>Backup of my preferences:&lt;code&gt;&lt;pre style='font-size:0.8em'&gt;Gofer it&lt;br /&gt;   squeaksource: 'MetacelloRepository';&lt;br /&gt;   package: 'ConfigurationOfPhexample';&lt;br /&gt;   package: 'ConfigurationOfAutotest';&lt;br /&gt;   squeaksource: 'TilingWindowManager';&lt;br /&gt;   package: 'ConfigurationOfTilingWindowManager';&lt;br /&gt;   load.&lt;br /&gt; &lt;br /&gt;(Smalltalk at:#ConfigurationOfTilingWindowManager) perform: #loadDevelopment.&lt;br /&gt;(Smalltalk at:#ConfigurationOfPhexample) load.&lt;br /&gt;((Smalltalk at:#ConfigurationOfAutotest) project version: #development) load: 'OB'.&lt;br /&gt;&lt;br /&gt;(Smalltalk at:#TWMUITheme) beCurrent. &lt;br /&gt;TaskbarMorph  showTaskbar: false.&lt;br /&gt;TasklistMorph keepOpen: true.&lt;br /&gt;(Smalltalk at:#TWMBar) perform: #showTWMBar: with: true.&lt;br /&gt;(Smalltalk at: #AutotestDashboard) perform: #showAutotestDashboard: with: true.&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7469119079357256288?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7469119079357256288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/06/my-pharo-image.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7469119079357256288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7469119079357256288'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/06/my-pharo-image.html' title='My Pharo image'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5986781952830795376</id><published>2011-06-11T13:40:00.002+02:00</published><updated>2011-06-11T13:45:49.950+02:00</updated><title type='text'>Albaaaaaator, Albaaaaaaator, capitaine au coeur d'or ......</title><content type='html'>Le papa d'&lt;a href="http://fr.wikipedia.org/wiki/Albator"&gt;Albator&lt;/a&gt; / &lt;a href="http://en.wikipedia.org/wiki/Captain_Harlock"&gt;Captain Harlock&lt;/a&gt;, &lt;a href="http://fr.wikipedia.org/wiki/Leiji_Matsumoto"&gt;Lieji Matsumoto&lt;/a&gt;, était à Annecy pour le festival d'animation.... on a la dédicace !!!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-cRGeTc41dLE/TfNUKTCm_YI/AAAAAAAAAjo/kXSsII-A8Tg/s1600/albator.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="295" src="http://1.bp.blogspot.com/-cRGeTc41dLE/TfNUKTCm_YI/AAAAAAAAAjo/kXSsII-A8Tg/s400/albator.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Nostalgie ....&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;iframe frameborder="0" height="384" src="http://www.dailymotion.com/embed/video/x6a1q4" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://www.dailymotion.com/video/x6a1q4_generique-albator-84_shortfilms" target="_blank"&gt;Générique Albator 84&lt;/a&gt; &lt;i&gt;par &lt;a href="http://www.dailymotion.com/MigXIII" target="_blank"&gt;MigXIII&lt;/a&gt;&lt;/i&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5986781952830795376?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5986781952830795376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/06/albaaaaaator-albaaaaaaator-capitaine-au.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5986781952830795376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5986781952830795376'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/06/albaaaaaator-albaaaaaaator-capitaine-au.html' title='Albaaaaaator, Albaaaaaaator, capitaine au coeur d&apos;or ......'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-cRGeTc41dLE/TfNUKTCm_YI/AAAAAAAAAjo/kXSsII-A8Tg/s72-c/albator.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4408917076918488403</id><published>2011-06-07T22:29:00.001+02:00</published><updated>2011-06-07T22:30:08.913+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Multiple Worlds for TWM</title><content type='html'>With the help of my &lt;a href="http://cara74.seasidehosting.st"&gt;local developper group&lt;/a&gt;, I've integrated &lt;a href="http://seandenigris.com/blog/?p=767"&gt;Sean DeNigris work&lt;/a&gt; and now Tiling Window Manager supports Multiple Worlds.&lt;br /&gt;&lt;br /&gt;Here's a screencast that shows the progress. (My English seems a little less catastrophic than the previous screencast :)&lt;br /&gt;&lt;br /&gt;Note: The tests failing on the screencast (Pharo 1.2) are green on Pharo 1.3.&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://player.vimeo.com/video/24791296?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="640" height="480" frameborder="0"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Load TWM with:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt;  squeaksource: 'TilingWindowManager';&lt;br /&gt;  package: 'TWM';&lt;br /&gt;  load.&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And ConfigurationOfKeyMapping 1.7 (TWM do not work with 1.8 actually) with:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt;    squeaksource: 'ShortWays';&lt;br /&gt;    package: 'ConfigurationOfKeymapping';&lt;br /&gt;    load.&lt;br /&gt;    &lt;br /&gt;(ConfigurationOfKeymapping project version: '1.7') load.&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4408917076918488403?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4408917076918488403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/06/multiple-worlds-for-twm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4408917076918488403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4408917076918488403'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/06/multiple-worlds-for-twm.html' title='Multiple Worlds for TWM'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2088104201588278454</id><published>2011-05-28T00:03:00.003+02:00</published><updated>2011-05-31T23:16:59.829+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Tiling Window Manager</title><content type='html'>I love Emacs because of editing speed and fast buffer switching among a lot neat features.&lt;br /&gt;&lt;br /&gt;This week I've tried to make Pharo a little better on window management. This video shows the progress (thanks Gastón and Patrick for help and ideas).&lt;br /&gt;&lt;br /&gt;Next step: KeyMapping integration + history à la Emacs winner-mode.&lt;br /&gt;&lt;br /&gt;Sorry for my soooooooo awful english (tired ....).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 05/31/2011&lt;/b&gt;: you can load last version with colored theme with:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt;  squeaksource: 'TilingWindowManager';&lt;br /&gt;  package: 'TWM';&lt;br /&gt;  load.&lt;br /&gt;(Smalltalk at: #StandardUITheme) beCurrent.&lt;br /&gt;(Smalltalk at: #TWMBar) showTWMBar:true.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Tested on PharoCore 1.2 and Pharo 1.3.&lt;br /&gt;&lt;br /&gt;Now this has support for world snapshot, that means you can arrange the windows and take a snapshot of the layout. Then you can restore these layouts later. (icons camera, &lt;, &gt; and trash). I'm not entirely satisfied with this, thanks for feedback.&lt;br /&gt;&lt;br /&gt;In Pharo 1.3 there's initial integration with KeyMapping. To load:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt;  squeaksource: 'ShortWays';&lt;br /&gt;  package: 'ConfigurationOfKeymapping';&lt;br /&gt;  load.&lt;br /&gt;&lt;br /&gt;(Smalltalk at: #ConfigurationOfKeymapping project version: '1.7') load.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then in settings browser &gt; Keymappings &gt; Tiling Window Managers you have all actions of the dock mapped.&lt;br /&gt;&lt;br /&gt;You can choose a Layout Strategy in settings.&lt;br /&gt;&lt;br /&gt;Actually there's Horizontal (default) and Vertical (last used windows fills all remaining space) strategies.&lt;br /&gt;&lt;br /&gt;To add your own just subclass TWMLayoutStrategy and implement #tileWindows:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://player.vimeo.com/video/24334739?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="640" height="480" frameborder="0"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;To load TWM: &lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt; squeaksource: 'TilingWindowManager';&lt;br /&gt; package: 'TWM';&lt;br /&gt; load.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I also think the UI theme Patrick has cleaned fits better with TilingWM:&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt; squeaksource: 'PBASandbox';&lt;br /&gt; package: 'PBSandbox';&lt;br /&gt; load.&lt;br /&gt; &lt;br /&gt;StandardUITheme beCurrent.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2088104201588278454?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2088104201588278454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/05/tiling-window-manager.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2088104201588278454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2088104201588278454'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/05/tiling-window-manager.html' title='Tiling Window Manager'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4991920384585397962</id><published>2011-04-14T09:22:00.000+02:00</published><updated>2011-04-14T09:22:48.840+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><title type='text'>Pomodoro scripts</title><content type='html'>Sous OSX j'utilise &lt;a href="http://pomodoro.ugolandini.com/"&gt;ce logiciel comme chronomètre&lt;/a&gt; qui a l'avantage de pouvoir exécuter des scripts lorsque le Pomodoro démarre, s'arrête, ....&lt;br /&gt;&lt;br /&gt;Au travail on utilise beaucoup la messagerie instantanée. J'utilise &lt;a href="http://www.adium.im/"&gt;Adium&lt;/a&gt; comme client. &lt;br /&gt;&lt;br /&gt;Besoins:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;je ne veux pas être dérangé pendant mon Pomodoro (passer mon état à indisponible)&lt;/li&gt;&lt;li&gt;je veux que mes collaborateurs sachent quand se termine mon Pomodoro pour me contacter&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Solution&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;Dans Adium créer un statut indisponible avec comme titre'Pomodoro en cours'&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/--oXNCB_kkJY/TaafW_mPfRI/AAAAAAAAAjA/7_h2cqo13gg/s1600/adiumconf.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="362" src="http://3.bp.blogspot.com/--oXNCB_kkJY/TaafW_mPfRI/AAAAAAAAAjA/7_h2cqo13gg/s400/adiumconf.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Dans les préférences de Pomodoro, onglet script, configurez comme ceci:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Start&lt;/b&gt;:&lt;br /&gt;&lt;code&gt;tell application "Adium" to set the status of every account whose status type is available to the first status whose title is "Pomodoro en cours"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Reset et End&lt;/b&gt;:&lt;br /&gt;&lt;code&gt;tell application "Adium" to set the status of every account whose status type is away to the first status whose title is "Disponible"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Every 2 mins&lt;/b&gt;:&lt;br /&gt;&lt;code&gt;tell application "Adium" to set status message of every account to "Pomodoro en cours, fin dans $time mn"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-N4AmucyxJ_I/Taad-Mvz77I/AAAAAAAAAiw/Vv7lt34GTyw/s1600/pomodoro.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="343" src="http://4.bp.blogspot.com/-N4AmucyxJ_I/Taad-Mvz77I/AAAAAAAAAiw/Vv7lt34GTyw/s400/pomodoro.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Tout le long du Pomodoro votre statut sera mis à jour:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-1If7NdotCdo/Taaeo21mnkI/AAAAAAAAAi4/AucoZz8bbnE/s1600/adium.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="36" src="http://2.bp.blogspot.com/-1If7NdotCdo/Taaeo21mnkI/AAAAAAAAAi4/AucoZz8bbnE/s400/adium.png" width="325" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Et dès que quelqu'un ose vous contacter, réponse automatique !&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-H_wx38RM_ls/TaagMhWlaUI/AAAAAAAAAjI/v7xxdsKCbSQ/s1600/reponseauto.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="91" width="400" src="http://4.bp.blogspot.com/-H_wx38RM_ls/TaagMhWlaUI/AAAAAAAAAjI/v7xxdsKCbSQ/s400/reponseauto.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4991920384585397962?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4991920384585397962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/04/pomodoro-scripts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4991920384585397962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4991920384585397962'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/04/pomodoro-scripts.html' title='Pomodoro scripts'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/--oXNCB_kkJY/TaafW_mPfRI/AAAAAAAAAjA/7_h2cqo13gg/s72-c/adiumconf.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2719727391535779888</id><published>2011-04-13T16:08:00.006+02:00</published><updated>2011-04-13T16:48:52.300+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Petite Horloge revisited</title><content type='html'>Another Pharo Smalltalk snippet with temp classes (I like this :)&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(Class new&lt;br /&gt;    superclass: StringMorph;&lt;br /&gt;    setFormat: StringMorph format;&lt;br /&gt;    compile: 'step self contents: Time now printString';&lt;br /&gt;    new)&lt;br /&gt;        openInWindowLabeled: 'Petite Horloge'.&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Don't forget the setFormat: or the VM will crash.&lt;a href="http://forum.world.st/test-crashing-the-cog-vm-tp3393032p3393191.html"&gt;Some explanations&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2719727391535779888?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2719727391535779888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/04/petite-horloge-revisited.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2719727391535779888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2719727391535779888'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/04/petite-horloge-revisited.html' title='Petite Horloge revisited'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5578101668379485987</id><published>2011-04-13T13:44:00.006+02:00</published><updated>2011-04-13T14:45:45.650+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Polymorph counter example</title><content type='html'>I've discovered that I can write this in Pharo:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre style="font-size:0.8em"&gt;"This creates a class and one instance on the fly"&lt;br /&gt;counter := Class new &lt;br /&gt;              superclass: Object; &lt;br /&gt;              addInstVarNamed: 'counter'; &lt;br /&gt;              compile: 'initialize &lt;br /&gt;                           counter := 0';&lt;br /&gt;              compile: 'counterString &lt;br /&gt;                           ^ counter asString';&lt;br /&gt;              compile: 'increment &lt;br /&gt;                           counter := counter + 1. &lt;br /&gt;                           self changed:#counterString';&lt;br /&gt;              compile: 'decrement &lt;br /&gt;                           counter := counter - 1. &lt;br /&gt;                           self changed:#counterString';&lt;br /&gt;              new.&lt;br /&gt;&lt;br /&gt;(UITheme builder newColumn: {&lt;br /&gt;  UITheme builder newLabelFor: counter getLabel: #counterString getEnabled: nil.&lt;br /&gt;  UITheme builder newRow: {&lt;br /&gt;    UITheme builder newButtonFor: counter action: #increment label: '+' help: nil.&lt;br /&gt;    UITheme builder newButtonFor: counter action: #decrement label: '-' help: nil.&lt;br /&gt;  }&lt;br /&gt;}) openInWindowLabeled: 'Counter example'.&lt;/pre&gt;&lt;/code&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-IFNTiQSTc0A/TaWMOGiQ5JI/AAAAAAAAAio/6K5xRGDa0j8/s1600/Counter%2Bexample.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="81" width="101" src="http://4.bp.blogspot.com/-IFNTiQSTc0A/TaWMOGiQ5JI/AAAAAAAAAio/6K5xRGDa0j8/s400/Counter%2Bexample.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Coooooooooooooool ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5578101668379485987?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5578101668379485987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/04/polymorph-counter-example.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5578101668379485987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5578101668379485987'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/04/polymorph-counter-example.html' title='Polymorph counter example'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-IFNTiQSTc0A/TaWMOGiQ5JI/AAAAAAAAAio/6K5xRGDa0j8/s72-c/Counter%2Bexample.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4618521976537998786</id><published>2011-03-19T09:28:00.001+01:00</published><updated>2011-03-19T10:29:02.451+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Multiple worlds for Pharo</title><content type='html'>&lt;a href="http://seandenigris.com/blog/?p=767"&gt;Sean DeNigris submitted a changeset to get multiple worlds in Pharo&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I've played a little with it to get a world switcher. If you want it, first file in the &lt;a href="http://pharo.googlecode.com/issues/attachment?aid=1998046892926746151&amp;name=MultipleWorlds.2.cs&amp;token=7a300580fe8c233c2d7b3c14eee18b9a"&gt;changeset file&lt;/a&gt; (download it the drag the file on an opened image).&lt;br /&gt;&lt;br /&gt;Then the following code add three worlds named 2,3,4 and create a dock in each world.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre style="font-size: 0.9em"&gt;|wm|&lt;br /&gt;wm := WorldManager instance.&lt;br /&gt;#('2' '3' '4') do: [:aString| wm createOrSwitchToWorldNamed: aString].&lt;br /&gt;&lt;br /&gt;wm worlds keysAndValuesDo:  [:aWorldName :aWorld| |dock|&lt;br /&gt;  dock := DockingBarMorph new&lt;br /&gt;          adhereToTop;&lt;br /&gt;          openInWorld: aWorld.&lt;br /&gt;&lt;br /&gt;  wm worlds keysAndValuesDo: [:aWorldName2 :aWorld2|&lt;br /&gt;    dock addMorph: (SimpleButtonMorph new&lt;br /&gt;                    label: aWorldName2;&lt;br /&gt;                    target: [wm createOrSwitchToWorldNamed: aWorldName2];&lt;br /&gt;                    actionSelector: #value) ].&lt;br /&gt;                    &lt;br /&gt;  dock addMorph: (StringMorph contents: aWorldName).&lt;br /&gt;].&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4618521976537998786?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4618521976537998786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/03/multiple-worlds-for-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4618521976537998786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4618521976537998786'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/03/multiple-worlds-for-pharo.html' title='Multiple worlds for Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1217265663847231725</id><published>2011-02-28T22:23:00.000+01:00</published><updated>2011-02-28T22:23:24.140+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Work on Mocketry</title><content type='html'>Mocketry is a &lt;a href="http://www.squeaksource.com/Mocketry.html"&gt;Mocketry&lt;/a&gt; is Smalltalk mock object framework.&lt;br /&gt;&lt;br /&gt;To load it in Pharo: &lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt;  squeaksource: 'MetacelloRepository';&lt;br /&gt;  package: 'ConfigurationOfMocketry';&lt;br /&gt;  load.&lt;br /&gt;&lt;br /&gt;(Smalltalk at:#ConfigurationOfMocketry) project latestVersion load.&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I've done the first part of the &lt;a href="http://www.pharocasts.com/2010/08/see-how-to-get-data-from-url-parse-xml.html"&gt;Picasa screencast&lt;/a&gt; in a TDD way using Mocketry to prevent external HTTP requests.&lt;code&gt;&lt;pre&gt;Gofer it&lt;br /&gt; squeaksource: 'LaurentLSandbox';&lt;br /&gt; package: 'Picasa';&lt;br /&gt; load.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;As the requests are done using &lt;code&gt;HTTPSocket class&gt;&gt;httpGet:&lt;/code&gt;, one way is to give a mock to PicasaSearch so we can check (and stub) the HTTP request: &lt;br /&gt;&lt;code&gt;&lt;pre style='font-size:0.8em'&gt;PicasaSearchTwoRoughSeaTest&gt;&gt;setUp&lt;br /&gt;  [:mockHTTPSocketClass|&lt;br /&gt;    [photos := PicasaSearch new&lt;br /&gt;        httpSocketClass: mockHTTPSocketClass;&lt;br /&gt;        addKeyword: 'rough';&lt;br /&gt;        addKeyword: 'sea';  &lt;br /&gt;        maxResult: 2;&lt;br /&gt;        photos.] &lt;br /&gt; &lt;br /&gt;     should strictly satisfy: [&lt;br /&gt;        (mockHTTPSocketClass httpGet: &lt;br /&gt;          'http://picasaweb.google.com/data/feed/api/all?q=rough+sea&amp;max-results=2')&lt;br /&gt;        willReturn: self fixtureXMLResponseForTwoRoughSea] &lt;br /&gt;  ] runScenario.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;#fixtureXMLResponseForTwoRoughSea&lt;/code&gt; will return an XML string and test methods will check that it is correctly parsed.&lt;br /&gt;&lt;br /&gt;In PicasaSearch: &lt;code&gt;&lt;pre&gt;httpGetDocument &lt;br /&gt;  |url| &lt;br /&gt;  url := String streamContents: [:aStream|&lt;br /&gt;  aStream &lt;br /&gt;    nextPutAll: 'http://picasaweb.google.com/data/feed/api/all?q=';&lt;br /&gt;    nextPutAll: ('+'  join: self keywords);&lt;br /&gt;    nextPutAll: '&amp;max-results=';&lt;br /&gt;    nextPutAll: self maxResult asString.    &lt;br /&gt;  ].  &lt;br /&gt;&lt;br /&gt;  ^ (self httpSocketClass httpGet: url).&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;See that mocketry extends BlockClosure to create mocks:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;[:myFirstMock :mySecondMock|&lt;br /&gt;"do stuff with mocks"&lt;br /&gt;] runScenario&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;and set up expectations:&lt;code&gt;&lt;pre&gt;[:myFirstMock :mySecondMock|&lt;br /&gt;  [ "do stuff with mocks" ] &lt;br /&gt;  should strictly satisfy: &lt;br /&gt;  [ "what is expected on mocks" ]  &lt;br /&gt;] runScenario&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;See HelpSystem book loaded with Mocketry for several examples.&lt;br /&gt;&lt;br /&gt;Comments and better code propositions are welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1217265663847231725?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1217265663847231725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/work-on-mocketry.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1217265663847231725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1217265663847231725'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/work-on-mocketry.html' title='Work on Mocketry'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3931032837599446520</id><published>2011-02-21T18:37:00.001+01:00</published><updated>2011-02-21T18:38:15.112+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>XML Browser with Pharo</title><content type='html'>I've written a little tool to help me browse xml. If you want to try it:&lt;br /&gt;&lt;pre style="font-size: 0.9em;"&gt;&lt;code&gt;Gofer new&lt;br /&gt; squeaksource: 'LaurentLSandbox';&lt;br /&gt; package: 'XML-GUI';&lt;br /&gt; load.&lt;br /&gt; &lt;br /&gt;((Smalltalk at:#XMLBrowser) labelled: 'Picasa search')&lt;br /&gt;  browseAtUrl: &lt;br /&gt;   'http://picasaweb.google.com/data/feed/api/all?q=lighthouse&amp;amp;max-results=10'.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-m2ZqLNiJjVg/TWKhcnWhSrI/AAAAAAAAAiI/Uopqf9jblh0/s1600/xmlbrowser.JPG%2B" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="430" src="http://4.bp.blogspot.com/-m2ZqLNiJjVg/TWKhcnWhSrI/AAAAAAAAAiI/Uopqf9jblh0/s640/xmlbrowser.JPG%2B" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;See &lt;code&gt;XMLBrowser&lt;/code&gt; comment for examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3931032837599446520?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3931032837599446520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/xml-browser-with-pharo.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3931032837599446520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3931032837599446520'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/xml-browser-with-pharo.html' title='XML Browser with Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-m2ZqLNiJjVg/TWKhcnWhSrI/AAAAAAAAAiI/Uopqf9jblh0/s72-c/xmlbrowser.JPG%2B' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4403372315179377726</id><published>2011-02-18T07:57:00.004+01:00</published><updated>2011-02-18T20:05:15.039+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><title type='text'>FizzBuzz Kata: minimal solution</title><content type='html'>Trying to find a minimal solution for the &lt;a href="http://codingdojo.org/cgi-bin/wiki.pl?KataFizzBuzz"&gt;FizzBuzz Kata&lt;/a&gt; in Smalltalk:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;fb := [:counter| |rules|&lt;br /&gt;       rules := {15-&gt;'FizzBuzz'. 5-&gt;'Buzz'. 3-&gt;'Fizz'. 1-&gt;counter}.&lt;br /&gt;       rightRule := rules detect: [:aRule| counter \\ aRule key == 0].&lt;br /&gt;       rightRule value].  &lt;br /&gt;&lt;br /&gt;self assert: (fb value: 7) == 7.&lt;br /&gt;self assert: (fb value: 3) == 'Fizz'.&lt;br /&gt;self assert: (fb value: 5) == 'Buzz'.&lt;br /&gt;self assert: (fb value: 15) == 'FizzBuzz'.&lt;br /&gt;&lt;br /&gt;1 to: 100 do: [:counter | &lt;br /&gt;               Transcript &lt;br /&gt;                 show: (fb value: counter) asString;&lt;br /&gt;                 cr]&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Update: Little variant from Alexandre:&lt;code&gt;&lt;pre&gt;rightRule := rules detect: [:aRule| counter isDivisibleBy: aRule key]&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4403372315179377726?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4403372315179377726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/fizzbuzz-kata-minimal-solution.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4403372315179377726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4403372315179377726'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/fizzbuzz-kata-minimal-solution.html' title='FizzBuzz Kata: minimal solution'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-357564619883442515</id><published>2011-02-08T07:45:00.002+01:00</published><updated>2011-02-08T07:53:46.932+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Smalltalk tiny exercise for TDD newbies</title><content type='html'>Done in course:&lt;br /&gt;&lt;br /&gt;Create the category &lt;b&gt;Exercise&lt;/b&gt; in the Browser. All classes you create will be placed in this category.&lt;br /&gt;&lt;br /&gt;Implement the class &lt;b&gt;Operation&lt;/b&gt; (using Test-Driven Development) which should work like this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;&lt;pre&gt;a := Operation new.&lt;br /&gt;a result.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;Should return 0.&lt;/li&gt;&lt;li&gt;&lt;code&gt;&lt;pre&gt;b := Operation new.&lt;br /&gt;b add: 2.&lt;br /&gt;b add: 3.&lt;br /&gt;b result.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;Should return 5.&lt;/li&gt;&lt;li&gt;&lt;code&gt;&lt;pre&gt;Operation new&lt;br /&gt;  add: 2;&lt;br /&gt;  add: 4;&lt;br /&gt;  divideBy: 2;&lt;br /&gt;  result.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;Should return 3.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Add the message &lt;code&gt;Operation&gt;&gt;#multiplyBy&lt;/code&gt; by yourself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-357564619883442515?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/357564619883442515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/smalltalk-tiny-exercise-for-tdd-newbies.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/357564619883442515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/357564619883442515'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/smalltalk-tiny-exercise-for-tdd-newbies.html' title='Smalltalk tiny exercise for TDD newbies'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3292516108964763331</id><published>2011-02-06T19:31:00.002+01:00</published><updated>2011-02-07T07:32:27.364+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>new funny ProfStef lesson</title><content type='html'>Update to last ProfStef (on Pharo 1.2 only):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;Gofer it&lt;br /&gt; squeaksource: 'MetacelloRepository';&lt;br /&gt; package: 'ConfigurationOfProfStef';&lt;br /&gt; load.&lt;br /&gt; &lt;br /&gt;ConfigurationOfProfStef project latestVersion load.&lt;br /&gt;&lt;br /&gt;ProfStef &lt;br /&gt;  go;&lt;br /&gt;  tutorial: SmalltalkSyntaxTutorial lesson: #instanciation.&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/TU7oy0l9GXI/AAAAAAAAAiA/blUz0XgQVWg/s1600/profstef.JPG%2B" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="548" src="http://1.bp.blogspot.com/_JLK6EDfW4so/TU7oy0l9GXI/AAAAAAAAAiA/blUz0XgQVWg/s640/profstef.JPG%2B" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3292516108964763331?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3292516108964763331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/new-funny-profstef-lesson.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3292516108964763331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3292516108964763331'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/new-funny-profstef-lesson.html' title='new funny ProfStef lesson'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/TU7oy0l9GXI/AAAAAAAAAiA/blUz0XgQVWg/s72-c/profstef.JPG%2B' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4478103539970787473</id><published>2011-02-02T09:12:00.000+01:00</published><updated>2011-02-02T09:12:53.535+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Why technology matters</title><content type='html'>&lt;cite&gt;Life is very short. You have a limited amount of time in which to realize your dreams, in which to do your projects. If you’re like me, you probably have dozens and dozens of ideas for apps, sites, portals, that you want to implement. Most of those ideas will suck. But you don’t know which ones, you won’t know until you try them. Your goal, in this life, is to iterate faster. To fail more, to fail quicker, until you get to the brilliant ideas and brilliant projects that can change the world, or at least make you a dollar or two.&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;From &lt;a href="http://smalltalkzen.wordpress.com/2011/02/01/a-secret-passion-and-your-choice-of-web-framework/"&gt;A Secret: Passion and Your Choice of Web Framework by Dmitri Zagidulin&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4478103539970787473?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4478103539970787473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/02/why-technology-matters.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4478103539970787473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4478103539970787473'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/02/why-technology-matters.html' title='Why technology matters'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5483474347574978203</id><published>2011-01-28T08:07:00.000+01:00</published><updated>2011-01-28T08:07:51.209+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Smalltalk à l'OSDC.fr 2010</title><content type='html'>Et là j'ai peur en me voyant en vidéo ....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYKerE4C" type="application/x-shockwave-flash" width="480" height="299" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYKerlcC" type="application/x-shockwave-flash" width="480" height="299" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYKemyQC" type="application/x-shockwave-flash" width="480" height="299" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYKenB4C" type="application/x-shockwave-flash" width="480" height="299" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5483474347574978203?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5483474347574978203/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/01/smalltalk-losdcfr-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5483474347574978203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5483474347574978203'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/01/smalltalk-losdcfr-2010.html' title='Smalltalk à l&apos;OSDC.fr 2010'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1624398242190201374</id><published>2011-01-25T22:06:00.005+01:00</published><updated>2011-02-03T08:11:00.671+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo GUI with Polymorph</title><content type='html'>I've written a tiny address book to prepare a screencast on Polymorph basis. If you want to look at it:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;Gofer it&lt;br /&gt; squeaksource: 'Pharocasts';&lt;br /&gt; package: 'ContactManager';&lt;br /&gt; load.&lt;br /&gt; &lt;br /&gt;(Smalltalk at:#ContactListEditor) open.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/TT80nUnPyOI/AAAAAAAAAh0/A2-o7j3Rv4M/s1600/polymorph.JPG%2B" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="346" src="http://2.bp.blogspot.com/_JLK6EDfW4so/TT80nUnPyOI/AAAAAAAAAh0/A2-o7j3Rv4M/s400/polymorph.JPG%2B" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Start by looking how the contact list is built:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ContactListEditor&amp;gt;&amp;gt;open&lt;br /&gt;  |builder content|&lt;br /&gt;  builder := UITheme builder.&lt;br /&gt;  content := builder newColumn: {   &lt;br /&gt;        builder &lt;br /&gt;                newListFor: self   &lt;br /&gt;                list: #contacts&lt;br /&gt;                selected: #contactSelectedIndex&lt;br /&gt;                changeSelected: #contactSelectedIndex:&lt;br /&gt;                help: 'contacts'.&lt;br /&gt;        builder newRow: {&lt;br /&gt;                builder newButtonFor: self &lt;br /&gt;                        action: #addButtonClick &lt;br /&gt;                        label: 'Add' &lt;br /&gt;                        help: 'Create a new contact'.&lt;br /&gt;                builder newButtonFor: self &lt;br /&gt;                        action: #removeButtonClick &lt;br /&gt;                        getEnabled: #hasSelectedContact &lt;br /&gt;                        label: 'Remove' &lt;br /&gt;                        help: 'Remove selected contact'.&lt;br /&gt;                builder newButtonFor: self &lt;br /&gt;                        action: #editButtonClick &lt;br /&gt;                        getEnabled: #hasSelectedContact &lt;br /&gt;                        label: 'Edit' &lt;br /&gt;                        help: 'Edit selected contact'  }}.&lt;br /&gt;   &lt;br /&gt;  (content openInWindowLabeled: 'Contacts') extent: 400@500.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;#newRow: and #newColumn: are an easy way to align elements on the window.&lt;br /&gt;&lt;br /&gt;When the Add button is clicked, message #addButtonClick is sent on the ContactListEditor object:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ContactListEditor&gt;&gt;addButtonClick&lt;br /&gt;  |newContact|&lt;br /&gt;  newContact := Contact new.&lt;br /&gt; &lt;br /&gt;  ContactEditor new&lt;br /&gt;        contact: newContact;&lt;br /&gt;        onOK: [ Contact database add: newContact.  &lt;br /&gt;                selectedContactIndex := Contact database size.&lt;br /&gt;                self &lt;br /&gt;                        changed: #contacts;&lt;br /&gt;                        changed: #hasSelectedContact];&lt;br /&gt;        openModal.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The closure given to onOK: adds the new Contact and tells the view to refresh components which depends on #contacts and #hasSelectedContact selectors - that means the contact list and the Remove and Edit buttons.&lt;br /&gt;&lt;br /&gt;ContactEditor defines a modal dialog to edit the firstName and lastName of a Contact:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ContactEditor&gt;&gt;openModal&lt;br /&gt;  |builder dialog content firstName|&lt;br /&gt;  &lt;br /&gt;  builder := UITheme builder.&lt;br /&gt;  content := (builder newLabelGroup: {&lt;br /&gt;                'First name' -&amp;gt; (&lt;br /&gt;                       firstName := (builder&lt;br /&gt;                         newTextEntryFor: contact&lt;br /&gt;                         getText: #firstName&lt;br /&gt;                         setText: #firstName: &lt;br /&gt;                         help: 'Enter the first name of the contact')&lt;br /&gt;                       acceptOnCR: false;&lt;br /&gt;                       minWidth: 200).&lt;br /&gt;                'Last name' -&amp;gt; (&lt;br /&gt;                       (builder&lt;br /&gt;                          newTextEntryFor: contact &lt;br /&gt;                          getText: #lastName &lt;br /&gt;                          setText: #lastName: &lt;br /&gt;                          help: 'Enter the last name of the contact')&lt;br /&gt;                        acceptOnCR: false;&lt;br /&gt;                        minWidth: 200) }).&lt;br /&gt; &lt;br /&gt;  dialog := builder &lt;br /&gt;              newPluggableDialogWindow:'Edit contact' &lt;br /&gt;              for: content.&lt;br /&gt;  dialog rememberKeyboardFocus: firstName.&lt;br /&gt;  builder openModal: dialog.&lt;br /&gt; &lt;br /&gt;  dialog cancelled ifFalse: [self doOnOK].&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;From Gary Chambers (and thanks !):&lt;br /&gt;&lt;cite&gt;&lt;br /&gt;Disabling the acceptOnCR for each text frield allows the default dialog handling for the return key (defaults to OK).&lt;br /&gt;&lt;br /&gt;Normally the initial keyboard focus for a dialog is the default button, if specified. Remembering the first name field prior to opening will give that field focus.&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Now it should be easier to understand Polymorph examples found in &lt;br /&gt;&lt;a href="http://slbrowserfb.appspot.com/code/pharo/1.1/UITheme"&gt;UITheme&lt;/a&gt;&amp;nbsp;class&amp;gt;&amp;gt;exampleBasicControls and friends (in examples protocol).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1624398242190201374?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1624398242190201374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/01/pharo-gui-with-polymorph.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1624398242190201374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1624398242190201374'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/01/pharo-gui-with-polymorph.html' title='Pharo GUI with Polymorph'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JLK6EDfW4so/TT80nUnPyOI/AAAAAAAAAh0/A2-o7j3Rv4M/s72-c/polymorph.JPG%2B' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4736195337328842398</id><published>2011-01-24T10:35:00.007+01:00</published><updated>2011-01-24T22:52:41.485+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>CodeRetreat à Grenoble</title><content type='html'>Vendredi dernier je me suis rendu au premier &lt;a href="http://www.coderetreat.com/how-it-works.html"&gt;CodeRetreat&lt;/a&gt; en France organisé par Johan Martinsson, Remy Sanlaville et Miguel Moquillon, avec le soutien du &lt;a href="http://clubagile.org/"&gt;CARA&lt;/a&gt; et &lt;a href="http://www.alpesjug.org/"&gt;Alpes JUG&lt;/a&gt; - merci tout le monde !&lt;br /&gt;&lt;br /&gt;En résumé, le CodeRetreat consiste à enchaîner des itérations d'une heure en pair-programming et Test-Driven Development sur le même problème tout au long de la journée. Le problème choisi était le &lt;a href="http://fr.wikipedia.org/wiki/Jeu_de_la_vie"&gt;jeu de la vie&lt;/a&gt;, qui a les avantages d'offrir plusieurs approches possibles et d'être assez complexe pour ne pas être résolu en 45mn.&lt;br /&gt;&lt;br /&gt;Chaque itération se déroule de la manière suivante:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Constitution des binômes (on change à chaque itération).&lt;/li&gt;&lt;li&gt;45mn de programmation.&lt;/li&gt;&lt;li&gt;15mn de rétrospective.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;C'est agile, le temps est chronométré (belle organisation).&lt;br /&gt;&lt;br /&gt;A la fin des 45mn de programmation, le code est effacé. Comme l'a répété Johan, ce n'est pas le résultat qui compte, mais la démarche, l'approche du développement qu'on doit s'efforcer de faire le plus parfaitement. C'est à dire:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;développement piloté uniquement par les tests&lt;/li&gt;&lt;li&gt;pas de duplication de code&lt;/li&gt;&lt;li&gt;noms clairs et significatifs&lt;/li&gt;&lt;li&gt;code le plus simple possible&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Côté langages nous avions beaucoup de Java. Miguel et moi en avons profité pour faire découvrir Pharo / Smalltalk et faire une session ensemble en Haskell. Il y a aussi eu un peu de Ruby.&lt;br /&gt;&lt;br /&gt;Les 15mn de rétrospective permettaient d'échanger sur les différentes approches, les difficultés rencontrées, succès. On voit bien qu'il y a plusieurs manières d'attaquer le problème. Au fur et à mesure des automatismes se mettent en place ce qui permet de gagner du temps pour aborder d'autres aspects du problème.&lt;br /&gt;&lt;br /&gt;Suit ce que j'en retire.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Pair-programming:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Chaque session se déroulait avec un binôme différent et chaque expérience, démarche a du coup été significativement différente. J'ai rencontré plusieurs cas:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;une personne qui a l'idée et une autre qui suit: c'est du coup souvent la personne qui veut voir son idée réalisée qui pilote et qui tape. La personne qui suit dépense beaucoup d'énergie pour rester dans le coup (j'ai été dans cette position sur une session). Il est nécessaire que la personne qui pilote temporise.&lt;/li&gt;&lt;li&gt;deux personnes qui ont des idées: cela amène assez vite a quelques débats sympathiques mais c'est l'expérience qui me semble la plus enrichissante, d'où émerge des solutions de type "waouh, ça c'est cool".&lt;/li&gt;&lt;li&gt;deux personnes ont la même idée, suivent la même route: dans ce cas c'est plus la volonté de bien faire qui ressort et la recherche de qualité / perfection dans la démarche est vraiment élevée&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Dans tous les cas, il y a toujours une bonne ambiance à coder avec des inconnus :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Test-Driven Development:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Je suis convaincu de la démarche et la pratique depuis que j'ai lu eXtreme Programming de Kent Beck vers 2002/2003. Donc je fais partie des gens qui ont des automatismes assez arrêtés sur le sujet :). Pour avoir vu d'autres façons de faire au cours de la session, je reste persuadé que la manière la plus claire est d'avoir:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;un TestCase par contexte. Le setUp établit le contexte, les tests font les vérifications.&lt;/li&gt;&lt;li&gt;un assert par test. Avoir plusieurs assert doit rester exceptionnel car ça complexifie les tests (ce n'est pas interdit, mais il faut avoir conscience du compromis).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Un exemple concret est la règle: &lt;a href="http://fr.wikipedia.org/wiki/Jeu_de_la_vie#R.C3.A8gles"&gt;"Si une cellule a exactement deux voisines vivantes, elle reste dans son état actuel à l’étape suivante"&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Avec Rémy nous avons codé comme suit (grosso modo, le code a été effacé :)&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ALivingCellWithTwoOrThreeLivingNeighbours&amp;gt;&amp;gt;#testShouldLive&lt;br /&gt;  |aliveCell|&lt;br /&gt;  aliveCell := Cell new revive.&lt;br /&gt;  self assert: aliveCell isAlive.&lt;br /&gt;&lt;br /&gt;  2 to: 3 do: [:numberOfNeighbours|&lt;br /&gt;    aliveCell numberOfNeighbours: numberOfNeighbours.&lt;br /&gt;    self assert: aliveCell shouldLive.&lt;br /&gt;  ]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Le soucis c'est que quand le test est en échec, il faut débugger pour savoir si ça coince pour deux ou trois cellules.&lt;br /&gt;&lt;br /&gt;Dans ce cas je préfère avoir deux cas:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ALivingCellWithTwoLivingNeighbours&lt;/li&gt;&lt;li&gt;ALivingCellWithThreeLivingNeighbours&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Ceci dit cela risque d'amener une duplication de code, vu que les tests sont identiques dans chaque cas. Une solution codée avec Aline est d'utiliser des Traits pour définir les tests de manière générique puis de mettre seulement les différences dans les TestCase.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;Trait named: #TCellIsAliveAndShouldLive&lt;br /&gt;&lt;br /&gt;TCellIsAliveAndShouldLive&gt;&gt;#testIsAlive&lt;br /&gt;  self assert: self aliveCell isAlive&lt;br /&gt;&lt;br /&gt;TCellIsAliveAndShouldLive&gt;&gt;#testShouldLive&lt;br /&gt;  self assert: self aliveCell shouldLive&lt;br /&gt;&lt;br /&gt;TCellIsAliveAndShouldLive&gt;&gt;#aliveCell&lt;br /&gt;   self explicitRequirement &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Du coup pour nos cas de test:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;TestCase subclass: #ALivingCellWithTwoLivingNeighbours&lt;br /&gt;  uses: TCellIsAliveAndShouldLive&lt;br /&gt;&lt;br /&gt;ALivingCellWithTwoLivingNeighbours&amp;gt;&amp;gt;#aliveCell&lt;br /&gt;  ^ Cell new&lt;br /&gt;       revive;&lt;br /&gt;       numberOfNeighbours: 2.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;de même:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;TestCase subclass: #ALivingCellWithThreeLivingNeighbours&lt;br /&gt;  uses: TCellIsAliveAndShouldLive&lt;br /&gt;&lt;br /&gt;ALivingCellWithThreeLivingNeighbours&amp;gt;&amp;gt;#aliveCell&lt;br /&gt;  ^ Cell new&lt;br /&gt;        revive;&lt;br /&gt;        numberOfNeighbours: 3.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Approches&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Les deux approches principales consistaient à commencer par implémenter la Cellule ou bien la Grille / Univers. Il était aussi possible de passer par une classe Neighbours intermédiaire.&lt;br /&gt;&lt;br /&gt;Haskell qui est un langage fonctionnel permet d'avoir une approche par les ensembles. &lt;br /&gt;&lt;br /&gt;Une solution liée à l'imagerie en travaillant en binaire a été évoquée mais pas implémentée à ma connaissance.&lt;br /&gt;&lt;br /&gt;Pour simplifier l'algorithme qui calcule si la cellule doit vivre ou mourir en connaissant son état actuel et le nombre de ses voisins vivants, deux variantes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;avoir deux classes LiveCell et DeadCell qui ont chacune leur algorithme&lt;/li&gt;&lt;li&gt;implémenter le pattern strategy et donc un objet Cell utiliserait soit un DeadCellStrategy, soit un LiveCellStrategy. Ceci à l'avantage de ne pas avoir à changer le type des instances dans la grille&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Avec Smalltalk une solution utilisée pour implémenter le pattern strategy se basait sur les Closures et évitait de créer des classes supplémentaires:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;  Cell&amp;gt;&amp;gt;#liveCellStrategy&lt;br /&gt;    ^ [(liveNeighbours &amp;lt; 2) or: [ liveNeighbours &amp;gt; 3 ]]&lt;br /&gt;&lt;br /&gt;  Cell&amp;gt;&amp;gt;#deadCellStrategy&lt;br /&gt;    ^ [(liveNeighbours = 3) not]&lt;br /&gt;&lt;br /&gt;  Cell&amp;gt;&amp;gt;die&lt;br /&gt;    shouldDieStrategy := self deadCellStrategy&lt;br /&gt;&lt;br /&gt;  Cell&amp;gt;&amp;gt;revive&lt;br /&gt;    shouldDieStrategy := self liveCellStrategy&lt;br /&gt;&lt;br /&gt;  Cell&amp;gt;&amp;gt;#shouldDie&lt;br /&gt;    ^ shouldDieStrategy value&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bilan&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Journée enrichissante et éreintante. La retrospective finale en a un peu pâti vu que tout le monde était fatigué. Une proposition d'amélioration qui me plaît est de découper la session de 45mn en deux pomodoro de 20mn pour ménager notre énergie.&lt;br /&gt;&lt;br /&gt;Quelques "Smalltalk c'est cool" font plaisir ;)&lt;br /&gt;&lt;br /&gt;En tout cas je reste convaincu par l'approche pair-programming qui est trés enrichissante et donne du courage pour explorer de nouvelles pistes et aller plus vite (car on se perds moins).&lt;br /&gt;&lt;br /&gt;C'est dans ces moments là qu'on sent que la programmation est un artisanat au sens noble du terme, où la beauté du résultat est une source de motivation.&lt;br /&gt;&lt;br /&gt;Par &lt;a href="http://twitter.com/#!/bootis"&gt;@bootis&lt;/a&gt; &lt;cite&gt;quality = you know why it works&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://martinsson-johan.blogspot.com/"&gt;Le compte-rendu de Johan&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4736195337328842398?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4736195337328842398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/01/coderetreat-grenoble.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4736195337328842398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4736195337328842398'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/01/coderetreat-grenoble.html' title='CodeRetreat à Grenoble'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1165167172324183723</id><published>2011-01-14T07:56:00.000+01:00</published><updated>2011-01-14T07:56:15.583+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>PharoConf Annecy 2011</title><content type='html'>La première PharoConf Annecy aura lieu le jeudi 10 février à l'IMUS.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pharoconf-annecy.seasidehosting.st/"&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://pharoconf-annecy.seasidehosting.st/"&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/TS_zhQXaYzI/AAAAAAAAAhY/Uyl7fWNfIOU/s1600/pharoconf_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="177" src="http://1.bp.blogspot.com/_JLK6EDfW4so/TS_zhQXaYzI/AAAAAAAAAhY/Uyl7fWNfIOU/s320/pharoconf_2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Site web de l'évènement: &lt;a href="http://pharoconf-annecy.seasidehosting.st/"&gt;http://pharoconf-annecy.seasidehosting.st/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Les interventions seront plus tournées vers des coding-dojo et ateliers, développer du vrai code qui tourne :)&lt;br /&gt;&lt;br /&gt;Deux points forts:&lt;br /&gt;- Randori Test-Driven Development animé par Miguel Moquillon du Club Agile Rhône Alpes&lt;br /&gt;- PharoSprint animé par Stéphane Ducasse&lt;br /&gt;&lt;br /&gt;Entrée gratuite, mettez à jour vos agendas !&lt;br /&gt;&lt;br /&gt;(Et merci de faire passer le message :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1165167172324183723?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1165167172324183723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/01/pharoconf-annecy-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1165167172324183723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1165167172324183723'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/01/pharoconf-annecy-2011.html' title='PharoConf Annecy 2011'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/TS_zhQXaYzI/AAAAAAAAAhY/Uyl7fWNfIOU/s72-c/pharoconf_2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5900237171306512417</id><published>2011-01-10T22:42:00.000+01:00</published><updated>2011-01-10T22:42:22.084+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Petite horloge</title><content type='html'>Morphic basics with a little Watch (Horloge in french).&lt;br /&gt;&lt;br /&gt;Add the class:&lt;code&gt;&lt;pre&gt;StringMorph subclass: #Horloge&lt;br /&gt; instanceVariableNames: ''&lt;br /&gt; classVariableNames: ''&lt;br /&gt; poolDictionaries: ''&lt;br /&gt; category: 'Sandbox'&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;One method called periodically thanks to stepping mechanism of Morphic: &lt;code&gt;&lt;pre&gt;Horloge&gt;&gt;step&lt;br /&gt; self contents: Time now printString.&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then evaluate in a Workspace: &lt;code&gt;&lt;pre&gt;Horloge new openInWorld.&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/TSt8dZbFFPI/AAAAAAAAAhQ/aSBCpW6z02g/s1600/horloge.JPG%2B" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="200" width="266" src="http://4.bp.blogspot.com/_JLK6EDfW4so/TSt8dZbFFPI/AAAAAAAAAhQ/aSBCpW6z02g/s320/horloge.JPG%2B" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Stop the watch: &lt;code&gt;&lt;pre&gt;Horloge allInstances last stopStepping&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Start it again:&lt;code&gt;&lt;pre&gt;Horloge allInstances last startStepping&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Finally close it:&lt;code&gt;&lt;pre&gt;Horloge allInstances last delete&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5900237171306512417?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5900237171306512417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2011/01/petite-horloge.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5900237171306512417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5900237171306512417'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2011/01/petite-horloge.html' title='Petite horloge'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JLK6EDfW4so/TSt8dZbFFPI/AAAAAAAAAhQ/aSBCpW6z02g/s72-c/horloge.JPG%2B' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2512904759871446140</id><published>2010-12-04T09:30:00.000+01:00</published><updated>2010-12-04T09:30:20.593+01:00</updated><title type='text'>My cat is a Linux geek :)</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/TPn8BhodApI/AAAAAAAAAg4/wuZRFi5qDuA/s1600/IMG_0316.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/_JLK6EDfW4so/TPn8BhodApI/AAAAAAAAAg4/wuZRFi5qDuA/s400/IMG_0316.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2512904759871446140?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2512904759871446140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/12/my-cat-is-linux-geek.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2512904759871446140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2512904759871446140'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/12/my-cat-is-linux-geek.html' title='My cat is a Linux geek :)'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JLK6EDfW4so/TPn8BhodApI/AAAAAAAAAg4/wuZRFi5qDuA/s72-c/IMG_0316.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8887757909976586927</id><published>2010-11-28T09:41:00.008+01:00</published><updated>2010-11-28T15:50:38.334+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><title type='text'>Revenge of Smalltalk</title><content type='html'>(Joke inside)&lt;br /&gt;&lt;br /&gt;In Paul Graham's essay &lt;a href="http://paulgraham.com/icad.html"&gt;Revenge of the Nerds&lt;/a&gt; (Read it, good ideas there):&lt;br /&gt;&lt;cite&gt;&lt;br /&gt;Appendix: Power&lt;br /&gt;&lt;br /&gt;As an illustration of what I mean about the relative power of programming languages, consider the following problem. We want to write a function that generates accumulators-- a function that takes a number n, and returns a function that takes another number i and returns n incremented by i.&lt;br /&gt;&lt;br /&gt;(That's incremented by, not plus. An accumulator has to accumulate.)&lt;br /&gt;&lt;br /&gt;In Common Lisp this would be&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defun foo (n)&lt;br /&gt;  (lambda (i) (incf n i)))&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;cite&gt;&lt;br /&gt;In Smalltalk the code is &lt;b&gt;slightly longer&lt;/b&gt; than in Lisp:&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;foo: n                              &lt;br /&gt;  |s|                      &lt;br /&gt;  s := n.                          &lt;br /&gt;  ^[:i| s := s+i. ]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;cite&gt;&lt;br /&gt;because although in general lexical variables work, you can't do an assignment to a parameter, so you have to create a new variable s.&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Well, the Lisp code has 34 significant characters while the Smalltalk code has only 25 significant characters (we can remove the last dot).  So the Smalltalk code is 26% shorter than the Lisp code :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Perl example: 32 characters&lt;/li&gt;&lt;li&gt;Javascript example: 46 characters&lt;/li&gt;&lt;li&gt;Python example: 54 characters&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Yeah, Smalltalk is really powerful ;P&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt;&lt;br /&gt;&lt;a href="http://forum.world.st/Cannot-assign-a-parameter-tp3062241p3062283.html"&gt;Looking at Parser code&lt;/a&gt; we can &lt;a href="http://forum.world.st/Cannot-assign-a-parameter-tp3062241p3062359.html"&gt;easily remove the temp. variable limitation&lt;/a&gt; and write:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;foo: n               &lt;br /&gt;  ^[:i| n := n+i]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;So&lt;ul&gt;&lt;li&gt;One power of Smalltalk is to be able to change the system.&lt;/li&gt;&lt;li&gt;17 significant characters: Smalltalk &lt;b&gt;100% more powerful&lt;/b&gt; than Lisp :D&lt;/li&gt;(still a joke - but ....) &lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Why this design decision ? Some explanation from &lt;a href="http://forum.world.st/Cannot-assign-a-parameter-tp3062241p3062411.html"&gt;Nicolas&lt;/a&gt; and &lt;a href="http://forum.world.st/Cannot-assign-a-parameter-tp3062241p3062422.html"&gt;Adrian&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8887757909976586927?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8887757909976586927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/11/revenge-of-smalltalk.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8887757909976586927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8887757909976586927'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/11/revenge-of-smalltalk.html' title='Revenge of Smalltalk'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7529243145457340323</id><published>2010-10-28T14:01:00.004+02:00</published><updated>2010-10-28T14:10:09.110+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Three views of OO Programming</title><content type='html'>By Ralph Johnson, quoted in &lt;a href="http://www.springerlink.com/content/n7143830149p6861/"&gt;The Myths of Object-Orientation&lt;/a&gt; p.624:&lt;br /&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;cite&gt; I explain three views of OO programming. The Scandinavian view is that an OO system is one whose creators realise that programming is modelling. The mystical view is that an OO system is one that is built out of objects that communicate by sending messages to each other, and computation is the messages flying from object to object. The software engineering view is that an OO system is one that supports data abstrac- tion, polymorphism by late-binding of function calls, and inheritance.&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It seems that in (French) schools we learn the engineering view. Then with experience we can agree on the mystical view. With Smalltalk I think I now understand the Scandinavian view. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Oscar Nierstrasz have written in &lt;a href="http://blog.jot.fm/2010/08/26/ten-things-i-hate-about-object-oriented-programming/"&gt;Ten Things I Hate About OOP&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;For me the point of OOP is that it isn’t a paradigm like procedural, logic or functional programming. Instead, OOP says “for every problem you should design your own paradigm”. In other words, the OO paradigm really is: Programming is Modeling&lt;/cite&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7529243145457340323?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7529243145457340323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/10/three-views-of-oo-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7529243145457340323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7529243145457340323'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/10/three-views-of-oo-programming.html' title='Three views of OO Programming'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7568628878299641796</id><published>2010-06-18T12:08:00.002+02:00</published><updated>2010-06-18T12:09:54.890+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Autotest for Pharo</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;I've started a new little project. Autotest is a live testing tool (similar to Ruby Autotest, but the Smalltalk way, more dynamic).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Autotest automatically runs tests related to the method you edit. Screencast to see it in action:&amp;nbsp;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;object height="480" width="640"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12666507&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12666507&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="480"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Autotest uses the following heuristics to find the tests to run:&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- if the method is a test, runs it&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- it the method is a test setUp or tearDown, run all the tests of the TestCase&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- else find all senders which are tests in the same package and runs them (it detects different packages that are related, for example ProfStef-Core and ProfStef-Test)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;To activate the Autotest dashboard:&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- open the settings browser&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- go under System&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt;  &lt;/span&gt;- check "Show Autotest Dashboard" option&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To load it:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Gofer new&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt; &lt;/span&gt;squeaksource: 'Autotest';&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt; &lt;/span&gt;package: 'Autotest';&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: pre;"&gt; &lt;/span&gt;load&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;HelpSystem book included.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7568628878299641796?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7568628878299641796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/06/autotest-for-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7568628878299641796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7568628878299641796'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/06/autotest-for-pharo.html' title='Autotest for Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5073452508314792468</id><published>2010-05-15T13:20:00.000+02:00</published><updated>2010-05-15T13:20:25.219+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>GNU/Linux install party des Savoie</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;Une grande&amp;nbsp;&lt;b&gt;Install Party des Savoie&lt;/b&gt;&amp;nbsp;aura lieu le&amp;nbsp;&lt;b&gt;samedi 22 mai&lt;/b&gt;&amp;nbsp;et - évènement unique -&amp;nbsp;&lt;b&gt;simultanément sur Chambéry&lt;/b&gt;&amp;nbsp;(MJC Résidence Escoffier, 379 fbg Montmélian)&amp;nbsp;&lt;b&gt;et sur Annecy&lt;/b&gt;&amp;nbsp;(CDDP, 2 rue des Aravis) de 10h à 17h (les associations seront sur place 1 heure avant).&lt;br /&gt;&lt;br /&gt;La distribution retenue pour une installation grand public est&amp;nbsp;&lt;b&gt;Ubuntu 10.04 'Lucid Lynx'&lt;/b&gt;&amp;nbsp;LTS, et ses dérivés au cas où matériel le nécessiterai.&lt;br /&gt;&lt;br /&gt;Chambéry et Annecy seront reliées en vidéo via EKIGA (sous réserve d'essais préalables concluants).&lt;br /&gt;&lt;br /&gt;L'annonce de l'Install-Party est diffusée sur les radio locales (France Bleu Pays de Savoie, ODS Radio, Montagne FM) et est relayée dans la presse par le Dauphiné Libéré. Des affiches sont également diffusées dans les villes.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5073452508314792468?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5073452508314792468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/05/gnulinux-install-party-des-savoie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5073452508314792468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5073452508314792468'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/05/gnulinux-install-party-des-savoie.html' title='GNU/Linux install party des Savoie'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7678624563288617434</id><published>2010-04-20T22:44:00.000+02:00</published><updated>2010-04-20T22:44:57.907+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>easy_squeakvm on github</title><content type='html'>I've created my first repository on github. So easy! &lt;br /&gt;&lt;br /&gt;&lt;a href="http://github.com/lolgzs/easy_squeakvm"&gt;easy_squeakvm&lt;/a&gt; is used to build the squeak vm from scratch on Linux in &lt;b&gt;one&lt;/b&gt; command.&lt;br /&gt;To build the VM, just get the the script and execute&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;./easy_squeakvm.sh&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This will&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;checkout squeakvm sources from svn repository&lt;/li&gt;&lt;li&gt;download PharoCore image&lt;/li&gt;&lt;li&gt;load VMMaker into PharoCore&lt;/li&gt;&lt;li&gt;generate the interpreter&lt;/li&gt;&lt;li&gt;build the VM&lt;/li&gt;&lt;li&gt;put binaries in out/squeakvm, ready to use&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I was tired of typing the same commands over and over :)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://github.com/lolgzs/easy_squeakvm"&gt;http://github.com/lolgzs/easy_squeakvm&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7678624563288617434?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7678624563288617434/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/04/easysqueakvm-on-github.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7678624563288617434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7678624563288617434'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/04/easysqueakvm-on-github.html' title='easy_squeakvm on github'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1006347529621212591</id><published>2010-03-10T23:10:00.004+01:00</published><updated>2010-03-18T21:58:15.739+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak-vm'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>How I build squeak vm on (Arch)Linux</title><content type='html'>I write this post as a memento for me. I will appreciate some feedback on building the VM on other Linux distro.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Prepare a working directory&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;All the work will take place in ~/squeakvm (/home/lol/squeakvm on my machine):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;mkdir ~/squeakvm&lt;br /&gt;cd ~/squeakvm&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Get squeak-vm source&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The easiest part. The VM source code tarball can be found on &lt;a href="http://www.squeakvm.org/unix/"&gt;http://www.squeakvm.org/unix/&lt;/a&gt;. At the time of writing, the last stable version is 3.11-3.2135. Go and get it:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;wget http://www.squeakvm.org/unix/release/Squeak-3.11.3.2135-src.tar.gz&lt;br /&gt;tar -xvzf Squeak-3.11.3.2135-src.tar.gz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Load VMMaker tool&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;For more explanations on why VMMaker is needed and how it works, &lt;a href="http://www.javipimas.com.ar/node/2"&gt;here is a good overview&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To generate the source code for your own VM, you need a running Pharo image. Here I use a fresh &lt;a href="https://gforge.inria.fr/frs/download.php/25397/Pharo1.0-10508-rc2dev10.01.2.zip"&gt;Pharo 1.0-10508 rc2 image&lt;/a&gt; from &lt;a href="http://pharo-project.org/"&gt;Pharo&lt;/a&gt; website.&lt;br /&gt;&lt;br /&gt;Open your image, then load VMMaker by evaluating this in a Workspace:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;Gofer new&lt;br /&gt; squeaksource: 'MetacelloRepository';&lt;br /&gt; package: 'ConfigurationOfVMMaker';&lt;br /&gt; load.&lt;br /&gt; &lt;br /&gt;(Smalltalk at:#ConfigurationOfVMMaker) project lastVersion load.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/S5gCwIUxkzI/AAAAAAAAAeE/t7-2L0lpc38/s1600-h/vm1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="221" src="http://1.bp.blogspot.com/_JLK6EDfW4so/S5gCwIUxkzI/AAAAAAAAAeE/t7-2L0lpc38/s400/vm1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Load FT2Plugin&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE&lt;/b&gt;: Should not be necessary now. See &lt;a href="http://magaloma.blogspot.com/2010/03/how-i-build-squeak-vm-on-archlinux.html?showComment=1268944730081#c3166080062627307718"&gt;Javier's comment&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To display anti-aliased font of Pharo images, you need the FT2Plugin. To load it:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;MCHttpRepository&lt;br /&gt; location:'http://www.squeaksource.com/FreeTypePlus'&lt;br /&gt; user: ''&lt;br /&gt; password: ''.&lt;br /&gt;&lt;br /&gt;MCHttpRepository&lt;br /&gt; location: 'http://source.impara.de/freetype'&lt;br /&gt; user: ''&lt;br /&gt; password: ''.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. Configure VMMaker and generate the VM source&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now you can open VMMakerTool:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;VMMakerTool openInWorld&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the &lt;b&gt;Path to platforms&lt;/b&gt; field put the path to the extracted Squeak VM tarball: &lt;b&gt;~/squeakvm/Squeak-3.11.3.2135-src&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Then right-click on one of the plugins pane and select &lt;b&gt;make all external&lt;/b&gt; for a simple configuration.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/S5gI3aDEK5I/AAAAAAAAAeM/WuPxQlQ3Ww0/s1600-h/vm5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="157" src="http://3.bp.blogspot.com/_JLK6EDfW4so/S5gI3aDEK5I/AAAAAAAAAeM/WuPxQlQ3Ww0/s320/vm5.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note you can have a description of each plugin by reading the class comment.Classes are part of&lt;b&gt; VMMaker-Plugins&lt;/b&gt; category.&lt;br /&gt;&lt;br /&gt;In &lt;b&gt;path to generated source&lt;/b&gt; select a directory where the VM source will be written. Here &lt;b&gt;~/squeakvm/src32&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;The VMMakerTool window should look like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/S5gHZYuoxsI/AAAAAAAAAeI/nZGcLZHl0SU/s1600-h/vm3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="406" src="http://2.bp.blogspot.com/_JLK6EDfW4so/S5gHZYuoxsI/AAAAAAAAAeI/nZGcLZHl0SU/s640/vm3.png" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Then click on the &lt;b&gt;Entire&lt;/b&gt; button (top-left of window). Wait a moment, pray, and the code should be generated.&lt;br /&gt;&lt;br /&gt;Finally, I replace the original VM source with the generated one.&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;rm -rf ~/squeakvm/Squeak-3.11.3.2135-src/unix/src&lt;br /&gt;cp -a ~/squeakvm/src32 ~/squeakvm/Squeak-3.11.3.2135-src/unix/src&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: theorically this should not be necessary as the configure script accepts an option to specify the source directory path, but it doesn't seem to work.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. Build the VM&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First create a directory for the build and go in:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;mkdir ~/squeakvm/Squeak-3.11.3.2135-src/build&lt;br /&gt;cd ~/squeakvm/Squeak-3.11.3.2135-src/build&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then run the configure script&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;../unix/cmake/configure &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;First problem. I have this error:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;CMake Warning (dev) in &lt;br /&gt;/home/lol/squeakvm/Squeak-3.11.3.2135-src/build/=/CMakeLists.txt:&lt;br /&gt;  Syntax error in cmake code when parsing string&lt;br /&gt;&lt;br /&gt;    ${=_link_directories}&lt;br /&gt;&lt;br /&gt;  syntax error, unexpected cal_SYMBOL, expecting } (21)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Thanks &lt;a href="http://n4.nabble.com/unix-CMake-compilation-error-tp1462169p1566309.html"&gt;to this mail&lt;/a&gt; there's a patch to correct it.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;cd ~/squeakvm/Squeak-3.11.3.2135-src/unix/cmake/&lt;br /&gt;wget http://lolgzs.free.fr/pharo/Plugins.cmake.patch&lt;br /&gt;patch -p0 &amp;lt; Plugins.cmake.patch&lt;br /&gt;&lt;/pre&gt;Then clean and run the configure script again &lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;cd ~/squeakvm/Squeak-3.11.3.2135-src/build&lt;br /&gt;rm -rf *&lt;br /&gt;../unix/cmake/configure &lt;br /&gt;&lt;/pre&gt;You should have the Makefile now. Run make: &lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;make&lt;br /&gt;&lt;/pre&gt;Then another problem as libfreetype (needed by FT2Plugin) is not found. To correct it, create config.cmake for FT2Plugin this way: &lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;mkdir ~/squeakvm/Squeak-3.11.3.2135-src/unix/plugins/FT2Plugin/&lt;br /&gt;echo "PLUGIN_FIND_LIBRARY(FT2 freetype)" &amp;gt; &lt;br /&gt;    ~/squeakvm/Squeak-3.11.3.2135-src/unix/plugins/FT2Plugin/config.cmake&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://n4.nabble.com/how-to-add-FTPlugin-tp1288695p1288985.html"&gt;More informations on this error here&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE&lt;/b&gt;: See &lt;a href="http://magaloma.blogspot.com/2010/03/how-i-build-squeak-vm-on-archlinux.html?showComment=1268944730081#c3166080062627307718"&gt;Javier's comment&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Clean and run make again: &lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;make clean&lt;br /&gt;make&lt;br /&gt;&lt;/pre&gt;Then install: &lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;sudo make install&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;7. Conclusion&lt;/b&gt;&lt;br /&gt;The process is not so easy. To help I put all the squeakvm directory (with sources, pharo image with vmmaker) on &lt;a href="http://lolgzs.free.fr/pharo/squeakvm.tar.gz"&gt;http://lolgzs.free.fr/pharo/squeakvm.tar.gz&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;UPDATE: &lt;a href="http://www.adrian-lienhard.ch/blog?article=building-a-pharo-squeak-vm-from-first-principles"&gt;Good post for MacOSX &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1006347529621212591?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1006347529621212591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/03/how-i-build-squeak-vm-on-archlinux.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1006347529621212591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1006347529621212591'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/03/how-i-build-squeak-vm-on-archlinux.html' title='How I build squeak vm on (Arch)Linux'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/S5gCwIUxkzI/AAAAAAAAAeE/t7-2L0lpc38/s72-c/vm1.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3290690707143743090</id><published>2010-02-25T18:35:00.000+01:00</published><updated>2010-02-25T18:35:03.182+01:00</updated><title type='text'>Pharo &amp; Smalltalk party</title><content type='html'>J'organise avec quelques confrères une "Pharo &amp; Smalltalk party", dans l'objectif de faire découvrir et partager les expériences du développement Pharo et Smalltalk.&lt;br /&gt;&lt;br /&gt;Que vous soyez développeur curieux ou Smalltalker expérimenté, rendez-vous le Samedi 20 Mars de 14h à 18h dans les locaux de Félix Création à Cran-Gévrier (bassin Annécien).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://maps.google.fr/maps/ms?ie=UTF8&amp;msa=0&amp;msid=103554697329621538913.0004807015a3d5af550e7&amp;ll=45.910466,6.101875&amp;spn=0.016274,0.038238&amp;z=15&amp;iwloc=0004807015a6d8fbd2908" target="blank"&gt;Voici le plan du lieu de rendez-vous.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3290690707143743090?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3290690707143743090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/02/pharo-smalltalk-party.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3290690707143743090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3290690707143743090'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/02/pharo-smalltalk-party.html' title='Pharo &amp; Smalltalk party'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1693646480198608957</id><published>2010-01-30T19:15:00.001+01:00</published><updated>2010-01-30T20:25:42.877+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='seaside'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Seaside-Hosting compatible Pharo</title><content type='html'>L'hébergeur gratuit (pour des applications non-commerciales) &lt;a href="http://www.seasidehosting.st/"&gt;Seaside-Hosting&lt;/a&gt; accepte maintenant les images Pharo ! J'en ai donc profité pour déployer ma première image Seaside... et c'est d'une simplicité étonnante. &lt;br /&gt;&lt;br /&gt;Il suffit juste de créer un compte, uploader son image, clicker sur Start et c'est fini. Cool.&lt;br /&gt;&lt;br /&gt;J'ai donc mis en ligne une image avec &lt;a href="http://magaloma.blogspot.com/2009/12/simplewebdoc-sur-squeaksource.html"&gt;SimpleWebDoc&lt;/a&gt;: &lt;a href="http://magaloma.seasidehosting.st/"&gt;http://magaloma.seasidehosting.st/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1693646480198608957?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1693646480198608957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/seaside-hosting-compatible-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1693646480198608957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1693646480198608957'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/seaside-hosting-compatible-pharo.html' title='Seaside-Hosting compatible Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3712465767363810326</id><published>2010-01-29T08:33:00.000+01:00</published><updated>2010-01-29T08:33:43.883+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Screencast sous Linux</title><content type='html'>Après avoir essayé pas mal d'outils disponibles sous Linux pour réaliser des screencasts et alimenter &lt;a href="http://pharocasts.blogspot.com/"&gt;Pharocasts&lt;/a&gt;, voici le contenu de ma boîte à outils du moment.&lt;br /&gt;&lt;br /&gt;Pour enregistrer j'utilise &lt;a href="http://xvidcap.sourceforge.net/"&gt;Xvidcap&lt;/a&gt;. On définit une zone de capture et le mode multi-frame permet de générer la vidéo à la volée. Ma zone de capture est en 800x600, éviter les résolutions exotiques (garder du 4:3 ou 16:9). Dans le cas contraire on risque une perte importante de la qualité par les encodeurs et les sites de publication en ligne.&lt;br /&gt;J'enregistre en avi/mpeg4.&lt;br /&gt;&lt;br /&gt;Pour couper les scènes, j'aime bien &lt;a href="http://fixounet.free.fr/avidemux/"&gt;Avidemux&lt;/a&gt;. Interfaces texte, GTK et Qt disponibles. Il s'appuie sur son propre fork de &lt;a href="http://ffmpeg.org/"&gt;FFmpeg&lt;/a&gt; pour l'encodage et c'est très rapide si vous gardez le même format que la vidéo originale.&lt;br /&gt;&lt;br /&gt;Pour encapsuler au format mov, car les possesseurs d'anciennes versions de MacOSX ont des soucis avec l'avi, j'utilise la commande suivante (qui ne doit pas être la meilleure, vu que le fichier généré double de taille pour une qualité moindre):&lt;br /&gt;&lt;pre&gt;ffmpeg -i mavideo.mpeg -b 500k mavideo.mov&lt;/pre&gt;&lt;br /&gt;Je suis toujours à la recherche d'un outil sympa pour le montage, type &lt;a href="p://www.kdenlive.org"&gt;Kdenlive&lt;/a&gt;, mais je n'ai encore rien trouvé qui me plaise vraiment.&lt;br /&gt;&lt;br /&gt;Pour publier, entre Vimeo, YouTube et DailyMotion, c'est &lt;a href="http://vimeo.com"&gt;Vimeo &lt;/a&gt;que je préfère pour la qualité de la vidéo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3712465767363810326?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3712465767363810326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/screencast-sous-linux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3712465767363810326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3712465767363810326'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/screencast-sous-linux.html' title='Screencast sous Linux'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3269049796338283036</id><published>2010-01-22T09:34:00.001+01:00</published><updated>2010-01-29T08:35:13.051+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>ProfStef news</title><content type='html'>&lt;a href="http://www.squeaksource.com/ProfStef.html"&gt;ProfStef&lt;/a&gt; est inclut dans le &lt;a href="http://www.squeaksource.com/MetacelloRepository.html"&gt;Metacello Repository&lt;/a&gt;. &lt;a href="http://gemstonesoup.wordpress.com/2009/08/25/metacello-package-management-for-monticello/"&gt;Metacello&lt;/a&gt; permet de spécifier&amp;nbsp;les dépendances entre packages.&lt;br /&gt;&lt;br /&gt;Cela signifie qu'il est maintenant assez simple de charger ProfStef et ses dépendances dans Pharo via &lt;a href="http://www.squeaksource.com/Loader.html"&gt;Loader&lt;/a&gt; (l'équivalent d'&lt;b&gt;apt-get&lt;/b&gt; pour Pharo):&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer new&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;squeaksource: 'Loader';&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;package: 'Loader';&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;load.&lt;br /&gt;&lt;br /&gt;Loader new load: 'ProfStef'.&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Il est maintenant possible de créer son propre tutorial intéractif pour ProfStef. Comment ? Il y a un tutorial pour ça :).&lt;br /&gt;&lt;br /&gt;Dans un Workspace, évaluez:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;ProfStef goOn: HowToMakeYourOwnTutorial.&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3269049796338283036?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3269049796338283036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/profstef-news.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3269049796338283036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3269049796338283036'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/profstef-news.html' title='ProfStef news'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3261513238967536191</id><published>2010-01-18T21:14:00.003+01:00</published><updated>2010-01-18T21:16:59.269+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Smalltalk interactive tutorial</title><content type='html'>&lt;a href="http://www.squeaksource.com/ProfStef.html"&gt;ProfStef&lt;/a&gt; est un tutorial intéractif sur Smalltalk (un peu inspiré de &lt;a href="http://tryruby.org/"&gt;Try Ruby&lt;/a&gt;).&lt;br /&gt;L'objectif est d'apprendre les bases du langage Smalltalk de manière ludique.&lt;br /&gt;&lt;br /&gt;Pour charger ProfStef dans votre image, évaluez le code suivant dans un Workspace:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;Gofer new&lt;br /&gt;    squeaksource: 'ProfStef';&lt;br /&gt;    package: 'ProfStef';&lt;br /&gt;    load.&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/S1TA3-QLGsI/AAAAAAAAAc8/exWS2WANJVA/s1600-h/profstef.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/S1TA3-QLGsI/AAAAAAAAAc8/exWS2WANJVA/s640/profstef.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3261513238967536191?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3261513238967536191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/smalltalk-interactive-tutorial.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3261513238967536191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3261513238967536191'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/smalltalk-interactive-tutorial.html' title='Smalltalk interactive tutorial'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JLK6EDfW4so/S1TA3-QLGsI/AAAAAAAAAc8/exWS2WANJVA/s72-c/profstef.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-5974395829972904984</id><published>2010-01-10T23:21:00.000+01:00</published><updated>2010-01-10T23:21:59.139+01:00</updated><title type='text'>Blog dédié à Pharo</title><content type='html'>Je viens de mettre en ligne un blog qui me servira à publier et pointer des tutoriels vidéo sur Pharo. Ça s'appelle Pharocasts et c'est ici: &lt;a href="http://pharocasts.blogspot.com/"&gt;http://pharocasts.blogspot.com/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-5974395829972904984?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/5974395829972904984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/blog-dedie-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5974395829972904984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/5974395829972904984'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/blog-dedie-pharo.html' title='Blog dédié à Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-37007277822337659</id><published>2010-01-08T20:44:00.005+01:00</published><updated>2010-01-10T21:00:19.205+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>GUI et Pharo</title><content type='html'>J'ai fait une vidéo sur Pharo montrant les techniques de base pour disposer des éléments dans une fenêtre et répondre aux clics sur les boutons.&lt;br /&gt;&lt;br /&gt;&lt;object height="480" width="640"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=8619593&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=8619593&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="480"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Voici un des exemples de code de la vidéo:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;|aWindow aPanel buttonPanel|&lt;br /&gt;&lt;br /&gt;aWindow := SystemWindow labelled: 'My first window'.&lt;br /&gt;&lt;br /&gt;aPanel := PluggablePanelMorph new.&lt;br /&gt;aWindow addMorph: aPanel fullFrame: (&lt;br /&gt;    LayoutFrame&lt;br /&gt;        fractions: (0@0 corner: 1@1)&lt;br /&gt;        offsets: (0@0 corner: 0@50 negated)).&lt;br /&gt;aPanel color: (Color blue).&lt;br /&gt;&lt;br /&gt;buttonPanel := PluggablePanelMorph new.&lt;br /&gt;aWindow addMorph: buttonPanel fullFrame: ( &lt;br /&gt;    LayoutFrame&lt;br /&gt;        fractions: (0@0.99 corner: 1@1)&lt;br /&gt;        offsets: (0@50 negated corner: 0@0)).&lt;br /&gt;buttonPanel  color: (Color green).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#(one two three) do: [:label| |aButton| &lt;br /&gt; aButton := PluggableButtonMorph new label: label; yourself.&lt;br /&gt; buttonPanel addMorph: aButton fullFrame: nil.&lt;br /&gt;].&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;aWindow openInWorld.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-37007277822337659?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/37007277822337659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/gui-et-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/37007277822337659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/37007277822337659'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/gui-et-pharo.html' title='GUI et Pharo'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4544937518375092041</id><published>2010-01-04T11:24:00.004+01:00</published><updated>2010-01-11T10:41:27.834+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Ruby 1.9, SQLite et UTF-8</title><content type='html'>Comme abordé précédemment dans le billet &lt;a href="http://magaloma.blogspot.com/2009/12/migrer-vers-ruby-19.html"&gt;Migrer vers Ruby 1.9&lt;/a&gt;, le gem sqlite3-ruby actuel (version 1.2.5) ne fonctionne pas vraiment avec des données UTF-8.&lt;br /&gt;&lt;br /&gt;Un fork de la version 1.2.4 qui corrige ces problèmes est disponible ici:&amp;nbsp;&lt;a href="http://github.com/qoobaa/sqlite3-ruby"&gt;http://github.com/qoobaa/sqlite3-ruby&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Pour l'installer, clonez d'abord le repository avec git:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;git clone git://github.com/qoobaa/sqlite3-ruby.git&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;puis allez dans le répertoire créé et construisez le gem:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cd sqlite3-ruby&lt;br /&gt;gem build sqlite3-ruby.gemspec&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;si tout fonctionne vous avez maintenant le gem &lt;b&gt;sqlite3-ruby-1.2.6.gem&lt;/b&gt;. Installez-le:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;gem install sqlite3-ruby-1.2.6.gem &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;C'est tout pour Linux, sous Windows cela se complique étant donné que le gem intègre une extension C qu'il faut linker avec SQLite. Vous devriez donc tomber sur l'erreur:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Building native extensions.  This could take a while...&lt;br /&gt;ERROR:  Error installing sqlite3-ruby:&lt;br /&gt;        ERROR: Failed to build gem native extension.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ou si vous avec déjà installé le devkit:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;checking for sqlite3.h... no&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La suite nécessite d'avoir le &lt;b&gt;devkit&lt;/b&gt; d'installé sur votre machine (voir &lt;a href="http://rubyinstaller.org/download.html"&gt;http://rubyinstaller.org/download.html&lt;/a&gt; et &lt;a href="http://wiki.github.com/oneclick/rubyinstaller/development-kit"&gt;http://wiki.github.com/oneclick/rubyinstaller/development-kit&lt;/a&gt;&amp;nbsp;).&lt;br /&gt;&lt;br /&gt;Créez un répertoire qui contiendra les fichiers nécessaires à la compilation de l'extension, à savoir &lt;b&gt;sqlite3.h&lt;/b&gt; (dans le sous-répertoire &lt;b&gt;include&lt;/b&gt;) et &lt;b&gt;sqlite3.dll&lt;/b&gt; (dans le sous-répertoire &lt;b&gt;lib&lt;/b&gt;). Par exemple:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;C:\Ruby19\sqlite3&lt;br /&gt;            |- include&lt;br /&gt;            |   |- sqlite3.h&lt;br /&gt;            |- lib&lt;br /&gt;                |- sqlite3.dll &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le fichier &lt;b&gt;sqlite3.h&lt;/b&gt; se trouve dans les sources de SQLite: &lt;a href="http://www.sqlite.org/sqlite-amalgamation-3_6_21.zip"&gt;http://www.sqlite.org/sqlite-amalgamation-3_6_21.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Le fichier &lt;b&gt;sqlite3.dll&lt;/b&gt; se trouve dans la distribution binaire pour Windows: &lt;a href="http://www.sqlite.org/sqlitedll-3_6_22.zip"&gt;http://www.sqlite.org/sqlitedll-3_6_22.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Modifiez ensuite la configuration de la compilation de l'extension pour inclure le répertoire créé. Ouvrez le fichier &lt;b&gt;ext/sqlite3_api/extconf.rb&lt;/b&gt; des sources du gem et ajoutez la ligne suivante:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;dir_config("sqlite3", "C:\\Ruby19\\sqlite3")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vous pouvez maintenant regénérer le gem et l'installer:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;gem build sqlite3-ruby.gemspec&lt;br /&gt;gem install sqlite3-ruby-1.2.6.gem &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4544937518375092041?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4544937518375092041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2010/01/ruby-19-sqlite-et-utf-8.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4544937518375092041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4544937518375092041'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2010/01/ruby-19-sqlite-et-utf-8.html' title='Ruby 1.9, SQLite et UTF-8'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1329036077393668539</id><published>2009-12-30T13:36:00.003+01:00</published><updated>2010-01-04T18:15:42.908+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Migrer vers ruby 1.9</title><content type='html'>Je viens de migrer deux applications Rails 2.3.5 vers Ruby 1.9.1 avec deux problèmes majeurs concernant les encodings. Bien que Ruby soit unicode par défaut, pas mal de gems ne parlent pas l'UTF-8 par défaut...&lt;br /&gt;&lt;br /&gt;Premier soucis avec ERB qui compile les templates en utilisant ASCII-8BIT par défaut. Cela génère l'erreur suivante:&lt;br /&gt;&lt;pre&gt;ActionView::TemplateError (incompatible character encodings: ASCII-8BIT and UTF-8)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Il y a pas mal de discussions sur le sujet ici: &lt;a href="https://rails.lighthouseapp.com/projects/8994/tickets/2188-i18n-fails-with-multibyte-strings-in-ruby-19-similar-to-2038"&gt;https://rails.lighthouseapp.com/projects/8994/tickets/2188-i18n-fails-with-multibyte-strings-in-ruby-19-similar-to-2038&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Une solution &lt;i&gt;hacky&lt;/i&gt; à coup de monkey patch est de créer un fichier &lt;b&gt;lib/actionview_utf8&lt;/b&gt; comme ceci (c'est la ligne avec le source.force_encoding qui est rajoutée au code original):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;module ActionView&lt;br /&gt;  module Renderable #:nodoc:&lt;br /&gt;    private&lt;br /&gt;      def compile!(render_symbol, local_assigns)&lt;br /&gt;        locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join&lt;br /&gt;&lt;br /&gt;        source = &amp;lt;&amp;lt;-end_src&lt;br /&gt;          def #{render_symbol}(local_assigns)&lt;br /&gt;            old_output_buffer = output_buffer;#{locals_code};#{compiled_source}&lt;br /&gt;          ensure&lt;br /&gt;            self.output_buffer = old_output_buffer&lt;br /&gt;          end&lt;br /&gt;        end_src&lt;br /&gt;        source.force_encoding(Encoding::UTF_8) if source.respond_to?(:force_encoding)&lt;br /&gt;&lt;br /&gt;        begin&lt;br /&gt;          ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)&lt;br /&gt;        rescue Errno::ENOENT =&amp;gt; e&lt;br /&gt;          raise e # Missing template file, re-raise for Base to rescue&lt;br /&gt;        rescue Exception =&amp;gt; e # errors from template code&lt;br /&gt;          if logger = defined?(ActionController) &amp;amp;&amp;amp; Base.logger&lt;br /&gt;            logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"&lt;br /&gt;            logger.debug "Function body: #{source}"&lt;br /&gt;            logger.debug "Backtrace: #{e.backtrace.join("\n")}"&lt;br /&gt;          end&lt;br /&gt;&lt;br /&gt;          raise ActionView::TemplateError.new(self, {}, e)&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;puis d'ajouter les lignes suivantes à &lt;b&gt;environnement.rb&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Rails::Initializer.run do |config|&lt;br /&gt;  ...&lt;br /&gt;  config.after_initialize do &lt;br /&gt;    require 'lib/actionview_utf8'&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ensuite je suis tombé sur le problème des adaptateurs aux bases de données qui retournent de l'ASCI-8BIT au lieu d'UTF-8 à la lecture des données. Une solution du même acabit est décrite ici: &lt;a href="http://gnuu.org/2009/11/06/ruby19-rails-mysql-utf8/"&gt;http://gnuu.org/2009/11/06/ruby19-rails-mysql-utf8/&lt;/a&gt;. Ceci dit ça ne fonctionne qu'avec MySql.&lt;br /&gt;&lt;br /&gt;Donc on créé le fichier &lt;b&gt;lib/mysql_utf8&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;require "mysql"&lt;br /&gt;&lt;br /&gt;class Mysql::Result&lt;br /&gt;  def encode(value, encoding = "utf-8")&lt;br /&gt;    String === value ? value.force_encoding(encoding) : value&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def each_utf8(&amp;amp;block)&lt;br /&gt;    each_orig do |row|&lt;br /&gt;      yield row.map {|col| encode(col) }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  alias each_orig each&lt;br /&gt;  alias each each_utf8&lt;br /&gt;&lt;br /&gt;  def each_hash_utf8(&amp;amp;block)&lt;br /&gt;    each_hash_orig do |row|&lt;br /&gt;      row.each {|k, v| row[k] = encode(v) }&lt;br /&gt;      yield(row)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  alias each_hash_orig each_hash&lt;br /&gt;  alias each_hash each_hash_utf8&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et pour &lt;b&gt;environnement.rb&lt;/b&gt; on se retrouve avec:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Rails::Initializer.run do |config|&lt;br /&gt;  ...&lt;br /&gt;  config.after_initialize do &lt;br /&gt;    require 'lib/actionview_utf8'&lt;br /&gt;    require 'lib/mysql_utf8'&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ouf, tous les tests passent...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1329036077393668539?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1329036077393668539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/migrer-vers-ruby-19.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1329036077393668539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1329036077393668539'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/migrer-vers-ruby-19.html' title='Migrer vers ruby 1.9'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8564061417160110643</id><published>2009-12-29T17:02:00.005+01:00</published><updated>2009-12-29T17:12:47.753+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Zend Framework avec lighthttpd</title><content type='html'>Je vais travailler sur un projet en PHP avec Zend Framework. Venant du monde Rails, on s'habitue assez vite à lancer son serveur web dans un terminal, voir plusieurs en parallèle sans avoir à manipuler la configuration d'un Apache qui tourne sous un autre compte utilisateur.&lt;br /&gt;&lt;br /&gt;En PHP je n'ai pas trouvé d'équivalent à Webrick ou Mongrel intégré à ZF, mais je m'en suis finalement sorti avec lighthttpd.&lt;br /&gt;&lt;br /&gt;A la racine du projet ZF, créez un &lt;b&gt;lighthttpd.conf&lt;/b&gt; dans lequel vous configurez entre autres:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;le port 3000 (on ne change pas de vieilles habitudes :)&lt;/li&gt;&lt;li&gt;l'URL rewriting pour que toutes les requêtes passent par index.php (l'équivalent du .htaccess tel que créé par ZF)&lt;/li&gt;&lt;li&gt;l'accès à votre application via FastCGI&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;ce qui donne quelque chose comme suit:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;server.document-root = env.PWD + "/public"&lt;br /&gt;server.port = 3000&lt;br /&gt;&lt;br /&gt;server.modules = (&lt;br /&gt;  "mod_rewrite",&lt;br /&gt;  "mod_fastcgi"&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;mimetype.assign = (&lt;br /&gt;  ".html" =&amp;gt; "text/html", &lt;br /&gt;  ".txt" =&amp;gt; "text/plain",&lt;br /&gt;  ".jpg" =&amp;gt; "image/jpeg",&lt;br /&gt;  ".png" =&amp;gt; "image/png" &lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;url.rewrite-once = (&lt;br /&gt;  ".*\?(.*)$" =&amp;gt; "/index.php?$1",&lt;br /&gt;  ".*\.(js|ico|gif|jpg|png|css)$" =&amp;gt; "$0",&lt;br /&gt;  "" =&amp;gt; "/index.php"&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )&lt;br /&gt;&lt;br /&gt;server.indexfiles = ( "index.php" )&lt;br /&gt;&lt;br /&gt;fastcgi.server = (&lt;br /&gt;  ".php" =&amp;gt;&lt;br /&gt;    ( "localhost" =&amp;gt;&lt;br /&gt;      (&lt;br /&gt;        "socket" =&amp;gt; "/tmp/php-fastcgi.socket",&lt;br /&gt;        "bin-path" =&amp;gt; "/usr/bin/php-cgi"&lt;br /&gt;      )&lt;br /&gt;    )&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ensuite un petit script bash pour nous faciliter le lancement du serveur et l'initialisation de l'environnement ZF, par exemple &lt;b&gt;start_zf.sh&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;export APPLICATION_ENV=development&lt;br /&gt;lighttpd -f lighthttpd.conf -D&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;N'oubliez pas de rendre le script exécutable:&lt;br /&gt;&lt;pre&gt;chmod +x start_zf.sh&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;puis lancez l'accès à votre projet ZF:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;./start_zf.sh&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si tout va bien le site est accessible sur &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8564061417160110643?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8564061417160110643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/zend-framework-avec-lighthttpd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8564061417160110643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8564061417160110643'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/zend-framework-avec-lighthttpd.html' title='Zend Framework avec lighthttpd'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8768207443938570936</id><published>2009-12-18T23:57:00.001+01:00</published><updated>2009-12-19T21:41:20.723+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>SimpleWebDoc et yUML</title><content type='html'>Je viens d'interfacer &lt;a href="http://magaloma.blogspot.com/2009/12/simplewebdoc-sur-squeaksource.html"&gt;SimpleWebDoc&lt;/a&gt;&amp;nbsp;et &lt;a href="http://yuml.me/"&gt;yUML&lt;/a&gt;&amp;nbsp;pour générer les diagrammes d'héritage entre les classes d'un même package. Le tout à un clic de souris.&lt;br /&gt;&lt;br /&gt;Le package Tests:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SyZB2xcpVgI/AAAAAAAAAac/O6wh8FjXATg/s1600-h/5c661f19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SyZB2xcpVgI/AAAAAAAAAac/O6wh8FjXATg/s400/5c661f19.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Le package Kernel:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/Syy4_YCJK_I/AAAAAAAAAa8/Rn6iJUdQ1os/s1600-h/kernel3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="640" src="http://1.bp.blogspot.com/_JLK6EDfW4so/Syy4_YCJK_I/AAAAAAAAAa8/Rn6iJUdQ1os/s640/kernel3.png" width="608" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8768207443938570936?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8768207443938570936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/simplewebdoc-et-yuml.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8768207443938570936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8768207443938570936'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/simplewebdoc-et-yuml.html' title='SimpleWebDoc et yUML'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JLK6EDfW4so/SyZB2xcpVgI/AAAAAAAAAac/O6wh8FjXATg/s72-c/5c661f19.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1378112272944936310</id><published>2009-12-12T14:52:00.002+01:00</published><updated>2009-12-13T14:54:06.738+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>SimpleWebDoc sur SqueakSource</title><content type='html'>Suite à mon billet précédent, &lt;a href="http://magaloma.blogspot.com/2009/12/voyage-dans-pharo.html"&gt;Pharo et documentation des packages&lt;/a&gt;, j'ai continué a explorer Pharo pour ressortir les méthodes des classes et les commentaires contenus en en-tête des méthodes.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SyOYy6hgjMI/AAAAAAAAAZg/Fimm3bch02g/s1600-h/swd1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SyOYy6hgjMI/AAAAAAAAAZg/Fimm3bch02g/s200/swd1.png" /&gt;&lt;/a&gt;J'ai mis le package &lt;a href="http://www.squeaksource.com/SimpleWebDoc.html"&gt;SimpleWebDoc sur SqueakSource&lt;/a&gt;. Cela me donne l'occasion d'expliquer comment charger un package depuis Monticello. Tout d'abord lancez le &lt;b&gt;Monticello Browser&lt;/b&gt; depuis le menu &lt;b&gt;World.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;Cela ouvre une fenêtre dans laquelle on voit les packages installés dans le panneau de gauche, les repository (dépôts) dans le panneau de droite. L'étape suivante est d'ajouter le repository où se trouve &lt;b&gt;SimpleWebDoc&lt;/b&gt; dans la liste de ceux enregistrés dans &lt;b&gt;Monticello&lt;/b&gt;. Cliquez sur le bouton &lt;b&gt;+Repository&lt;/b&gt; et sélectionnez &lt;b&gt;HTTP&lt;/b&gt; (Note: un repository Monticello est un simple répertoire local ou distant où se trouve les packages zippés, avec l'extension .mcz).&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYy-JexqI/AAAAAAAAAZk/piFbg8Xyk2w/s1600-h/swd2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="146" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYy-JexqI/AAAAAAAAAZk/piFbg8Xyk2w/s320/swd2.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Saisissez ensuite l'emplacement du repository, ici:&amp;nbsp;&lt;span style="font-family: monospace; font-size: 11px; white-space: pre;"&gt;http://www.squeaksource.com/SimpleWebDoc&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SyOYzMZ8_5I/AAAAAAAAAZo/cNbuQHaWo9s/s1600-h/swd3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SyOYzMZ8_5I/AAAAAAAAAZo/cNbuQHaWo9s/s320/swd3.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Le repository ajouté, sélectionnez-le et cliquez &lt;b&gt;Open&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYzHGgV5I/AAAAAAAAAZs/KP0xCcVrFgY/s1600-h/swd4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYzHGgV5I/AAAAAAAAAZs/KP0xCcVrFgY/s320/swd4.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Une fenêtre s'ouvre listant tous les packages présents dans le repository (panneau de gauche) et la liste des versions disponibles (panneau de droite), la plus récente en tête. Sélectionnez la version la plus récente et cliquez sur &lt;b&gt;Load&lt;/b&gt; pour charger le package &lt;b&gt;SimpleWebDoc&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYzbzP1FI/AAAAAAAAAZw/-hQMwb1PB_0/s1600-h/swd5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="291" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SyOYzbzP1FI/AAAAAAAAAZw/-hQMwb1PB_0/s320/swd5.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;SWDocView&lt;/b&gt; est automatique enregistré comme application &lt;b&gt;Seaside&lt;/b&gt; au chargement. Si vous regardez la méthode de classe initialize de &lt;b&gt;SWDocView&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;initialize&lt;br /&gt;&amp;nbsp;&amp;nbsp;self registerAsApplication: 'view-doc'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Une classe est un objet, et comme tout objet son instanciation appelle &lt;b&gt;initialize&lt;/b&gt;. En chargeant le package &lt;b&gt;SimpleWebDoc&lt;/b&gt;, on instancie la meta-classe &lt;b&gt;SWDocView&lt;/b&gt; (elle même instance de &lt;b&gt;MetaClass&lt;/b&gt;). &lt;b&gt;SWDocView&lt;/b&gt; s'enregistre alors comme application nommée &lt;b&gt;view-doc&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;Vous pouvez donc ouvrir votre navigateur web préféré et aller à l'adresse:&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;&lt;a href="http://localhost:8080/seaside/view-doc"&gt;http://localhost:8080/seaside/view-doc&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Sx9-zJ0xQiI/AAAAAAAAAT8/Npaek96phRI/s1600-h/simple_web_doc1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Sx9-zJ0xQiI/AAAAAAAAAT8/Npaek96phRI/s400/simple_web_doc1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Les méthodes de classe sont en italique et soulignées:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SyOUCEJgaBI/AAAAAAAAAZY/M1JGTtqElLg/s1600-h/simple_web_doc3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SyOUCEJgaBI/AAAAAAAAAZY/M1JGTtqElLg/s400/simple_web_doc3.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1378112272944936310?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1378112272944936310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/simplewebdoc-sur-squeaksource.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1378112272944936310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1378112272944936310'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/simplewebdoc-sur-squeaksource.html' title='SimpleWebDoc sur SqueakSource'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/SyOYy6hgjMI/AAAAAAAAAZg/Fimm3bch02g/s72-c/swd1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3182786061252621605</id><published>2009-12-10T10:18:00.001+01:00</published><updated>2009-12-13T14:54:31.087+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>The Smalltalk way to learn Smalltalk</title><content type='html'>Pour apprendre un nouveau langage, avant de me jeter sur le compilateur / interpréteur et tenter d'écrire un (mauvais) programme, j'achète un ou deux livres bien perçus pour m'imprégner de la philosophie et objectifs du langage. Je lis quelques programmes.&lt;br /&gt;&lt;br /&gt;Pour Smalltalk je crois avoir commis une erreur et j'ai mis du temps à tout simplement "rentrer dedans", bien plus que tous les langages profondément objets étudiés auparavant (dont Ruby ).&lt;br /&gt;&lt;i&gt;&lt;br /&gt;I'm still amazed by how many people think they can grok Smalltalk by seeing syntax examples. Smalltalk isn't its syntax, it's its environment. Smalltalk is a living world of running objects, there are no files, no applications, just what's running. To understand Smalltalk, you have to either actually use it for a while, or have a seasoned Smalltalker demonstrate it to you. Reading sample code just won't cut it.&lt;br /&gt;&lt;/i&gt;(Ramon Leon, &lt;a href="http://onsmalltalk.com/why-smalltalk"&gt;Why Smalltalk&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;C'est un vrai conseil ;)&lt;br /&gt;&lt;br /&gt;Et je comprends maintenant ceci:&lt;i&gt; Object oriented programming means something entirely different to a Smalltalker than to someone doing OO in another language.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Au passage, le blog de Ramon Leon&amp;nbsp;&lt;a href="http://onsmalltalk.com/"&gt;OnSmalltalk&lt;/a&gt;&amp;nbsp;est une vrai mine d'or, articles complets, bien écrits, profonds et les commentaires n'en valent pas moins.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3182786061252621605?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3182786061252621605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/smalltalk-way-to-learn-smalltalk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3182786061252621605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3182786061252621605'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/smalltalk-way-to-learn-smalltalk.html' title='The Smalltalk way to learn Smalltalk'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2332174223948161461</id><published>2009-12-06T22:51:00.046+01:00</published><updated>2009-12-13T14:54:46.131+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='seaside'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo et documentation des packages</title><content type='html'>Voici quelques expérimentations faites alors que je voulais explorer les documentations contenues dans Pharo. Dans ce billet nous verrons comment ressortir les commentaires des classes d'un package donné. Puis je décrirai le codage d'une application Seaside minimaliste pour visualiser cette documentation depuis un navigateur Web.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Voyage au centre du package&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Les classes sont regroupées dans des packages gérées par &lt;b&gt;Monticello&lt;/b&gt;. On peut faire l'analogie avec les &lt;b&gt;gems&lt;/b&gt; du monde &lt;b&gt;Ruby&lt;/b&gt; ou les gestionnaires de paquets des distributions Linux.&lt;br /&gt;&lt;br /&gt;Chaque package étant une instance de la classe &lt;b&gt;MCPackage&lt;/b&gt;, on peut en sortir la liste. Ouvrez un &lt;b&gt;Workspace&lt;/b&gt; et affichez le résultat du code suivant (via click-droit, &lt;b&gt;print it&lt;/b&gt;)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;MCPackage allInstances&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/Sxvh5QM8hdI/AAAAAAAAARg/C5RjhJILY9g/s1600-h/mc1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/Sxvh5QM8hdI/AAAAAAAAARg/C5RjhJILY9g/s320/mc1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Cela devrait vous retourner une longue liste de packages:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/Sxvh8gEeUMI/AAAAAAAAARk/QHUThWpjz8Q/s1600-h/mc2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/Sxvh8gEeUMI/AAAAAAAAARk/QHUThWpjz8Q/s400/mc2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Comptons en le nombre en évaluant la taille du tableau retourné:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;MCPackage allInstances size.&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Sur mon image je trouve 155 packages.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Chaque package a un nom via l'accesseur &lt;b&gt;name&lt;/b&gt;. On peut ainsi en sortir la liste:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;MCPackage allInstances collect: [:p| p name]&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;En triant par ordre alphabétique:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;(MCPackage allInstances collect: [:p| p name]) sort&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Affichons le tout dans un &lt;b&gt;Transcript&lt;/b&gt;. Ouvrez-le via le menu &lt;b&gt;World &amp;gt; Tools &amp;gt; Transcript&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SxvlIdBqF0I/AAAAAAAAARo/GM8AmgxNgHI/s1600-h/mc3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SxvlIdBqF0I/AAAAAAAAARo/GM8AmgxNgHI/s320/mc3.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Puis évaluez le code suivant dans le &lt;b&gt;Workspace&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;|packageNames|&lt;br /&gt;packageNames := MCPackage allInstances collect: [:p| p name].&lt;br /&gt;packageNames sort do:[:name| Transcript show:name; cr]&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SxvlPvHwUGI/AAAAAAAAARs/SHqPBYLWzp4/s1600-h/mc4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SxvlPvHwUGI/AAAAAAAAARs/SHqPBYLWzp4/s400/mc4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Les objects &lt;b&gt;PackageInfo&lt;/b&gt; référencent toutes les classes d'un package. Chaque objet &lt;b&gt;MCPackage&lt;/b&gt; est associé à un objet &lt;b&gt;PackageInfo&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Prenons par exemple le premier package et affichons toutes les classes qu'il contient via l'accesseur &lt;b&gt;classes&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;MCPackage allInstances first packageInfo classes&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SxvrMNHjleI/AAAAAAAAARw/sEyx3c1tpqc/s1600-h/mc5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SxvrMNHjleI/AAAAAAAAARw/sEyx3c1tpqc/s400/mc5.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Pour extraire toutes les classes d'un package donné, une petite sélection fait l'affaire.&amp;nbsp;Le code suivant renvoie toutes les classes du package &lt;b&gt;System-Tools&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;|systemTools|&lt;br /&gt;systemTools := (MCPackage allInstances select: [:p| p name = 'System-Tools']) first.&lt;br /&gt;systemTools packageInfo classes.&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Sxy-W8VHANI/AAAAAAAAASg/UYoQRQHcdJs/s1600-h/correc1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Sxy-W8VHANI/AAAAAAAAASg/UYoQRQHcdJs/s640/correc1.png" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;On peut accéder au commentaire de chaque classe via l'accesseur &lt;b&gt;comment&lt;/b&gt;. Affichons le commentaire de la classe &lt;b&gt;SpaceTally&lt;/b&gt; dans un &lt;b&gt;Transcript&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;Transcript show:SpaceTally comment&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/Sxvtm1dU6hI/AAAAAAAAAR4/CHl-jthpZF4/s1600-h/mc7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="http://1.bp.blogspot.com/_JLK6EDfW4so/Sxvtm1dU6hI/AAAAAAAAAR4/CHl-jthpZF4/s400/mc7.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;De là à afficher toutes les classes d'un package avec leurs commentaires, il n'y a qu'un pas:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="font-family: monospace; white-space: pre;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;|systemTools|&lt;br /&gt;systemTools := (MCPackage allInstances select: [:p| p name = 'System-Tools']) first.&lt;br /&gt;systemTools packageInfo classes do:[:c|&lt;br /&gt;    Transcript show: '***',c name; cr.&lt;br /&gt;    Transcript show: c comment; cr; cr]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/Sxy_AGtnWjI/AAAAAAAAASk/IX6DZurwqS4/s1600-h/correc2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="260" src="http://2.bp.blogspot.com/_JLK6EDfW4so/Sxy_AGtnWjI/AAAAAAAAASk/IX6DZurwqS4/s400/correc2.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;2. Application Seaside pour consulter les commentaires&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Rajoutons maintenant un peu de &lt;i&gt;bling-bling&lt;/i&gt; à tout ça. Un Transcript c'est bien, une page web c'est plus rigolo.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Créons le package &lt;b&gt;SimpleWebDoc&lt;/b&gt; qui contiendra une application Seaside minimaliste permettant de parcourir les commentaires des classes des packages.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Quelques précisions pour commencer. J'utilise ici l'image&lt;a href="http://gforge.inria.fr/frs/download.php/24830/pharo1.0-10496-rc1web09.11.4.zip"&gt; Pharo 1.0 RC1 09.11.04 avec Seaside et Pier&lt;/a&gt;, téléchargeable sur le site de &lt;a href="http://pharo-project.org/pharo-download"&gt;Pharo&lt;/a&gt; à l'heure où j'écris ces lignes.&lt;br /&gt;&lt;/div&gt;Le navigateur de classes utilisé par défaut est &lt;b&gt;Browser&lt;/b&gt; (pour des raisons de performances et stabilité), or je vais utiliser le&lt;b&gt; Package Browser&lt;/b&gt;. Pour changer le navigateur par défaut, ouvrez le navigateur de classe et dans le menu de la fenêtre sélectionnez &lt;b&gt;Choose new default Browser&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SxulfHU0IuI/AAAAAAAAARU/8juTrl6lteQ/s1600-h/browser1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SxulfHU0IuI/AAAAAAAAARU/8juTrl6lteQ/s400/browser1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Ici sélectionnez &lt;b&gt;O2PackageBrowserAdaptator&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SxulfHU0IuI/AAAAAAAAARU/8juTrl6lteQ/s1600-h/browser1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SxulfV_mFZI/AAAAAAAAARY/v2JWx9f-0PY/s1600-h/browser2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="145" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SxulfV_mFZI/AAAAAAAAARY/v2JWx9f-0PY/s320/browser2.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Lancez un nouveau navigateur de classe, vous devriez obtenir le &lt;b&gt;Package Browser&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/Sxulfayg5XI/AAAAAAAAARc/ob50Tkf3B8o/s1600-h/browser3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/Sxulfayg5XI/AAAAAAAAARc/ob50Tkf3B8o/s640/browser3.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Ajoutons maintenant le package&amp;nbsp;&lt;b&gt;SimpleWebDoc&lt;/b&gt;: click droit, &lt;b&gt;create package.&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKF4_y70I/AAAAAAAAASE/fzKN7RZsly0/s1600-h/doc1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKF4_y70I/AAAAAAAAASE/fzKN7RZsly0/s320/doc1.png" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKFzzyqJI/AAAAAAAAASI/24wjJxehapY/s1600-h/doc2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKFzzyqJI/AAAAAAAAASI/24wjJxehapY/s320/doc2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;Nouveau clic droit sur le package et choisissez &lt;b&gt;various &amp;gt; add to smart groups&lt;/b&gt; pour le rajouter dans l'onglet &lt;b&gt;groups&lt;/b&gt; du Package Browser.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SxwKN_jBBgI/AAAAAAAAASM/79LpvuJDEBs/s1600-h/doc3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="172" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SxwKN_jBBgI/AAAAAAAAASM/79LpvuJDEBs/s320/doc3.png" width="320" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKODpKGKI/AAAAAAAAASQ/cqSEWZf9yd8/s1600-h/doc4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SxwKODpKGKI/AAAAAAAAASQ/cqSEWZf9yd8/s1600/doc4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Créer application Seaside nécessite d'hériter &lt;b&gt;WAComponent&lt;/b&gt; et de l'enregistrer comme application. Voici la déclaration de la classe &lt;b&gt;SWDocView&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;WAComponent subclass: #SWDocView&lt;br /&gt; instanceVariableNames: ''&lt;br /&gt; classVariableNames: ''&lt;br /&gt; poolDictionaries: ''&lt;br /&gt; category: 'SimpleWebDoc'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pour afficher un composant, Seaside lui envoie le messager &lt;b&gt;renderContentOn:&lt;/b&gt; en lui passant un objet &lt;b&gt;WARenderCanvas&lt;/b&gt; sur lequel &lt;i&gt;dessiner&lt;/i&gt; notre page web (&lt;a href="http://book.seaside.st/book/fundamentals/rendering-components/rendering-hello-world"&gt;en utilisant des &lt;i&gt;brosses/brush&lt;/i&gt;&lt;/a&gt;). Nous allons ici construire un formulaire affichant tous les packages:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderContentOn: html&lt;br /&gt; html form: [&lt;br /&gt;  html select &lt;br /&gt;   list: (MCPackage allInstances collect: [:p| p name])&lt;br /&gt; ].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Améliorons la lisibilité du code en créant &lt;b&gt;allPackageNames&lt;/b&gt; qui retourne les noms de tous les packages, dans l'ordre alphabétique:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;allPackageNames &lt;br /&gt; ^ (MCPackage allInstances collect: [:p| p name]) sort&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et ajustons &lt;b&gt;renderContentOn:&lt;/b&gt; en conséquence:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderContentOn: html&lt;br /&gt; html form: [&lt;br /&gt;  html select &lt;br /&gt;   list: self allPackageNames;&lt;br /&gt; ].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Testons ce que donne ce premier code. Déclarez le composant &lt;b&gt;SWDocView&lt;/b&gt; comme application en évaluant ce qui suit dans un &lt;b&gt;Workspace&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SWDocView registerAsApplication: 'simple-web-doc'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pointez ensuite votre navigateur Web à l'adresse:&lt;a href="http://localhost:8080/seaside/simple-web-doc"&gt; http://localhost:8080/seaside/simple-web-doc&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/Sxwhoy36ojI/AAAAAAAAASY/V6Mihx1H_ns/s1600-h/web2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="204" src="http://4.bp.blogspot.com/_JLK6EDfW4so/Sxwhoy36ojI/AAAAAAAAASY/V6Mihx1H_ns/s320/web2.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Ajoutons un bouton pour valider le formulaire:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;renderContentOn: html&lt;br /&gt; html form: [&lt;br /&gt;  html select &lt;br /&gt;   list: self allPackageNames;&lt;br /&gt;  html submitButton with:'show'&lt;br /&gt; ].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Maintenant la partie intéressante: lorsqu'on valide le formulaire, on affiche la documentation du package. Pour cela nous stockons le nom sélectionné dans la variable d'instance &lt;b&gt;selectedPackage&lt;/b&gt; en utilisant le callback de la brosse &lt;b&gt;WASelectTag&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderContentOn: html&lt;br /&gt; html form: [&lt;br /&gt;  html select &lt;br /&gt;   list: self allPackageNames;&lt;br /&gt;   callback: [:value| selectedPackage := value].&lt;br /&gt;  html submitButton with:'show'&lt;br /&gt; ].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si &lt;b&gt;selectedPackage&lt;/b&gt; est définit, affichons la documentation:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderContentOn: html&lt;br /&gt; html form: [&lt;br /&gt;  html select &lt;br /&gt;   list: self allPackageNames;&lt;br /&gt;   callback: [:value| selectedPackage := value].&lt;br /&gt;  html submitButton with:'show'&lt;br /&gt; ].&lt;br /&gt; &lt;br /&gt; selectedPackage ifNotNil: [&lt;br /&gt;   self renderPackage:selectedPackage On:html.&lt;br /&gt; ]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La méthode&lt;b&gt; SWDocView&amp;gt;&amp;gt;renderPackage:On:&lt;/b&gt; qui reprends l'exemple précédent:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderPackage:packageName On:html&lt;br /&gt;  |package|&lt;br /&gt;  package := (MCPackage allInstances select: [:p| p name = packageName]) first.&lt;br /&gt;  package packageInfo classes do: [:c| &lt;br /&gt;    html heading level:2; with: c name.&lt;br /&gt;      html break.&lt;br /&gt;      html text: c comment.&lt;br /&gt;      html horizontalRule.&lt;br /&gt;  ]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et voici le résultat:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Sxwmsb7_5dI/AAAAAAAAASc/0oVXITrjUoQ/s1600-h/web3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="265" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Sxwmsb7_5dI/AAAAAAAAASc/0oVXITrjUoQ/s400/web3.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Amenons un peu de lisibilté en écrivant une méthode qui retourne toutes les classes d'un package:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;classesOfPackageNamed: packageName&lt;br /&gt; |package|&lt;br /&gt; package := (MCPackage allInstances select: [:p| p name = packageName]) first.&lt;br /&gt; ^ package packageInfo classes&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;d'où une mise à jour de &lt;b&gt;renderPackage:On:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;renderPackage:packageName On:html&lt;br /&gt; (self classesOfPackageNamed:packageName) do: [:c| &lt;br /&gt;  html heading level:2; with: c name.&lt;br /&gt;  html break.&lt;br /&gt;  html text: c comment.&lt;br /&gt;  html horizontalRule.&lt;br /&gt;  ]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Bon nombre de choses sont à réaliser pour avoir une navigation correcte. Pour aller plus loin, consultez &lt;a href="http://book.seaside.st/"&gt;Dynamic Web Development with Seaside&lt;/a&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2332174223948161461?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2332174223948161461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/12/voyage-dans-pharo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2332174223948161461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2332174223948161461'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/12/voyage-dans-pharo.html' title='Pharo et documentation des packages'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JLK6EDfW4so/Sxvh5QM8hdI/AAAAAAAAARg/C5RjhJILY9g/s72-c/mc1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3067699215396937238</id><published>2009-11-16T18:59:00.002+01:00</published><updated>2009-11-16T19:06:14.769+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Minitube: youtube sans flash</title><content type='html'>Attention, killer app. en vue: &lt;a href="http://flavio.tordini.org/minitube"&gt;minitube&lt;/a&gt; permet de rechercher et visionner les vidéos de YouTube, le tout sans flash ! C'est rapide, simple et agréable à utiliser.&lt;div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JLK6EDfW4so/SwGUgf3uo4I/AAAAAAAAAQM/2A88FgUcGLs/s1600/minitube1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 86px;" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SwGUgf3uo4I/AAAAAAAAAQM/2A88FgUcGLs/s320/minitube1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5404764313853272962" /&gt;&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Minitube apporte l'originalité de lire les résultats de recherche les uns à la suite des autres, créant ainsi une sorte de programme TV sur demande.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SwGSZ_9QqqI/AAAAAAAAAQE/Txs1n-HdMUI/s1600/minitube.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SwGSZ_9QqqI/AAAAAAAAAQE/Txs1n-HdMUI/s400/minitube.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Sous ArchLinux, j'ai eu besoin d'installer:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;le backend xine pour phonon: &lt;b&gt;sudo pacman -S phonon-xine&lt;/b&gt;&lt;/li&gt;&lt;li&gt;minitube via AUR: &lt;b&gt;yaourt -S minitube&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3067699215396937238?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3067699215396937238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/11/minitube-youtube-sans-flash.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3067699215396937238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3067699215396937238'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/11/minitube-youtube-sans-flash.html' title='Minitube: youtube sans flash'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JLK6EDfW4so/SwGUgf3uo4I/AAAAAAAAAQM/2A88FgUcGLs/s72-c/minitube1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8697915257577027685</id><published>2009-11-03T22:22:00.002+01:00</published><updated>2009-11-04T07:24:19.794+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>L'outil n'est pas le problème...</title><content type='html'>(...ni forcément la solution)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.crisp.se/henrikkniberg/"&gt;Henrik Kniberg&lt;/a&gt; (gourou de l'agilité) utilise régulièrement cette image dans ses présentations:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SvCUSnTHO2I/AAAAAAAAAP0/o4XZRa0kpAU/s1600-h/tool.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SvCUSnTHO2I/AAAAAAAAAP0/o4XZRa0kpAU/s640/tool.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;Cela peut prêter à sourire, mais j'en ressort quelques règles:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;évaluez les outils avant d'en choisir un, vérifiez qu'il correspond bien à vos besoins&lt;/li&gt;&lt;li&gt;connaissez intimement votre outil avant de l'utiliser en production: lisez des livres, retours d'expérience, internet est là pour vous aider&lt;/li&gt;&lt;li&gt;sachez dans quels cas ne pas l'utiliser&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;J'ai vu des équipes choisir Ruby et Rails après des déboires en PHP, pensant que Rails "c'est mieux". Aucun test unitaire, aucun apprentissage des conventions,... bref, retour au spaghetti code et déboires. &lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;D'autres mettent en place un Subversion et pestent contre l'outil car ils se retrouvent constamment avec des conflits à chaque commit. Le fonctionnement par lock était mieux.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ne parlons pas de tous les projets bancals  en Visual Basic car VB, "c'est simple !".&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Contrairement aux slogans, l'informatique, la programmation sont de plus en plus complexes. Il est de la responsabilité du développeur de se former.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ruby, Python, Git, Scrum... sont des outils très puissants. "With great power, comes great responsibility": apprenez à maîtriser votre pouvoir !!&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8697915257577027685?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8697915257577027685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/11/loutil-nest-pas-le-probleme.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8697915257577027685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8697915257577027685'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/11/loutil-nest-pas-le-probleme.html' title='L&apos;outil n&apos;est pas le problème...'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JLK6EDfW4so/SvCUSnTHO2I/AAAAAAAAAP0/o4XZRa0kpAU/s72-c/tool.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2891041387184080587</id><published>2009-10-21T22:46:00.006+02:00</published><updated>2009-10-24T07:43:36.705+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='livres'/><title type='text'>Groupe de lecture en entreprise</title><content type='html'>J'apprends essentiellement par la lecture de (bons) livres: nouveaux langages, pratiques de développement, management... Or j'ai toujours éprouvé beaucoup de difficultés à faire lire ces mêmes livres à des collègues. Il semble de notoriété publique qu'un livre qui touche de près ou de loin à l'informatique est forcément difficile et ennuyeux à lire (pour être poli). Si de plus rédigé en anglais ...&lt;br /&gt;&lt;br /&gt;Pour se donner le courage d'étudier un livre ensemble, d'apprendre et d'échanger autour de nouvelles connaissances, nous avons formé il y a deux mois un groupe de lecture dans mon entreprise. Nous avons commencé à trois personnes et maintenant nous sommes cinq, je croise les doigts pour que ça continue !&lt;br /&gt;&lt;br /&gt;Voici la démarche que nous avons adopté:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Sélectionner un ouvrage:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;Choisir un domaine dans lequel le groupe veut acquérir de nouvelles connaissances.&lt;/i&gt;  Par exemple: Ruby, Python, Scrum, ...&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-weight: normal;"&gt;&lt;i&gt;Chercher et proposer une liste réduite de livres et/ou essais sur un thème donné.&lt;/i&gt; &lt;/span&gt;&lt;span class="Apple-style-span" style="font-weight: normal;"&gt;Se fier aux critiques des lecteurs pour avoir la certitude que le livre est bon. Attention aux mauvaises traductions: les budgets de traduction entre un Harry Potter et un livre d'informatique semblent très différents ;).&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-weight: normal;"&gt;Acheter les livres (ou le PDF et imprimer). Chacun doit avoir son exemplaire.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;2. Choisir une organisation:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;A quelle heure se rencontre t'on ? Combien de fois par semaine ? Quels jours ? Quelle est la durée de la session ?&lt;/i&gt;&lt;br /&gt;Dans notre cas, nous nous réunissons tous les Lundi et Jeudi à 12h, pendant 45 mn maximum.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Une personne doit être leader du groupe&lt;/i&gt; et s'assurer du respect de l'organisation (un Scrum Master du groupe de lecture en quelque sorte).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. La préparation de la prochaine rencontre:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;On choisit collectivement ce qui doit être lu&lt;/i&gt; pour la prochaine rencontre: les trois sections suivantes, le prochain chapitre.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Une personne doit être désignée  (à tour de rôle) pour faire le résumé de la partie à lire.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;b&gt;4: La rencontre:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Tout le monde se retrouve autour d'une table. La personne en tâche de faire le résumé s'exécute. L'objectif est d'échanger, débattre et clarifier chacun des points abordés.&lt;br /&gt;&lt;br /&gt;A la fin de la rencontre, retour au &lt;b&gt;point 3&lt;/b&gt; pour préparer la rencontre suivante.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nous avons commencé cette pratique par la lecture de l'essai &lt;a href="http://samizdat.mines.edu/howto/HowToBeAProgrammer.html"&gt;How to be a Programmer: A Short, Comprehensive, and Personal Summary&lt;/a&gt; de Robert L.Dead. Cela nous a permis de nous mettre en jambe avec un document court (quatre rencontres) et de valider notre fonctionnement.&lt;br /&gt;&lt;br /&gt;Nous sommes ensuite passés à &lt;a href="http://diveintopython.adrahon.org/toc/index.html"&gt;Plonger au cœur de Python&lt;/a&gt; (traduction de Dive Into Python de Mark Pilgrim) qui a l'avantage d'être ludique (et en français me diront certains).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2891041387184080587?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2891041387184080587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/10/groupe-de-lecture-en-entreprise.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2891041387184080587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2891041387184080587'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/10/groupe-de-lecture-en-entreprise.html' title='Groupe de lecture en entreprise'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8516872204677321004</id><published>2009-10-16T08:46:00.005+02:00</published><updated>2009-10-16T08:56:41.120+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Privoxy et NetworkManager</title><content type='html'>Je suis passé à &lt;b&gt;NetworkManager&lt;/b&gt; pour gérer les connexions réseaux sur mon portable et je trouve &lt;i&gt;enfin&lt;/i&gt; cet outil  fonctionnel et pratique. Seul petit soucis: &lt;b&gt;privoxy&lt;/b&gt; (un proxy que j'utilise comme bloqueur de pubs, ce qui donne l'avantage de fonctionner quel que soit le navigateur web utilisé) perdait un peu les pédales à chaque fois que &lt;b&gt;NetworkManager&lt;/b&gt; reconfigurait le réseau et j'étais obligé de le redémarrer.&lt;br /&gt;&lt;br /&gt;Mais je viens de découvrir que &lt;b&gt;NeworkManager&lt;/b&gt; peut exécuter des scripts à chaque reconfiguration réseau. Je dégaine mon éditeur et voici un script à placer dans &lt;b&gt;/etc/NetworkManager/dispatcher.d&lt;/b&gt; qui démarre ou arrête &lt;b&gt;privoxy&lt;/b&gt; selon que le réseau soit disponible ou non (attention, c'est pour &lt;b&gt;ArchLinux&lt;/b&gt;):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;LOGGER="/usr/bin/logger -s -p user.notice -t NetworkManagerDispatcher"&lt;br /&gt;PID=`pidof -o %PPID /usr/sbin/privoxy`&lt;br /&gt;&lt;br /&gt;if [ -n $1 ] &amp;amp;&amp;amp; [ $2 == "up" ]; then&lt;br /&gt;    if [ ! -z "$PID" ];  then&lt;br /&gt;        $LOGGER "Privoxy is running, restart"&lt;br /&gt;        /etc/rc.d/privoxy restart&lt;br /&gt;    else&lt;br /&gt;        $LOGGER "Privoxy is stopped, start"&lt;br /&gt;        /etc/rc.d/privoxy start&lt;br /&gt;    fi&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;if [ -n $1 ] &amp;amp;&amp;amp; [ $2 == "down" ]; then&lt;br /&gt;    if [ ! -z "$PID" ];  then&lt;br /&gt;        $LOGGER "Privoxy is running, stop"&lt;br /&gt;        /etc/rc.d/privoxy stop&lt;br /&gt;    fi&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;En prime vous aurez droit à quelques logs pour vérifier que ça fonctionne:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat /var/log/messages.log |grep "NetworkManagerDispatcher"&lt;br /&gt;...&lt;br /&gt;Oct 15 18:42:09 magalo NetworkManagerDispatcher: Privoxy is stopped, start&lt;br /&gt;Oct 15 18:42:11 magalo NetworkManagerDispatcher: Privoxy is running, stop&lt;br /&gt;Oct 15 18:42:15 magalo NetworkManagerDispatcher: Privoxy is stopped, start&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8516872204677321004?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8516872204677321004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/10/privoxy-et-networkmanager.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8516872204677321004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8516872204677321004'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/10/privoxy-et-networkmanager.html' title='Privoxy et NetworkManager'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3060255340765616337</id><published>2009-10-02T08:58:00.001+02:00</published><updated>2009-10-02T08:58:36.451+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Embarquer Webkit</title><content type='html'>Pour une application embarquée j'avais besoin vite fait d'un navigateur web sans fioritures et support CSS + Javascript. Finalement c'est tout simple avec Python et Webkit.&lt;br /&gt;&lt;br /&gt;Avec &lt;a href="http://code.google.com/p/pywebkitgtk/"&gt;pywebkitgtk&lt;/a&gt;&amp;nbsp;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import gtk &lt;br /&gt;import webkit &lt;br /&gt;&lt;br /&gt;view = webkit.WebView() &lt;br /&gt;&lt;br /&gt;sw = gtk.ScrolledWindow() &lt;br /&gt;sw.add(view) &lt;br /&gt;&lt;br /&gt;win = gtk.Window(gtk.WINDOW_TOPLEVEL) &lt;br /&gt;win.add(sw) &lt;br /&gt;win.show_all() &lt;br /&gt;&lt;br /&gt;view.open('http://acid3.acidtests.org/') &lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;La mếme chose avec pyQt:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import sys&lt;br /&gt;from PyQt4.QtCore import *&lt;br /&gt;from PyQt4.QtGui import *&lt;br /&gt;from PyQt4.QtWebKit import *&lt;br /&gt;&lt;br /&gt;app = QApplication(sys.argv)&lt;br /&gt;web = QWebView()&lt;br /&gt;&lt;br /&gt;sa = QScrollArea()&lt;br /&gt;sa.setWidget(web)&lt;br /&gt;sa.show()&lt;br /&gt;&lt;br /&gt;web.load(QUrl('http://acid3.acidtests.org/'))&lt;br /&gt;&lt;br /&gt;sys.exit(app.exec_())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SsWkbBecopI/AAAAAAAAAPU/4cuo-FrsYVc/s1600-h/webkit.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SsWkbBecopI/AAAAAAAAAPU/4cuo-FrsYVc/s320/webkit.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3060255340765616337?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3060255340765616337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/10/embarquer-webkit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3060255340765616337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3060255340765616337'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/10/embarquer-webkit.html' title='Embarquer Webkit'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JLK6EDfW4so/SsWkbBecopI/AAAAAAAAAPU/4cuo-FrsYVc/s72-c/webkit.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2796492768287761678</id><published>2009-09-17T22:21:00.001+02:00</published><updated>2009-09-17T22:21:32.665+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Ruby snippets pour Emacs</title><content type='html'>Je viens de découvrir une excellente bibliothèque de gestion de snippets pour Emacs:  &lt;a href="http://code.google.com/p/yasnippet/"&gt;yasnippet&lt;/a&gt;. Rapide à mettre en oeuvre,&lt;a href="http://yasnippet.googlecode.com/svn/trunk/doc/index.html"&gt; une bonne documentation&lt;/a&gt; et plein de snippets disponibles !!&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pour Ruby: &lt;a href="http://github.com/Chrononaut/yasnippet-ruby-mode"&gt;http://github.com/Chrononaut/yasnippet-ruby-mode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Pour Rails: &lt;a href="http://github.com/eschulte/yasnippets-rails/"&gt;http://github.com/eschulte/yasnippets-rails/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Pour Shoulda: &lt;a href="http://github.com/rwc9u/yasnippets-shoulda"&gt;http://github.com/rwc9u/yasnippets-shoulda&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2796492768287761678?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2796492768287761678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/09/ruby-snippets-pour-emacs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2796492768287761678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2796492768287761678'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/09/ruby-snippets-pour-emacs.html' title='Ruby snippets pour Emacs'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8577657228786809638</id><published>2009-09-12T22:05:00.001+02:00</published><updated>2009-09-12T22:08:26.037+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='livres'/><title type='text'>Les lectures de l'été</title><content type='html'>Je me suis fait une nouvelle petite cure &lt;span style="font-style: italic;"&gt;pragmatique&lt;/span&gt; cet été. Les ouvrages du &lt;a href="http://pragprog.com/"&gt;Pragmatic Bookshelf&lt;/a&gt; me plaisent toujours autant: bien construits, écriture fluide. Le soucis c'est que le livre finit on s'aperçoit qu'on a encore bien plus à apprendre qu'avant de commencer...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://assets3.pragprog.com/images/covers/190x228/cfcar2.jpg?1236205293" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="96" src="http://assets3.pragprog.com/images/covers/190x228/cfcar2.jpg?1236205293" width="63" /&gt;&lt;/a&gt;&lt;/div&gt; &lt;a href="http://pragprog.com/titles/cfcar2/the-passionate-programmer"&gt;The Passionate Programmer: Creating a Remakable Career in Software Development &lt;/a&gt;de Chad Fowler adopte une structure similaire à &lt;a href="http://pragprog.com/titles/tpp/the-pragmatic-programmer"&gt;Pragmatic Programmers: From Journeyman To Master&lt;/a&gt;. Chacune des 53 sections détaille une pratique, une attitude, une réflexion pour améliorer la prise en main de notre carrière de développeur et être reconnu par les différentes "tribus" (programmeurs, managers, clients, ...)  avec lesquelles on se doit de communiquer. Le livre est très intéressant, convaincant, avec quelques bonnes anecdotes.&lt;br /&gt;J'ai surtout aimé les parallèles avec l'apprentissage d'un instrument de musique et les habitudes, aspirations des musiciens. En musique on se doit de pratiquer régulièrement notre instrument et de travailler aux limites de ses possibilités pour progresser. L'amélioration de ses compétences de programmeur nécessite aussi d'adopter un rythme d'entraînement pour pratiquer sa technique.&lt;br /&gt;Apprendre un instrument de musique différent de celui qu'on joue constitue un bon moyen de s'améliorer. Par exemple, un saxophoniste gagnera beaucoup à apprendre la basse ou la batterie, un bassiste le piano, ... Il est en effet plus rapide d'acquérir un jeu rythmique avec la basse ou la batterie, un jeu polyphonique avec le piano ou la guitare, ...  De même, un développeur C peut apprendre Python, un développeur Java le Lisp, Erlang, ... pour découvrir des techniques plus facilement qu'avec le langage qu'il connaît. Si vous voulez comprendre la méta-programmation, utilisez Ruby. Pour l'objet prenez Smalltalk....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://assets1.pragprog.com/images/covers/190x228/tsgit.jpg?1236205329" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="96" src="http://assets1.pragprog.com/images/covers/190x228/tsgit.jpg?1236205329" width="79" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a href="http://pragprog.com/titles/tsgit/pragmatic-version-control-using-git"&gt;Pragmatic Version Control Using Git&lt;/a&gt;  de Travis Swicegood fait partie du &lt;i&gt;Pragmatic Starter Kit&lt;/i&gt; (contrôle de version, test unitaires, automatisation) qui constitue la boîte à outil de base du développeur pragmatique ;). Travis Swicegood présente les différents types de VCS puis détaille l'utilisation de Git jusqu'à des concepts avancés. Le livre est bien structuré, les exemples manquent parfois un peu de profondeur à mon goût. J'ai bien aimé la présentation de l'interface Subversion de Git (git svn) qui permet de synchroniser son dépôt Git local avec un dépôt Git central. Cela permet de profiter des apports de Git même si l'équipe utilise Subversion, ou bien faire une migration en douceur. Les fonctionnalités de séparer un commit en plusieurs et vice-versa constituent un bon complément aux lacunes de Subversion sur ce point (par exemple en période de mise au point où on corrige plusieurs bugs en même temps et qu'on veut faire un commit par correction de retour au bureau).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://assets2.pragprog.com/images/covers/190x228/utc2.jpg?1236205130" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="96" src="http://assets2.pragprog.com/images/covers/190x228/utc2.jpg?1236205130" width="79" /&gt;&lt;/a&gt;&lt;/div&gt;Enfin, Andy Hunt et Dave Thomas  décrivent différentes techniques de test unitaires dans &lt;a href="http://pragprog.com/titles/utc2/pragmatic-unit-testing-in-c-with-nunit"&gt;Pragmatic Unit Testing in C# with NUnit&lt;/a&gt;. Le livre détaille les cas auxquels nous sommes couramment confrontés dans l'écriture de tests unitaires (mocks, travailler avec des bases de codes non testées, le test d'interfaces graphiques) et liste exhaustivement les points à vérifier pour avoir des tests complets. Ceci dit, pour apprendre le développement piloté par les tests, je conseillerais plutôt l'ouvrage de Dave Astels  "Test Driven Development: A Practical Guide". Le livre de Hunt et Thomas, certes de qualité, me paraît moins accessibles par les développeurs qui n'ont jamais écrit de tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8577657228786809638?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8577657228786809638/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/09/les-lectures-de-lete.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8577657228786809638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8577657228786809638'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/09/les-lectures-de-lete.html' title='Les lectures de l&apos;été'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8675735930922302961</id><published>2009-08-21T09:41:00.012+02:00</published><updated>2009-08-21T10:50:57.313+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Deux outils UML</title><content type='html'>J'aime les outils UML qui permettent de créer des diagrammes rapidement. Malheureusement la plupart des outils offrent une interface très clickodrome avec des popups dans tous les sens qui alourdit leur utilisation. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Deux outils UML vraiment très sympas sortent du lot:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.umlet.com/"&gt;UMLet&lt;/a&gt;: écrit en Java, chaque élément graphique est affiché selon une entrée texte avec une syntaxe légère. Par exemple:&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Movies&lt;br /&gt;--&lt;br /&gt;-moviesCollection:OrderedCollection&lt;br /&gt;--&lt;br /&gt;+add(aMovie)&lt;br /&gt;+includes(aMovie):Boolean&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;affichera ceci:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JLK6EDfW4so/So5TEp9dPTI/AAAAAAAAAOU/D6TPcGYAYLQ/s1600-h/uml1.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 253px; height: 85px;" src="http://4.bp.blogspot.com/_JLK6EDfW4so/So5TEp9dPTI/AAAAAAAAAOU/D6TPcGYAYLQ/s320/uml1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5372322744947785010"&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;font class="Apple-style-span" face="arial, helvetica, clean, sans-serif"&gt;&lt;font class="Apple-style-span" style="background-color: rgb(81, 149, 206); white-space: pre-wrap;"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font class="Apple-style-span" face="arial, helvetica, clean, sans-serif"&gt;&lt;font class="Apple-style-span" style="background-color: rgb(81, 149, 206); white-space: pre-wrap;"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font class="Apple-style-span" face="arial, helvetica, clean, sans-serif"&gt;&lt;font class="Apple-style-span" style="background-color: rgb(81, 149, 206); white-space: pre-wrap;"&gt;&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;font&gt;&lt;font&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://yuml.me/"&gt;yuml.me&lt;/a&gt;: c'est un service web qui génère un diagramme de classe à partir d'une description texte. On peut ensuite intégrer ce diagramme sur une page web. Par exemple:&lt;/li&gt;&lt;/ul&gt;&lt;/font&gt;&lt;/font&gt;&lt;font&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Movies|-moviesCollection:OrderedCollection|+add(aMovie);&lt;br/&gt;+includes(aMovie):Boolean]+-*[Movie|+title],&lt;br/&gt;[Movies]++-moviesCollection[OrderedCollection]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Génère ceci:&lt;br /&gt;&lt;img width=500 src="http://yuml.me/diagram/scruffy/class/[Movies%7C-moviesCollection:OrderedCollection%7C+add(aMovie);+includes(aMovie):Boolean]+-*[Movie%7C+title],[Movies]++-moviesCollection[OrderedCollection]"&gt;&lt;br /&gt;&lt;/font&gt;&lt;div&gt;&lt;font class="Apple-style-span" face="arial, helvetica, clean, sans-serif"&gt;&lt;font class="Apple-style-span" style="background-color: rgb(81, 149, 206); white-space: pre-wrap;"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8675735930922302961?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8675735930922302961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/08/deux-outils-uml.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8675735930922302961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8675735930922302961'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/08/deux-outils-uml.html' title='Deux outils UML'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JLK6EDfW4so/So5TEp9dPTI/AAAAAAAAAOU/D6TPcGYAYLQ/s72-c/uml1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-9026723775662837406</id><published>2009-07-13T14:24:00.009+02:00</published><updated>2009-12-13T14:55:03.750+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo et tests unitaires (4): 1 Trait acheté, 4 tests offerts !!</title><content type='html'>Dernière partie de la série:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://magaloma.blogspot.com/2009/06/pharo-et-tests-unitaires-utilisation.html"&gt;utilisation des outils de test et debuggage&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;écriture et test d'une Collection: &lt;a href="http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-2-les.html"&gt;partie 1&lt;/a&gt; , puis &lt;a href="http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-3-les.html"&gt;partie 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;utilisation des Traits dans les tests unitaires   (ce billet)&lt;/li&gt;&lt;/ul&gt;Smalltalk ajoute à l'héritage un autre mécanisme de réutilisation de code, les &lt;b&gt;Traits&lt;/b&gt;. Pour (sur-)simplifier, un Trait fournit un ensemble de méthodes définissant un comportement. Plusieurs classes peuvent utiliser un même Trait et une classe peut être composée de plusieurs Traits.  Cela nous offre un moyen d'éviter des duplications de code en mutualisant des fonctions ou comportements génériques entre les classes.&lt;br /&gt;&lt;br /&gt;On peut faire l'analogie avec les &lt;b&gt;modules&lt;/b&gt; de Ruby (appelés aussi &lt;b&gt;mixins&lt;/b&gt;). Par exemple, le &lt;a href="http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html"&gt;module &lt;i&gt;Observable&lt;/i&gt;&lt;/a&gt; implémente le design pattern du même nom. Si une classe inclut ce module, elle possède de fait les méthodes &lt;i&gt;add_observer&lt;/i&gt;, &lt;i&gt;notify_observers&lt;/i&gt;, ... Si cette même classe doit aussi être &lt;i&gt;Singleton&lt;/i&gt;, il suffit de lui inclure le module en question.&lt;br /&gt;&lt;br /&gt;Voici un exemple extrêmement scientifique:&lt;br /&gt;&lt;pre&gt;&lt;b&gt;require&lt;/b&gt; 'observer'&lt;br /&gt;&lt;b&gt;require&lt;/b&gt; 'singleton'&lt;br /&gt;&lt;br /&gt;&lt;b&gt;class&lt;/b&gt; Agathe&lt;br /&gt;&lt;b&gt;include&lt;/b&gt; Observable&lt;br /&gt;&lt;i&gt;# Agathe est unique&lt;/i&gt;&lt;br /&gt;&lt;b&gt;include&lt;/b&gt; Singleton&lt;br /&gt;&lt;br /&gt;&lt;b&gt;def&lt;/b&gt; reveille_toi&lt;br /&gt;&lt;i&gt;# ça peut mettre un peu de temps &lt;/i&gt;&lt;br /&gt;sleep 10*rand&lt;br /&gt;&lt;br /&gt;changed true&lt;br /&gt;notify_observers&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;class&lt;/b&gt; Laurent&lt;br /&gt;&lt;i&gt;# Laurent est unique aussi&lt;/i&gt; ;)&lt;br /&gt;&lt;b&gt;include&lt;/b&gt; Singleton&lt;br /&gt;&lt;br /&gt;&lt;b&gt;def&lt;/b&gt; initialize&lt;br /&gt;Agathe.instance.add_observer self&lt;br /&gt;&lt;b&gt;  end&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;def&lt;/b&gt; update&lt;br /&gt;puts "C'est pas trop tôt"&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;def&lt;/b&gt; reveille_agathe&lt;br /&gt;Agathe.instance.reveille_toi&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Laurent.instance.reveille_agathe&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et le résultat:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;$&lt;/b&gt; ruby test_modules.rb&lt;br /&gt;C'est pas trop tôt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pour plus de détails sur les Traits de Smalltalk, vous pouvez consulter le document &lt;a href="http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf"&gt;Traits: Composable Units of Behaviour&lt;/a&gt;(Nathanel Schärli, Stéphane Ducasse, Oscar Nierstrasz, Andrew P.Black).&lt;br /&gt;&lt;br /&gt;La manière d'utiliser les Traits pour les tests unitaires dans Pharo m'a interpellé.&lt;br /&gt;&lt;br /&gt;On écrit de nombreux tests unitaires de base qui se ressemblent, surtout lorsqu'on manipule des listes, des collections. Si on reprends notre exemple d'une classe qui représente une collection de films (Movies), on écrit à peu près les tests suivants:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;pour une instance de Movies toute fraîche:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;size retourne 0&lt;/li&gt;&lt;li&gt;isEmpty retourne true&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;j'ajoute un film (via Movies#add)&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;size retourne 1&lt;/li&gt;&lt;li&gt;isEmpty retourne false&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;j'ajoute trois films&lt;/li&gt;&lt;ul&gt;&lt;li&gt;size retourne 3&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;je retire un film&lt;/li&gt;&lt;ul&gt;&lt;li&gt;....&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;C'est sympa les premières fois mais la pratique devient vite répétitive. Heureusement, certains frameworks permettent de réduire ce genre de tests à une simple ligne de code (avec &lt;a href="http://github.com/thoughtbot/shoulda/tree/master"&gt;Shoulda &lt;/a&gt;dans le monde Rails par exemple).&lt;br /&gt;&lt;br /&gt;Dans Pharo, des Traits dédiés à faciliter l'écriture des tests ont été écrits. On peut le voir en parcourant le package &lt;b&gt;Collection-Tests&lt;/b&gt;. Les Traits sont par convention préfixés de la lettre T.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SlT9_ynPJ_I/AAAAAAAAAL4/qBXXRgfhtnY/s1600-h/pharo59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SlT9_ynPJ_I/AAAAAAAAAL4/qBXXRgfhtnY/s400/pharo59.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;1. Test de la classe Movies avec le Trait TSizeTest&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Créons notre champ d'expérimentation en ajoutant la classe &lt;b&gt;MoviesWithTraitsTest&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Object subclass: #MoviesWithTraitsTest&lt;br /&gt;instanceVariableNames: ''&lt;br /&gt;classVariableNames: ''&lt;br /&gt;poolDictionaries: ''&lt;br /&gt;category: 'Movies'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si vous avez bien suivi le &lt;a href="http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-2-les.html"&gt;premier billet &lt;/a&gt;sur les tests d'une Collection, nous testons la méthode &lt;b&gt;Movies#size&lt;/b&gt;  avec une collection vide, puis contenant un et plusieurs éléments. Et ça tombe bien, le Trait que &lt;b&gt;TSizeTest&lt;/b&gt; existe.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SlUC_0Xm4lI/AAAAAAAAAMA/ooT-L_oFekc/s1600-h/pharo60.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SlUC_0Xm4lI/AAAAAAAAAMA/ooT-L_oFekc/s400/pharo60.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Indiquons à notre classe de test d'utiliser ce Trait. Modifiez la définition de la classe comme suit:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Object subclass: #MoviesWithTraitsTest&lt;br /&gt;&lt;b&gt;uses: TSizeTest&lt;/b&gt;&lt;br /&gt;instanceVariableNames: ''&lt;br /&gt;classVariableNames: ''&lt;br /&gt;poolDictionaries: ''&lt;br /&gt;category: 'Movies'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et auto-magiquement, les méthodes du Trait sont ajoutées dans &lt;b&gt;MovieWithTraitsTest&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlUFY1PQ5CI/AAAAAAAAAMI/eLOC2PA7YIQ/s1600-h/pharo61.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlUFY1PQ5CI/AAAAAAAAAMI/eLOC2PA7YIQ/s400/pharo61.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Etudions la méthode &lt;b&gt;MovieWithTraitsTest#test0TSizeTest&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;test0TSizeTest&lt;br /&gt;self shouldnt: [self empty] raise: Error.&lt;br /&gt;self shouldnt: [self sizeCollection] raise: Error.&lt;br /&gt;self assert: self empty isEmpty.&lt;br /&gt;self deny: self sizeCollection isEmpty.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Les deux première lignes vérifient que les méthodes &lt;b&gt;MovieWithTraitsTest#empty&lt;/b&gt; et &lt;b&gt;MovieWithTraitsTest#sizeCollection&lt;/b&gt; ne lancent pas d'exception.&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;D'une part, les Traits ne peuvent définir de variables. D'autre part  l'utilisateur du Trait a le devoir d'implémenter certaines méthodes pour le bon fonctionnement ce ce Trait.&lt;br /&gt;&lt;br /&gt;Nous pouvons le vérifier en ouvrant &lt;b&gt;MovieWithTraitsTest#sizeCollection&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sizeCollection&lt;br /&gt;&lt;i&gt;"Answers a collection not empty"&lt;br /&gt;&lt;/i&gt;    ^ self explicitRequirement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Bon, allons examiner &lt;b&gt;explicitRequirement&lt;/b&gt; pour en connaître les détails. Clic droit sur la méthode et sélectionnez &lt;b&gt;implementors&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SlZJqc6CE-I/AAAAAAAAAMQ/tu6hEjIpoqU/s1600-h/pharo62.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SlZJqc6CE-I/AAAAAAAAAMQ/tu6hEjIpoqU/s320/pharo62.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Cela nous amène à la définition de &lt;b&gt;Object#explicitRequirement&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;explicitRequirement&lt;br /&gt;self error: 'Explicitly required method'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;qui lance explicitement une jolie erreur.&lt;br /&gt;&lt;br /&gt;Nous devons donc implémenter &lt;b&gt;MovieWithTraitsTest#empty&lt;/b&gt; et &lt;b&gt;MovieWithTraitsTest#sizeCollection.&lt;/b&gt; Commençons par la première en retournant une instance de &lt;b&gt;Movies&lt;/b&gt; vide:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;empty&lt;br /&gt;^ Movies new&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et une instance de &lt;b&gt;Movies&lt;/b&gt; avec quelques éléments pour la suivante:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sizeCollection&lt;br /&gt;&lt;i&gt;"Answers a collection not empty"&lt;br /&gt;&lt;/i&gt;    | movies |&lt;br /&gt;movies := Movies new.&lt;br /&gt;#('Amélie' 'Alien 4' 'Délicatessen') do: [:aTitle|&lt;br /&gt;movies add: (Movie newWithTitle: aTitle)].&lt;br /&gt;^ movies&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et lançons les tests. Le debugger se manifeste:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SlZP65IcCAI/AAAAAAAAAMg/JMD_37Bzovo/s1600-h/pharo64.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SlZP65IcCAI/AAAAAAAAAMg/JMD_37Bzovo/s400/pharo64.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;MovieWithTraitsTest#test0TSizeTest&lt;/b&gt; vérifie le retour de la méthode &lt;b&gt;Movies#isEmpty&lt;/b&gt; qui n'existe pas. Créons la méthode:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;isEmpty&lt;br /&gt;^ moviesCollection isEmpty&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;puis relançons les tests. C'est maintenant le test &lt;b&gt;MovieWithTraitsTest#testSize&lt;/b&gt; qui s'arrête car &lt;b&gt;Movies#do: aBlock&lt;/b&gt; n'est pas implémentée. Appuyons nous toujours sur &lt;b&gt;moviesCollection&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;&lt;pre&gt;do: aBlock&lt;br /&gt;moviesCollection do: aBlock &lt;/pre&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SlZSZb7kL5I/AAAAAAAAAMo/QJrF1Kr780A/s1600-h/pharo65.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SlZSZb7kL5I/AAAAAAAAAMo/QJrF1Kr780A/s200/pharo65.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;et puis tout va beaucoup mieux.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Notre propre Trait&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Allons plus loin en implémentant notre propre Trait de test. Pour reprendre la démarche du billet précédent, nous allons créer des tests vérifiant que &lt;b&gt;Movies#includes: aMovie&lt;/b&gt; retourne &lt;b&gt;true&lt;/b&gt; pour les instances de &lt;b&gt;Movie&lt;/b&gt; qu'il contient, &lt;b&gt;false&lt;/b&gt; dans le cas contraire.&lt;br /&gt;&lt;br /&gt;Le test à &lt;b&gt;false&lt;/b&gt; est plus simple à réaliser. Voici la méthode &lt;b&gt;MoviesWithTraitsTest#testIncludesElementNotInReturnsFalse&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;testIncludesElementNotInReturnsFalse&lt;br /&gt;self deny: (self sizeCollection includes: self elementNotIn).&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;elementNotIn&lt;/b&gt; représente une instance qui n'est pas censée se trouver dans &lt;b&gt;sizeCollection&lt;/b&gt;. Comme les Traits ne possèdent pas de variables d'instances, nous passons par une méthode:&lt;br /&gt;&lt;pre&gt;elementNotIn&lt;br /&gt;^ Movie newWithTitle: 'City Of Lost Children'.&lt;br /&gt;&lt;/pre&gt;Vérifiez le bon fonctionnement des tests, cela doit passer comme une lettre à la poste (quand il n'y a pas grève).&lt;br /&gt;&lt;br /&gt;Passons au test opposé:&lt;br /&gt;&lt;pre&gt;testIncludesElementInReturnsTrue&lt;br /&gt;self assert: (self sizeCollection includes: self elementIn).&lt;br /&gt;&lt;/pre&gt;Cette fois-ci, &lt;b&gt;MoviesWithTraitsTest#elementIn&lt;/b&gt; doit retourner une instance de &lt;b&gt;Movie&lt;/b&gt; précédemment ajoutée à la collection retournée par &lt;b&gt;sizeCollection&lt;/b&gt;. Nous devons passer par une variable d'instance.&lt;br /&gt;&lt;pre&gt;elementIn&lt;br /&gt;^ aVeryLongEngagement&lt;br /&gt;&lt;/pre&gt;et déclarez &lt;b&gt;aVeryLongEngagement&lt;/b&gt; comme variable d'instance.&lt;br /&gt;&lt;br /&gt;&lt;img height="105" src="http://lh4.ggpht.com/_JLK6EDfW4so/SlsZRq5eJjI/AAAAAAAAANs/-eBTAjIIlqQ/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" width="248" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Initialisez ensuite cette variable dans la méthode &lt;b&gt;setUp&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;setUp&lt;br /&gt;aVeryLongEngagement := Movie newWithTitle: 'A Very Long Engagement'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et nous pouvons ajouter l'instance dans  &lt;b&gt;MoviesWithTraitsTest#sizeCollection&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;sizeCollection&lt;br /&gt;&lt;i&gt;"Answers a collection not empty"&lt;/i&gt;&lt;br /&gt;| movies |&lt;br /&gt;movies := Movies new.&lt;br /&gt;#('Amélie' 'Alien 4' 'Délicatessen') do: [:aTitle|&lt;br /&gt;movies add: (Movie newWithTitle: aTitle)].&lt;br /&gt;&lt;b&gt;movies add: aVeryLongEngagement.&lt;/b&gt;&lt;br /&gt;^ movies&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ceci fait, nos tests passent.&lt;br /&gt;&lt;br /&gt;&lt;img height="263" src="http://lh6.ggpht.com/_JLK6EDfW4so/SlsbfUM1R-I/AAAAAAAAAN0/025gFSqc5iM/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" width="602" /&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;3. Création du Trait&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Nous allons maintenant extraire nos deux méthodes de test vers un nouveau Trait. Faites un clic droit sur la méthode &lt;b&gt;testIncludesElementInReturnsTrue&lt;/b&gt; et sélectionnez &lt;b&gt;move to trait...&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_JLK6EDfW4so/SlscVM3tknI/AAAAAAAAAN4/U3_R2JIUAY4/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;Pharo demande le Trait dans lequel déplacer la méthode. Nous créons un nouveau Trait, cliquez &lt;b&gt;*New Trait*&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_JLK6EDfW4so/SlsczqF_CeI/AAAAAAAAAOA/a4YfpxCni_0/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;puis choisissez lui un joli nom, ici &lt;b&gt;TCollectionIncludesTest&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_JLK6EDfW4so/Slsc_gJrGJI/AAAAAAAAAOE/lhBewCdjVLE/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;et votre nouveau Trait est né.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_JLK6EDfW4so/SlsdO0riinI/AAAAAAAAAOI/PqN7GL4wnbY/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;Si vous revenez à la définition de la classe &lt;b&gt;MoviesWithTraitsTest&lt;/b&gt;, vous remarquerez qu'elle utilise notre Trait:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_JLK6EDfW4so/SlsdhhugFNI/AAAAAAAAAOM/iOm2w-0zrEc/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;Celui-ci s'appuie sur deux méthodes: &lt;b&gt;sizeCollection&lt;/b&gt; et &lt;b&gt;elementIn&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;testIncludesElementInReturnsTrue&lt;br /&gt;self assert: (self sizeCollection includes: self elementIn).&lt;br /&gt;&lt;/pre&gt;elles sont destinées à être implémentées par les classes qui utilisent le Trait. Autant le rendre explicite. Rajoutons les méthodes suivantes à &lt;b&gt;TCollectionIncludesTest&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sizeCollection&lt;br /&gt;"Answers a collection not empty"&lt;br /&gt;^ self explicitRequirement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;elementIn&lt;br /&gt;"Answers an instance included in self sizeCollection"&lt;br /&gt;^ self explicitRequirement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ajoutons enfin &lt;b&gt;MoviesWithTraitsTest#testIncludesElementNotInReturnsFalse&lt;/b&gt; au Trait:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_JLK6EDfW4so/Slsfb58rZlI/AAAAAAAAAOQ/1VVpt9jZu9c/%5BUNSET%5D.png?imgmax=800" style="max-width: 800px;" /&gt;&lt;br /&gt;&lt;br /&gt;et complétons par la méthode &lt;b&gt;elementNotIn&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;elementNotIn&lt;br /&gt;"Answers an instance not included in self sizeCollection"&lt;br /&gt;^ self explicitRequirement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Voilà, le Trait peut être utilisé par d'autres classes.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Avantages et inconvénients.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;L'utilisation des Traits apporte les avantages suivants:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;la cohérence: un Trait définit un comportement, et les tests utilisant des Traits testeront ces mêmes comportements. Cela garantit que les mêmes noms de méthodes sur des objets de type différent aboutiront à un comportement similaire. On peut vérifier les conventions.&lt;/li&gt;&lt;li&gt; réutilisation: la mutualisation des méthodes de test évite les duplications de code pour tester des comportements similaires.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Ceci dit des effets de bord apparaissent. Si la granularité des Traits n'est pas assez fine, on se retrouve obligé à implémenter beaucoup de méthodes d'un coup. Par exemple, lorsque nous avons utilisé &lt;b&gt;TSizeTest&lt;/b&gt;, nous avons dû implémenter &lt;b&gt;Movies#do:&lt;/b&gt; et &lt;b&gt;Movies#isEmpty&lt;/b&gt;, méthodes qui ne nous intéressaient pas forcément. Un Trait avec plus de tests nous aurait obligé à implémenter encore plus de méthodes. Cela peut casser la démarche de développement piloté par les tests.&lt;br /&gt;&lt;br /&gt;Les Traits de test sont un atout, mais je pense important:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;u&gt; &lt;/u&gt;de bien étudier un Trait avant de l'utiliser&lt;/li&gt;&lt;li&gt;de garder des Traits avec un périmètre de test limité&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-9026723775662837406?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/9026723775662837406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-4-1-trait.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/9026723775662837406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/9026723775662837406'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-4-1-trait.html' title='Pharo et tests unitaires (4): 1 Trait acheté, 4 tests offerts !!'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/SlT9_ynPJ_I/AAAAAAAAAL4/qBXXRgfhtnY/s72-c/pharo59.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7810837238529109709</id><published>2009-07-05T22:51:00.010+02:00</published><updated>2009-12-13T14:55:18.807+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo et tests unitaires (3): les collections, seconde partie</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span style="color: #141312;"&gt;Episode 3, saison 1.&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://magaloma.blogspot.com/2009/06/pharo-et-tests-unitaires-utilisation.html"&gt;utilisation des outils de test et debuggage&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;écriture et test d'une Collection: &lt;a href="http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-2-les.html"&gt;partie 1&lt;/a&gt; , puis partie 2  (ce billet)&lt;/li&gt;&lt;li&gt;utilisation des Traits dans les tests unitaires&lt;/li&gt;&lt;/ul&gt;Résumé de l'épisode précédent:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;i&gt;Inspector Thomas D. Derrick was facing Mister M., a supposed movies dealer. He had to find the truth. What did Mister M. do with the movies ?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;- Where do you put the movies ? T.D.Derrick asked.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- I'm a Collection. I keep them.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- You're a Collection, aren't you ? So what's your size ?&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Well ... Zero now. I'm empty !!&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- I'm sure you lie. Take this movie ... "Star Wars". Take it !&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- OK OK, done.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- And tell me what's your size now ...&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- One. You see, I have it.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;These right answers did not disturbed T.D.Derrick.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;- Play again with me. Add these movies. "Blade Runner" and ... this one, "Alien".&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Done.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- And now, your size ?&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Three. Can I go now ?&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Mister M. seemed to be a real Collection. T.D.Derrick felt he was missing something. Then a subtil smile appeared on his face:&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;- Do you &lt;b&gt;include&lt;/b&gt;&lt;/i&gt;&lt;i&gt; Star Wars ?&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Err ... hard to tell. I don't understand.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- What ?&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- I don't understand "include"&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Yep, I got you now !!&lt;/i&gt;&lt;br /&gt;&lt;i&gt;- Damned ...&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Au dernier billet, j'ai décrit l'implémentation, pilotée par les tests, de la méthode &lt;b&gt;Movies#size&lt;/b&gt;. La méthode retourne le nombre de films ajoutés via &lt;b&gt;Movies#add&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Néanmoins, &lt;b&gt;Movies&lt;/b&gt; utilise un compteur et ne passe pas par une &lt;b&gt;Collection&lt;/b&gt; interne pour stocker les instances de &lt;b&gt;Movie&lt;/b&gt;. Les tests nous ont permis de développer la solution la plus simple possible.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;1. Test: Movies#includes: aMovie&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Ajoutons un autre test pour notre collection:&lt;b&gt; Movies#includes: aMovie&lt;/b&gt; pour une instance de &lt;b&gt;Movie&lt;/b&gt; précédemment ajoutée retourne &lt;b&gt;vrai&lt;/b&gt;, sinon &lt;b&gt;faux&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;pre&gt;testIncludesAnAddedMovieReturnsTrue&lt;br /&gt;| movies starWars bladeRunner alien |&lt;br /&gt;movies := Movies new.&lt;br /&gt;&lt;br /&gt;starWars := Movie newWithTitle: 'Star Wars'.&lt;br /&gt;bladeRunner := Movie newWithTitle: 'Blade Runner'.&lt;br /&gt;alien := Movie newWithTitle: 'Alien'.&lt;br /&gt;&lt;br /&gt;movies add: starWars; add: bladeRunner.        &lt;br /&gt;&lt;br /&gt;self assert: (movies includes: starWars).&lt;br /&gt;self assert: (movies includes: bladeRunner).&lt;br /&gt;self deny:  (movies includes: alien).&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Notez l'appel &lt;b&gt;self deny&lt;/b&gt; : si la méthode &lt;b&gt;TestCase#assert&lt;/b&gt; vérifie que l'expression en paramètre retourne &lt;b&gt;true&lt;/b&gt;, &lt;b&gt;TestCase#deny&lt;/b&gt; passe si l'expression testée retourne &lt;b&gt;false&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Lancez le test. Le debugger s'arrête car &lt;b&gt;Movies#includes:aMovie&lt;/b&gt; n'existe pas.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlD1BdBj9qI/AAAAAAAAAKo/Ql10rAHTN4w/s1600-h/pharo46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlD1BdBj9qI/AAAAAAAAAKo/Ql10rAHTN4w/s400/pharo46.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Comme vu au billet précédent, utilisez le bouton &lt;b&gt;Create Method&lt;/b&gt; pour la déclarer.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SlD2RBXpNkI/AAAAAAAAAKw/iXmu6cuMlUU/s1600-h/pharo48.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SlD2RBXpNkI/AAAAAAAAAKw/iXmu6cuMlUU/s320/pharo48.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Cette fois-ci, on doit vraiment passer par une collection interne.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;includes: aMovie&lt;br /&gt;^ moviesCollection includes: aMovie&lt;/pre&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;Déclarons &lt;b&gt;moviesCollection&lt;/b&gt; comme variable d'instance pour la rendre accessible aux autres méthodes de la classe &lt;b&gt;Movies&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SlD3APiuynI/AAAAAAAAAK4/0-tDGwL5DMY/s1600-h/pharo49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SlD3APiuynI/AAAAAAAAAK4/0-tDGwL5DMY/s320/pharo49.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Comme toute variable d'instance, elle doit être initialisée. Au hasard, utilisons une instance d'&lt;b&gt;OrderedCollection&lt;/b&gt;. Editez &lt;b&gt;Movies#initialize&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;initialize&lt;br /&gt;super initialize.&lt;br /&gt;size:=0.&lt;br /&gt;moviesCollection:=OrderedCollection new&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;En relançant le test, le debugger indique que la première assertion ne passe pas. En utilisant la fonctions &lt;b&gt;watch it&lt;/b&gt; sur&lt;b&gt; (movies includes: starWars)&lt;/b&gt;, le retour est en effet &lt;b&gt;false&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlD3vq5uzEI/AAAAAAAAALA/1jkAu5R4-Ec/s1600-h/pharo50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlD3vq5uzEI/AAAAAAAAALA/1jkAu5R4-Ec/s320/pharo50.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Utilisons un autre outil de debuggage. Sélectionner &lt;b&gt;movies&lt;/b&gt;, puis clic-droit, &lt;b&gt;inspect it&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBRBgHOVYI/AAAAAAAAAJ4/2-FN8_Kvkvs/s1600-h/pharo38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBRBgHOVYI/AAAAAAAAAJ4/2-FN8_Kvkvs/s320/pharo38.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;La fenêtre qui apparaît permet de visualiser l'état de l'instance &lt;b&gt;Movies&lt;/b&gt;. En cliquant sur &lt;b&gt;size&lt;/b&gt;, on voit que la méthode retourne &lt;b&gt;2&lt;/b&gt;. Cela semble logique, nous avons ajouté les instances "Star Wars" et "Blade Runner".&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlB8d7TZUgI/AAAAAAAAAKQ/CEUdZxdwoFw/s1600-h/pharo43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlB8d7TZUgI/AAAAAAAAAKQ/CEUdZxdwoFw/s320/pharo43.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Regardons &lt;b&gt;moviesCollection&lt;/b&gt;. Nous avons bien une instance d'&lt;b&gt;OrderedCollection&lt;/b&gt;, mais vide (rien entre les parenthèses).&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBQ7_YNTeI/AAAAAAAAAJw/GYmSYBaGsWU/s1600-h/pharo40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBQ7_YNTeI/AAAAAAAAAJw/GYmSYBaGsWU/s320/pharo40.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Pour s'en assurer, utilisons l'éditeur de la fenêtre pour demander la taille de &lt;b&gt;moviesCollection&lt;/b&gt;. Tapez&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;moviesCollection size&lt;/pre&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;puis utilisez la fonction &lt;b&gt;inspect it&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBTwaebVSI/AAAAAAAAAKA/lk2IJmEe9r4/s1600-h/pharo41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SlBTwaebVSI/AAAAAAAAAKA/lk2IJmEe9r4/s320/pharo41.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;On voit que &lt;b&gt;size&lt;/b&gt; retourne bien &lt;b&gt;0&lt;/b&gt; (une instance de la classe &lt;b&gt;SmallInteger&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;Les instances de &lt;b&gt;Movie&lt;/b&gt; ne sont donc pas ajoutées à &lt;b&gt;moviesCollection&lt;/b&gt;. Examinons la méthode&lt;b&gt; Movies#add&lt;/b&gt;:&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;add: aMovie&lt;br /&gt;size := size + 1&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SlD4YI_n04I/AAAAAAAAALI/iIhhDBiB7eY/s1600-h/pharo51.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SlD4YI_n04I/AAAAAAAAALI/iIhhDBiB7eY/s320/pharo51.png" /&gt;&lt;/a&gt;En effet ... modifions la méthode:&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;add: aMovie&lt;br /&gt;size := size + 1.&lt;br /&gt;moviesCollection add: aMovie&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;et maintenant le test passe !&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;2. Refactoring.&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;i&gt;2.1 movies, starWars, bladeRunner et alien&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SlD52tuRCHI/AAAAAAAAALY/UuaVZkICjEU/s1600-h/pharo52.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SlD52tuRCHI/AAAAAAAAALY/UuaVZkICjEU/s200/pharo52.png" /&gt;&lt;/a&gt;Supprimons les duplications. Avant toute chose, vérifions que tous les tests de notre &lt;b&gt;package Movies&lt;/b&gt; passent.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Les duplications se trouvent dans &lt;b&gt;MoviesSizeTest&lt;/b&gt;. Les instances de &lt;b&gt;Movie&lt;/b&gt; pour "Star Wars", "Blade Runner" et "Alien" sont créées dans les méthodes &lt;b&gt;testWithOneMovieSizeReturnsOne&lt;/b&gt;,  &lt;b&gt;testWithThreeMoviesSizeReturnsThree&lt;/b&gt; et &lt;b&gt;testIncludesAnAddedMovieReturnsTrue&lt;/b&gt;. De plus, chacune des méthode de test instancie &lt;b&gt;Movies&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;b&gt;TestCase&lt;/b&gt; propose les méthodes &lt;b&gt;setUp&lt;/b&gt; et &lt;b&gt;tearDown&lt;/b&gt; pour ce genre de situation. &lt;b&gt;TestCase#setUp&lt;/b&gt; est exécutée avant chaque test, et &lt;b&gt;tearDown&lt;/b&gt; aprés chaque test. Lorsqu'on exécute tous les tests de &lt;b&gt;MoviesSizeTest&lt;/b&gt;, l'appel des méthodes est le suivant (à l'ordre des tests près):&lt;br /&gt;&lt;/div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;setUp&lt;/li&gt;&lt;li&gt;testIncludesAnAddedMovieReturnsTrue&lt;/li&gt;&lt;li&gt;tearDown&lt;/li&gt;&lt;li&gt;setUp&lt;/li&gt;&lt;li&gt;testNewInstanceSizeReturnsZero&lt;/li&gt;&lt;li&gt;tearDown&lt;/li&gt;&lt;li&gt;setUp&lt;/li&gt;&lt;li&gt;testWithOneMovieSizeReturnsOne&lt;br /&gt;&lt;/li&gt;&lt;li&gt;tearDown&lt;/li&gt;&lt;li&gt;setUp&lt;/li&gt;&lt;li&gt;testWithThreeMoviesSizeReturnsThree&lt;/li&gt;&lt;li&gt; tearDown&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div style="text-align: left;"&gt;Nous allons mettre en commun la création des instances de &lt;b&gt;Movie&lt;/b&gt; dans la méthode &lt;b&gt;setUp&lt;/b&gt;. Définissez &lt;b&gt;MoviesSizeTest#setUp&lt;/b&gt; comme suit&lt;b&gt;:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;setUp&lt;br /&gt;movies := Movies new.&lt;br /&gt;starWars := Movie newWithTitle: 'Star Wars'.&lt;br /&gt;bladeRunner := Movie newWithTitle: 'Blade Runner'.&lt;br /&gt;alien := Movie newWithTitle: 'Alien'.&amp;lt;br /&amp;gt;&lt;/pre&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Les quatre variables sont des variables d'instances. Adaptez ensuite chaque méthode de &lt;b&gt;MoviesSizeTest&lt;/b&gt; en vérifiant que les tests passent à chaque fois.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;pre&gt;testNewInstanceSizeReturnsZero&lt;br /&gt;self assert: movies size = 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;pre&gt;testWithOneMovieSizeReturnsOne&lt;br /&gt;movies add: starWars&lt;br /&gt;self assert: movies size = 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;pre&gt;testWithThreeMoviesSizeReturnsThree&lt;br /&gt;movies add: starWars;&lt;br /&gt;add: bladeRunner;&lt;br /&gt;add: starWars.&lt;br /&gt;self assert: movies size = 3&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;pre&gt;testIncludesAnAddedMovieReturnsTrue&lt;br /&gt;movies add: starWars;&lt;br /&gt;add: bladeRunner.&lt;br /&gt;self assert: (movies includes: starWars).&lt;br /&gt;self assert: (movies includes: bladeRunner).&lt;br /&gt;self deny:  (movies includes: alien).&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;2.2 Renommer MoviesSizeTest&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Le nom de la classe ne reflète plus assez la totalité des tests, car on vérifie les retours des méthodes &lt;b&gt;Movies#size&lt;/b&gt; et &lt;b&gt;Movies#includes&lt;/b&gt;. Le vrai objectif est de s'assurer que &lt;b&gt;Movies#add&lt;/b&gt; ajoute bien les instances de &lt;b&gt;Movie&lt;/b&gt;. Renommons la classe en &lt;b&gt;MoviesAddMovieTest&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SlEHz22-v_I/AAAAAAAAALg/o7bWOqqxo4c/s1600-h/pharo55.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SlEHz22-v_I/AAAAAAAAALg/o7bWOqqxo4c/s200/pharo55.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Pour ce faire, clic-droit sur la classe et sélectionnez &lt;b&gt;rename&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Saisissez &lt;b&gt;MoviesAddMovieTest&lt;/b&gt; comme nom de classe et validez.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;i&gt;2.3 Supprimer le comteur size de Movies&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Regardons &lt;b&gt;Movies#add&lt;/b&gt; de nouveau:&lt;br /&gt;&lt;pre&gt;add: aMovie&lt;br /&gt;size := size + 1.&lt;br /&gt;moviesCollection add: aMovie&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;moviesCollection&lt;/b&gt; est une instance d'&lt;b&gt;OrderedCollection&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OrderedCollection#size&lt;/b&gt; retourne le nombre d'instance stockées. On peut donc supprimer le compteur &lt;b&gt;size&lt;/b&gt; qui fait doublon.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;add: aMovie&lt;br /&gt;moviesCollection add: aMovie&lt;/pre&gt;&lt;br /&gt;Relancez les tests:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SlEKqTCk0FI/AAAAAAAAALo/H0TGu815wW4/s1600-h/pharo57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SlEKqTCk0FI/AAAAAAAAALo/H0TGu815wW4/s320/pharo57.png" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;Oups ... On s'aperçoit via le debugger que&lt;b&gt; Movies#size&lt;/b&gt; retourne toujours &lt;b&gt;0&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SlEN0sWOg1I/AAAAAAAAALw/M7YxiUd_EJg/s1600-h/pharo58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SlEN0sWOg1I/AAAAAAAAALw/M7YxiUd_EJg/s320/pharo58.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;L'erreur saute aux yeux en ouvrant &lt;b&gt;Movies#size&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;size&lt;br /&gt;^ size&lt;/pre&gt;&lt;br /&gt;Corrigeons la méthode:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;size&lt;br /&gt;^ moviesCollection size&lt;/pre&gt;&lt;br /&gt;et les tests repassent !&lt;br /&gt;&lt;br /&gt;Nettoyons au passage &lt;b&gt;Movies#initialize&lt;/b&gt; pour supprimer l'initialisation de &lt;b&gt;size&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;initialize&lt;br /&gt;super initialize.&lt;br /&gt;moviesCollection:=OrderedCollection new&lt;/pre&gt;&lt;br /&gt;et nous pouvons finalement supprimer &lt;b&gt;size&lt;/b&gt; des variables d'instances de &lt;b&gt;Movies&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Object subclass: #Movies&lt;br /&gt;instanceVariableNames: 'moviesCollection'&lt;br /&gt;classVariableNames: ''&lt;br /&gt;poolDictionaries: ''&lt;br /&gt;category: 'Movies'&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. Pause publicité&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Notre collection est minimale mais testée. Comme pour les billets précédents, nous écrivons d'abord le test puis nous nous laissons guider par les erreurs et le debugger pour implémenter le code fonctionnel.&lt;br /&gt;&lt;br /&gt;Après chaque  test, on modifie le code testé et de test pour l'améliorer.&lt;br /&gt;&lt;br /&gt;La prochaine fois, nous retravaillerons &lt;b&gt;MoviesAddMovieTest&lt;/b&gt; mais en utilisant les Traits, un mécanisme de réutilisation de code entre classes.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7810837238529109709?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7810837238529109709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-3-les.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7810837238529109709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7810837238529109709'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-3-les.html' title='Pharo et tests unitaires (3): les collections, seconde partie'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JLK6EDfW4so/SlD1BdBj9qI/AAAAAAAAAKo/Ql10rAHTN4w/s72-c/pharo46.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4643116618223759375</id><published>2009-07-01T20:56:00.019+02:00</published><updated>2009-12-13T14:55:33.522+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo et tests unitaires (2): les collections, première partie</title><content type='html'>&lt;h3 class="post-title entry-title"&gt;&lt;/h3&gt;&lt;div class="post-body entry-content"&gt;&lt;div style="text-align: left;"&gt;En route pour le second billet de notre trilogie... qui devient au moins une quadrilogie (à vouloir trop en dire). Un petit point pour voir où nous en sommes dans la série:&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt; &lt;a href="http://magaloma.blogspot.com/2009/06/pharo-et-tests-unitaires-utilisation.html"&gt;utilisation des outils de test et debuggage&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;écriture et test d'une Collection:  partie 1 (ce billet) puis partie 2&lt;br /&gt;&lt;/li&gt;&lt;li&gt;utilisation des Traits dans les tests unitaires&lt;/li&gt;&lt;/ul&gt;La programmation d'un logiciel implique généralement l'écriture de nombreuses collections. D'autant plus que les bonnes règles de design nous conseillent que chaque collection soit encapsulée dans sa propre classe. Nous manipulons ainsi des primitives de plus haut niveau.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="post-body entry-content"&gt;&lt;a href="http://www.pragprog.com/titles/twa/thoughtworks-anthology" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="96" src="http://assets0.pragprog.com/images/covers/190x228/twa.jpg?1236205267" width="77" /&gt;&lt;/a&gt;Au passage, je conseille de lire l'essai "&lt;b&gt;Object Calisthenics&lt;/b&gt;" de Jeff Bay paru dans "&lt;b&gt;The ThoughtWorks Anthology&lt;/b&gt;". Jeff Bay y décrit neuf étapes aboutissant à un meilleur design, dont cette pratique sur les collections.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="post-body entry-content"&gt;&lt;/div&gt;&lt;div class="post-body entry-content"&gt;&lt;/div&gt;&lt;div class="post-body entry-content"&gt;&lt;br /&gt;Si on écrit beaucoup de collections, cela implique de coder de nombreux tests de ces collections. Toutefois, on arrive assez facilement à implémenter ce genre de test et cela me semble donc un bon moyen de se familiariser avec le développement piloté par les tests.&lt;br /&gt;&lt;/div&gt;&lt;div class="post-body entry-content"&gt;&lt;/div&gt;&lt;div class="post-body entry-content"&gt;Dans notre cas, nous avions précédemment créé la classe &lt;b&gt;Movie&lt;/b&gt;. Nous allons maintenant écrire une collection &lt;b&gt;Movies&lt;/b&gt; (notez le &lt;b&gt;s&lt;/b&gt;) pour recenser des instances de &lt;b&gt;Movie&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Nous aimerions connaître le nombre d'instances de &lt;b&gt;Movie&lt;/b&gt; ajoutées à notre collection, via la méthode &lt;b&gt;Movies#size&lt;/b&gt;. Voici les tests que nous pouvons écrire:&lt;br /&gt;&lt;div class="post-body entry-content"&gt;&lt;ol&gt;&lt;li&gt;pour une instance de Movies toute fraîche (donc vide): &lt;b&gt;size&lt;/b&gt; retourne &lt;b&gt;0&lt;/b&gt;&lt;/li&gt;&lt;li&gt;j'ajoute un film (via &lt;b&gt;Movies#add&lt;/b&gt;): &lt;b&gt;size&lt;/b&gt; retourne &lt;b&gt;1&lt;/b&gt;&lt;/li&gt;&lt;li&gt;j'ajoute trois films:&amp;nbsp; &lt;b&gt;size&lt;/b&gt; retourne &lt;b&gt;3&lt;/b&gt;&lt;/li&gt;&lt;/ol&gt;&lt;b&gt;1. Test d'une instance Movies vide&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Créons d'abord la classe &lt;b&gt;MoviesSizeTest&lt;/b&gt; qui hérite &lt;b&gt;TestCase&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;TestCase subclass: #MoviesSizeTest&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; instanceVariableNames: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; classVariableNames: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; poolDictionaries: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; category: 'Movies'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;puis ajoutons notre premier test:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;testNewInstanceSizeReturnsZero&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; | movies |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; movies := Movies new.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; self assert: movies size = 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Acceptez la méthode (Ctrl-s). Comme vu dans les billets précédents, Pharo va vous proposer de déclarer la variable &lt;b&gt;movies&lt;/b&gt;, puis la classe &lt;b&gt;Movies&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Lancez alors le test; le debugger s'arrête car nous n'avons pas redéfinit la méthode &lt;b&gt;size&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SkkdF5VS5EI/AAAAAAAAAGQ/jb90EyjQIxM/s1600-h/pharo12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SkkdF5VS5EI/AAAAAAAAAGQ/jb90EyjQIxM/s400/pharo12.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Rajoutons la méthode &lt;b&gt;Movies#size&lt;/b&gt; en appliquant un principe fondamental: "&lt;b&gt;Do the simplest thing that could possibly work&lt;/b&gt;":&lt;br /&gt;&lt;pre&gt;size&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ^ 0&lt;br /&gt;&lt;/pre&gt;relancez le test et savourez un instant ce sentiment de domination totale....&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SkkfnJffzUI/AAAAAAAAAGY/U5PJ1HrBmGY/s1600-h/pharo13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SkkfnJffzUI/AAAAAAAAAGY/U5PJ1HrBmGY/s200/pharo13.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Pourquoi faire passer le test de manière aussi "stupide" ?&lt;br /&gt;&lt;ul&gt;&lt;li&gt; rappelez-vous que stupide est un compliment pour un programme ;) (Keep It Simple, Stupid) : le code reste le plus compréhensible possible.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;on voit de manière flagrante que ce seul test est insuffisant.&lt;/li&gt;&lt;li&gt;on peut déjà archiver un travail testé.&lt;/li&gt;&lt;li&gt;on ressens la satisfaction d'un premier test qui passe, cela entretient la motivation. &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;De plus, ces premiers tests simples permettent de s'assurer de la présence de tous les éléments de base, ici la classe &lt;b&gt;Movies&lt;/b&gt; et la méthode &lt;b&gt;size&lt;/b&gt; (ce qui est un bon début).&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;2. Test d'une instance Movies contenant un film&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Nous allons maintenant ajouter une instance &lt;b&gt;Movie&lt;/b&gt; à notre collection et vérifier que &lt;b&gt;size&lt;/b&gt; retourne &lt;b&gt;1&lt;/b&gt;. Voici le code de notre test:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;testWithOneMovieSizeReturnsOne&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;| movies starWars |&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;movies := Movies new.&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;starWars := Movie newWithTitle: 'Star Wars'.&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;movies add: starWars.&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;self assert: movies size = 1&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;A l'exécution du test, le debugger s'arrête car la méthode &lt;b&gt;Movies#add &lt;/b&gt;n'existe pas. Profitons en pour étudier quelques outils. Sur la droite du debugger, un bouton &lt;b&gt;Create Method&lt;/b&gt; permet de créer directement la méthode manquante.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/Skp2hinW8RI/AAAAAAAAAHo/IglIURtlRf0/s1600-h/pharo23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/Skp2hinW8RI/AAAAAAAAAHo/IglIURtlRf0/s320/pharo23.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Pharo nous demande de choisir une catégorie pour notre méthode. Pour l'instant choisissez &lt;b&gt;as yet unclassified&lt;/b&gt;, nous verrons par la suite qu'il existe une bonne fonction de paresseux pour classer les méthodes :). La méthode créée, le debugger reprends l'exécution et s'arrête dessus:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp2v7ObcZI/AAAAAAAAAHw/EKEvEDjMUgU/s1600-h/pharo24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp2v7ObcZI/AAAAAAAAAHw/EKEvEDjMUgU/s200/pharo24.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Nous voyons qu'un modèle de méthode a été enregistré, à nous de le modifier pour l'accorder à nos besoins. Vous pouvez saisir le "stupide" code suivant directement dans l'éditeur du debugger:&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;add: aMovie &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size := 1&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;A l'acceptation, choisissez de déclarer size en variable d'instance.&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;Continuez le test (&lt;b&gt;Proceed&lt;/b&gt;), le debugger s'arrête car l'assertion ne passe pas. On s'attends à ce que &lt;b&gt;Movies#size&lt;/b&gt; retourne &lt;b&gt;1&lt;/b&gt;. Voyons ce que la méthode retourne actuellement. Sélectionnez &lt;b&gt;movies size&lt;/b&gt; dans le code du debugger, clic-droit, choisissez &lt;b&gt;watch it&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp5YXVRPpI/AAAAAAAAAH4/m9b_T3mSruA/s1600-h/pharo25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp5YXVRPpI/AAAAAAAAAH4/m9b_T3mSruA/s200/pharo25.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Une fenêtre s'ouvre et nous indique ce que retourne &lt;b&gt;size&lt;/b&gt;.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SkpR47uYN-I/AAAAAAAAAHg/6AzhxDI6Mgw/s1600-h/pharo19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SkpR47uYN-I/AAAAAAAAAHg/6AzhxDI6Mgw/s200/pharo19.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;En effet, nous n'avons pas modifié l'implémentation de &lt;b&gt;Movies#size&lt;/b&gt; pour utiliser notre nouvelle variable d'instance. Editez la méthode:&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;/div&gt;&lt;pre&gt;size&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ^ size&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/Skp6wUFU23I/AAAAAAAAAIA/LtYJjWqEwWo/s1600-h/pharo26.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/Skp6wUFU23I/AAAAAAAAAIA/LtYJjWqEwWo/s320/pharo26.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;et continuez le test.&lt;br /&gt;&lt;br /&gt;Victoire !!&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;3. .... ou presque&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Relançons tous les tests du package &lt;b&gt;Movies&lt;/b&gt; pour vérifier que tout est bon. Sélectionnez le package dans la première colonne du&lt;b&gt; Class Browser&lt;/b&gt; puis lancez les tests.&lt;br /&gt;&lt;br /&gt;Enfer et damnation:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/Skp8K7agoJI/AAAAAAAAAII/ejUTpfdGloQ/s1600-h/pharo27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/Skp8K7agoJI/AAAAAAAAAII/ejUTpfdGloQ/s320/pharo27.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;MoviesSizeTest#testNewInstanceSizeReturnsZero &lt;/b&gt;ne passe plus, &lt;b&gt;Movies#size&lt;/b&gt; ne retourne pas &lt;b&gt;0&lt;/b&gt;. Comme vu précédemment, utilisez la fonction &lt;b&gt;watch it&lt;/b&gt; pour afficher ce que retourne &lt;b&gt;movies size&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp9RPD6IXI/AAAAAAAAAIQ/6mCwElZUiYk/s1600-h/pharo28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/Skp9RPD6IXI/AAAAAAAAAIQ/6mCwElZUiYk/s200/pharo28.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Aie, nous n'aurions pas omis d'initialiser une certaine variable par hasard ?&lt;br /&gt;Spécifions l'initialisation de la classe &lt;b&gt;Movies&lt;/b&gt; pour mettre sa variable d'instance à 0.&amp;nbsp; Dans la classe &lt;b&gt;Movies&lt;/b&gt;, ajouter la méthode:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;initialize&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; super initialize.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size:=0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;puis relancez les tests. &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SkqAMj_Nc0I/AAAAAAAAAIY/qkKTfQbS1jE/s1600-h/pharo29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SkqAMj_Nc0I/AAAAAAAAAIY/qkKTfQbS1jE/s320/pharo29.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;C'est mieux.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;4. Test d'une instance Movies avec trois films&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Continuons en ajoutant trois films à notre instance de &lt;b&gt;Movies&lt;/b&gt; et vérifions que &lt;b&gt;Movies#size&lt;/b&gt; retourne &lt;b&gt;3&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;testWithThreeMoviesSizeReturnsThree&lt;br /&gt;&amp;nbsp;&amp;nbsp; | movies starWars bladeRunner alien |&lt;br /&gt;&amp;nbsp;&amp;nbsp; movies := Movies new.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; starWars := Movie newWithTitle: 'Star Wars'.&lt;br /&gt;&amp;nbsp;&amp;nbsp; bladeRunner := Movie newWithTitle: 'Blade Runner'.&lt;br /&gt;&amp;nbsp;&amp;nbsp; alien := Movie newWithTitle: 'Alien'.&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; movies add: starWars; add: bladeRunner; add: alien.&lt;br /&gt;&amp;nbsp;&amp;nbsp; self assert: movies size = 3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ce test ci ne devrait pas poser trop de problèmes. En le lançant, le debugger s'arrête car l'assertion ne passe pas. Après une petite vérification de &lt;b&gt;movies size&lt;/b&gt;, nous voyons que &lt;b&gt;size&lt;/b&gt; retourne &lt;b&gt;1&lt;/b&gt;. En effet, en ouvrant le code de &lt;b&gt;Movies#add&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;add: aMovie &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size := 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Modifiez le code comme suit:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;add: aMovie &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size := size + 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et tout est sous contrôle.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SktSOJ--D7I/AAAAAAAAAIo/vBWNNhnZYgk/s1600-h/pharo31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SktSOJ--D7I/AAAAAAAAAIo/vBWNNhnZYgk/s320/pharo31.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;5. Refactoring&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Il est temps d'introduire un autre principe du développement piloté par les tests.&lt;br /&gt;&lt;br /&gt;Les tests nous permettent de vérifier que nous n'avons pas cassé le fonctionnement de nos objets; nous pouvons alors prendre tout le loisir de jouer (si si, c'est un jeu) avec notre code pour l'améliorer.&lt;br /&gt;&lt;br /&gt;Généralement, nous cherchons durant cette phase les duplications de code. Car nos maîtres l'ont dit: &lt;b&gt;Don't Repeat Yourself !&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://assets1.pragprog.com/images/covers/190x228/tpp.jpg?1236205216" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="96" src="http://assets1.pragprog.com/images/covers/190x228/tpp.jpg?1236205216" width="72" /&gt;&lt;/a&gt;&lt;br /&gt;Au passage, le sujet est bien traité dans &lt;a href="http://www.pragprog.com/titles/tpp/the-pragmatic-programm"&gt;Pragmatic Programmers: From Journeyman to Master&lt;/a&gt; par Dave Thomas et Andy Hunt. &lt;br /&gt;&lt;br /&gt;Le refactoring concerne bien sûr le code testé, mais aussi notre code de test ! En pratique, lorsque les choses sont bien faites, le code fonctionnel teste notre code de test et vice-versa.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Une duplication se trouve dans la méthode&lt;b&gt; MoviesSizeTest#testWithThreeMoviesSizeReturnsThree&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp;&amp;nbsp; starWars := Movie newWithTitle: 'Star Wars'.&lt;br /&gt;&amp;nbsp;&amp;nbsp; bladeRunner := Movie newWithTitle: 'Blade Runner'.&lt;br /&gt;&amp;nbsp;&amp;nbsp; alien := Movie newWithTitle: 'Alien'.&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Améliorons le code en utilisant les jolies possibilités que nous procure Smalltalk:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;testWithThreeMoviesSizeReturnsThree&lt;br /&gt;&amp;nbsp;&amp;nbsp; | movies |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; movies := Movies new.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #('Star Wars' 'Blade Runner' 'Alien') do: [:title| &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; movies add: (Movie newWithTitle: title)].&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp; self assert: movies size = 3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pour être plus clair sur l'instanciation de &lt;b&gt;Movie&lt;/b&gt;, on peut utiliser une variable locale au block:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #('Star Wars' 'Blade Runner' 'Alien') do: [:title| &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; |aMovie|&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; aMovie:= Movie newWithTitle: title.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; movies add: aMovie].&lt;/pre&gt;&lt;br /&gt;A chaque modification, assurez vous que les tests passent encore. Amusez-vous a explorer les possibilités de Smalltalk dans votre nouveau bac à sable, c'est fait pour ça. Si jamais vous avez besoin de revenir en arrière à une version qui fonctionnait, cliquez sur le bouton &lt;b&gt;versions&lt;/b&gt; en haut à droite du &lt;b&gt;Class Browser&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SkuCYcI5uzI/AAAAAAAAAIw/iivargxzqW8/s1600-h/pharo32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SkuCYcI5uzI/AAAAAAAAAIw/iivargxzqW8/s320/pharo32.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;L'historique de la méthode apparaît et vous pouvez reprendre une version antérieure en cliquant sur &lt;b&gt;revert&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. Interlude &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Si &lt;b&gt;Movies#size&lt;/b&gt; fonctionne comme voulu, tests à l'appui, la fonction de stockage d'instances de &lt;b&gt;Movie&lt;/b&gt; reste à réaliser.&lt;br /&gt;&lt;br /&gt;Comme l'article deviens long, nous verrons ceci dans une seconde partie. &lt;br /&gt;&lt;br /&gt;Nous avons vu les différentes étapes du TDD (Test Driven Development):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;identifier une fonctionnalité&lt;/li&gt;&lt;li&gt; écrire le test&lt;/li&gt;&lt;li&gt;faire passer le test le plus rapidement possible&lt;/li&gt;&lt;li&gt;améliorer le code pour supprimer les duplications, simplifier, documenter, ...&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://www.stickyminds.com/images_upload/bk675_1727.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://www.stickyminds.com/images_upload/bk675_1727.jpg" /&gt;&lt;/a&gt;Nous avons mis en œuvre quelques outils parmi la multitude que Pharo offre pour nous accompagner dans cette démarche.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Pour finir, les exemples sont fortement inspirés de l'ouvrage "&lt;a href="http://www.amazon.fr/Test-Driven-Development-Practical-Guide/dp/0131016490"&gt;Test Driven Development: A Practical Guide&lt;/a&gt;" de Dave Astels.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4643116618223759375?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4643116618223759375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-2-les.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4643116618223759375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4643116618223759375'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/07/pharo-et-tests-unitaires-2-les.html' title='Pharo et tests unitaires (2): les collections, première partie'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/SkkdF5VS5EI/AAAAAAAAAGQ/jb90EyjQIxM/s72-c/pharo12.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-7557017097556202006</id><published>2009-06-26T22:17:00.021+02:00</published><updated>2009-12-13T14:55:46.212+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Pharo et tests unitaires: utilisation des outils</title><content type='html'>&lt;div style="text-align: left;"&gt;Voici le premier billet  d'une petite trilogie d'introduction sur l'écriture&amp;nbsp; de tests unitaires dans Pharo. La série abordera les sujets suivants:&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt; utilisation des outils de test et debuggage (ce billet)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;écriture et test d'une Collection&lt;/li&gt;&lt;li&gt;utilisation des Traits dans les tests unitaires&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Pour illustrer mes propos, je prendrai l'exemple extrêmement original de la manipulation d'une liste de films. Je veux créer des films, les ajouter à une liste et connaître le nombre total de films &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Création du package Movies:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SkPO2JkMFaI/AAAAAAAAAFQ/eIiUzmhFvOI/s1600-h/pharo1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SkPO2JkMFaI/AAAAAAAAAFQ/eIiUzmhFvOI/s200/pharo1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Pour commencer, créons le package &lt;b&gt;Movies&lt;/b&gt; dans lequel nous mettrons toutes nos classes.&amp;nbsp; Ouvrez le &lt;b&gt;Class Browser&lt;/b&gt;, clic-droit dans la première colonne et sélectionnez &lt;b&gt;create package&lt;/b&gt;. Saisissez &lt;b&gt;Movies&lt;/b&gt; comme nom puis valider.&lt;br /&gt;&lt;br /&gt;Le package &lt;b&gt;Movies&lt;/b&gt; apparaît dans la liste classée par ordre alphabétique. Comme nous allons l'utiliser souvent, plaçons-le en tête de liste pour le retrouver facilement. Clic-droit sur le package et sélectionnez &lt;b&gt;place package on top&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Notre premier test unitaire&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;Passons ensuite à la classe &lt;b&gt;Movie&lt;/b&gt;. Un film ayant un titre, nous allons vérifier qu'une instance de &lt;b&gt;Movie&lt;/b&gt; possède un &lt;b&gt;accesseur title&lt;/b&gt; qui retourne la chaîne passée à la construction de l'instance.&lt;br /&gt;&lt;br /&gt;En développement piloté par les tests, on commence par écrire ... le test.&lt;br /&gt;&lt;br /&gt;Déclarons une classe &lt;b&gt;MovieTest&lt;/b&gt; qui hérite &lt;b&gt;TestCase&lt;/b&gt; (classe de base pour les tests unitaires). Dans le panneau d'édition du&lt;b&gt; Class Browser&lt;/b&gt;, saisissez le code suivant:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;TestCase subclass: #MovieTest&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instanceVariableNames: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;classVariableNames: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;poolDictionaries: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;category: 'Movies'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pour notre test, nous créons deux instances de la classe &lt;b&gt;Movie&lt;/b&gt; en spécifiant leurs titres respectifs. Nous vérifions que les titres ont bien été assignés en utilisant la méthode &lt;b&gt;assert&lt;/b&gt; de &lt;b&gt;TestCase&lt;/b&gt;. Ajoutons la procédure &lt;b&gt;testMovieHasATitle&lt;/b&gt; comme ceci:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;testMovieHasATitle&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| starWars bladeRunner |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;starWars:= Movie newWithTitle: 'Star Wars'.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bladeRunner:= Movie newWithTitle: 'Blade Runner'.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self assert: starWars title = 'Star Wars'.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self assert: bladeRunner title = 'Blade Runner'&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SkPWEoBHGgI/AAAAAAAAAFY/bhRKW-leqYU/s1600-h/pharo2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SkPWEoBHGgI/AAAAAAAAAFY/bhRKW-leqYU/s320/pharo2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Lors de la sauvegarde de la procédure, Pharo détecte que la classe &lt;b&gt;Movie&lt;/b&gt; utilisée n'existe pas et nous propose de la déclarer. Ne nous en privons pas.&lt;br /&gt;&lt;br /&gt;Pharo demande de saisir la définition de la classe et propose &lt;b&gt;Object subclass: #Movie&lt;/b&gt;, ce qui conviens dans notre cas. De même pour la confirmation d'ajout de la classe dans le &lt;b&gt;package Movie&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Lançons maintenant notre test unitaire. Clic droit sur la classe &lt;b&gt;MovieTest&lt;/b&gt; et sélectionnez &lt;b&gt;run the tests&lt;/b&gt; (si vous préférez les raccourcis clavier, tapez Ctl+t).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: right;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SkUdetZC9xI/AAAAAAAAAFg/6xwSQx8lLfE/s1600-h/pharo5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SkUdetZC9xI/AAAAAAAAAFg/6xwSQx8lLfE/s320/pharo5.png" /&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SkUd9FIrlSI/AAAAAAAAAFo/iPSmCRn0I1o/s1600-h/pharo6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SkUd9FIrlSI/AAAAAAAAAFo/iPSmCRn0I1o/s320/pharo6.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: left;"&gt;Pharo lance le test, détecte une erreur et nous propose d'ouvrir le debugger, ce que nous allons faire.&lt;br /&gt;&lt;/div&gt;&lt;div class="" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;Implémentation de la classe Movie:&lt;/b&gt; &lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&amp;nbsp;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Le debugger nous présente la pile d'appel qui a conduit à l'erreur.&amp;nbsp; Comme par hasard ;), la pile d'appel contient notre méthode &lt;b&gt;MovieTest#testMovieHasATitle&lt;/b&gt;. En cliquant dessus, le debugger affiche le détail de la méthode et indique la ligne source de l'exception: on appelle la méthode inexistante &lt;b&gt;newWithTitle&lt;/b&gt; de la classe &lt;b&gt;Movie&lt;/b&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SkUfQVXxOHI/AAAAAAAAAFw/H6n4o-rjsQo/s1600-h/pharo7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SkUfQVXxOHI/AAAAAAAAAFw/H6n4o-rjsQo/s320/pharo7.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Retournons dans le &lt;b&gt;Class Browser&lt;/b&gt; pour l'ajouter. Sélectionnez la classe &lt;b&gt;Movie&lt;/b&gt;. Etant donné qu'on veut créer une méthode de classe, n'oubliez pas de cliquez sur le &lt;b&gt;bouton class&lt;/b&gt;. La méthode doit créer une nouvelle instance de la classe Movie, lui assigner son titre et retourner l'instance.&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;newWithTitle: aString&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| movie |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;movie := Movie new.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;movie title: aString.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;^ movie&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Une fois la méthode acceptée, retournez dans le debugger et cliquez sur le bouton &lt;b&gt;Proceed&lt;/b&gt; pour continuez l'exécution du test là où elle s'était arrêtée (si vous avez fermé le debugger, relancez le test).&lt;br /&gt;&lt;br /&gt;Cette fois, le debugger va plus loin et s'arrête dans notre nouvelle méthode &lt;b&gt;Movie#newWithTitle&lt;/b&gt; car l'instance de &lt;b&gt;Movie&lt;/b&gt; ne connaît pas la méthode&lt;b&gt; title:aString&lt;/b&gt;.&lt;b&gt; &lt;/b&gt;Ajoutons-là dans le &lt;b&gt;Class Browser&lt;/b&gt; (n'oubliez pas de cliquer sur le bouton &lt;b&gt;instance&lt;/b&gt;):&lt;br /&gt;&lt;pre&gt;title: aString&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title := aString&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SkW_vdgjzZI/AAAAAAAAAGI/yrxB28ufw_Q/s1600-h/pharo11.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SkW_vdgjzZI/AAAAAAAAAGI/yrxB28ufw_Q/s320/pharo11.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Lorsque vous acceptez la méthode, Pharo indique qu'il ne connaît pas la variable &lt;b&gt;title&lt;/b&gt;. Comme cette variable est un attribut d'instance, sélectionnez &lt;b&gt;declare instance&lt;/b&gt;. Si vous cliquez de nouveau sur la classe &lt;b&gt;Movie&lt;/b&gt; pour afficher sa définition, vous apercevrez notre nouvelle variable d'instance.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Object subclass: #Movie&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; instanceVariableNames: '&lt;b&gt;title&lt;/b&gt;'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; classVariableNames: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; poolDictionaries: ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; category: 'Movies'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Continuons l'exécution en cliquant sur &lt;b&gt;Proceed&lt;/b&gt;. Nous venons de créer la méthode &lt;b&gt;Movie#title:&lt;/b&gt; pour assigner le titre, il nous manque la méthode &lt;b&gt;Movie#title&lt;/b&gt; pour le lire. Ajoutez là:&lt;br /&gt;&lt;pre&gt;title&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ^ title&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Cette fois-ci, victoire !!&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SkUqTT_KTdI/AAAAAAAAAGA/qrkguo66pq4/s1600-h/pharo10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SkUqTT_KTdI/AAAAAAAAAGA/qrkguo66pq4/s320/pharo10.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Vous remarquerez le voyant vert indiquant que la totalité des tests de notre classe sont passés.&lt;br /&gt;&lt;br /&gt;Notez aussi que l'implémentation de la classe &lt;b&gt;Movie&lt;/b&gt; a été pilotée par le test: c'est en suivant les erreurs indiquées par le debugger que nous avons écrit le code réel. C'est une des raisons pour considérer le code de test plus important que le code réel.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;"OH: the trick is to write unit tests while you're sober, then write the code while you're drunk."&lt;/i&gt;, auteur dont j'ai oublié de noter le nom.&lt;br /&gt;&lt;br /&gt;Dans le prochain billet, nous écrirons la classe &lt;b&gt;Movies&lt;/b&gt; qui recense les films et les tests associés.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-7557017097556202006?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/7557017097556202006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/06/pharo-et-tests-unitaires-utilisation.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7557017097556202006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/7557017097556202006'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/06/pharo-et-tests-unitaires-utilisation.html' title='Pharo et tests unitaires: utilisation des outils'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JLK6EDfW4so/SkPO2JkMFaI/AAAAAAAAAFQ/eIiUzmhFvOI/s72-c/pharo1.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3283305245341925627</id><published>2009-06-16T23:19:00.020+02:00</published><updated>2009-12-13T14:56:06.327+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='seaside'/><category scheme='http://www.blogger.com/atom/ns#' term='pharo'/><title type='text'>Jouons avec Pharo et Seaside</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;J'ai assisté il y a quelque temps a une conférence de Stéphane Ducasse à l'Institut de Management d'Annecy. Co-créateur de projet &lt;a href="http://www.pharo-project.org/"&gt;Pharo&lt;/a&gt; et développeur actif de la communauté Smalltalk, Stéphane nous a présenté différents outils dont &lt;a href="http://seaside.st/"&gt;Seaside&lt;/a&gt;, un framework dédié à l'écriture d'applications Web (en insistant sur le terme application).&lt;br /&gt;&lt;br /&gt;Pharo est une implémentation Open Source du langage Smalltalk, qui se veut moderne, agile et cohérente. Pharo est né comme un fork de &lt;a href="http://www.squeak.org/"&gt;Squeak&lt;/a&gt;, puis a évolué en nettoyant l'implémentation des parties obsolètes, bancales ou instables, en simplifiant l'intéraction avec l'environnement de développement et en intégrant des outils de développement efficaces. De plus, chose essentielle, ils ont créé un joli logo.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgS6Rn7VII/AAAAAAAAAE4/YqUVvEzLKAU/s1600-h/pharo9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgS6Rn7VII/AAAAAAAAAE4/YqUVvEzLKAU/s320/pharo9.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;La première version stable 1.0 ne devrait plus trop se faire attendre (vers l'automne ?)&lt;br /&gt;&lt;br /&gt;J'avais déjà assisté a une présentation de Seaside aux RMLL à Mont-de-Marsan. Mais je voulais d'abord découvrir Smalltalk et du coup je suis passé à côté d'un des principaux fonctionnements novateurs qu'apporte Seaside au développements Web.&lt;br /&gt;&lt;br /&gt;Stéphane a parcouru un diaporama de Lukas Renggli "The Heretic Web Framework" disponible &lt;a href="http://www.lukas-renggli.ch/smalltalk/seaside" target="_blank"&gt;ici&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Prenons un exemple simple d'application "classique": nous voulons demander deux nombres et afficher le résultat dans une console. En python nous pourrions coder comme suit:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;value1 = input("Premier nombre: ")&lt;br /&gt;value2 = input("Second nombre: ")&lt;br /&gt;print(value1+value2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tant que l'utilisateur n'a pas saisi de nombre, le programme reste en attente sur la fonction &lt;b&gt;input&lt;/b&gt; Nous utilisons le retour de la fonction pour stocker les deux valeurs. &lt;br /&gt;&lt;br /&gt;Imaginons la même application en environnement Web. Le programme de calcul fonctionne côté serveur et l'utilisateur se sert de son navigateur Web pour saisir les valeurs. La plupart des systèmes actuels nous propose de créer une vue ou template pour définir l'interface utilisateur, un contrôlleur pour exécuter les requêtes client et renvoyer le résultat. Nous devons généralement implémenter un graphe d'état et manipuler les sessions pour savoir si nous en sommes à la demande de la première ou seconde valeur.&lt;br /&gt;&lt;br /&gt;Seaside permet de programmer de la même manière qu'une application classique. L'équivalent Seaside/Smalltalk du code python précèdent est:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;| value1 value2 |&lt;br /&gt;value1 := self request: ‘Premier nombre’.&lt;br /&gt;value2 := self request: ‘Second nombre’.&lt;br /&gt;self inform: value1 asNumber + value2 asNumber.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On n'est pas dépaysé....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Maintenant un peu de concret. Téléchargez la machine virtuelle Pharo et l'image intégrant Seaside sur &lt;a href="http://pharo-project.org/download"&gt;le site du projet&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SjgLuqKcS3I/AAAAAAAAAEI/SMbX0ruSIhk/s1600-h/pharo1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SjgLuqKcS3I/AAAAAAAAAEI/SMbX0ruSIhk/s400/pharo1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Sous Linux, on doit malheureusement bricoler un peu. Décompressez la machine virtuelle puis activez le bit d'exécution du binaire squeak:&lt;br /&gt;&lt;pre&gt;unzip pharo-vm-0.15.1b-linux.zip&lt;br /&gt;chmod +x pharo-vm-0.15.1b-linux/squeak&lt;br /&gt;&lt;/pre&gt;On peut ensuite décompresser l'image et la lancer:&lt;br /&gt;&lt;pre&gt;unzip pharo0.1-10324web09.06.1.zip&lt;br /&gt;pharo-vm-0.15.1b-linux/squeak pharo0.1-10324web09.06.1/pharo0.1-10324web09.06.1.image&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgYcese1bI/AAAAAAAAAFA/Q0JAxId42Jk/s1600-h/pharo10.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgYcese1bI/AAAAAAAAAFA/Q0JAxId42Jk/s200/pharo10.png" /&gt;&lt;/a&gt;L'image ouverte, Seaside tourne sur le port 8080. Vous pouvez le vérifier en pointant votre navigateur Web à l'adresse &lt;a href="http://localhost:8080/seaside"&gt;http://localhost:8080/seaside&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dans Seaside, les &lt;b&gt;points d'entrée&lt;/b&gt; de nos applications doivent être définis. Nous allons créer une nouvelle classe de type &lt;b&gt;WATask&lt;/b&gt;, un "contrôlleur" Seaside, et l'assigner à une URL.&lt;br /&gt;&lt;br /&gt;Les classes peuvent être définies un utilisant le &lt;b&gt;Class Browser&lt;/b&gt;, via le menu &lt;b&gt;World&lt;/b&gt; ouvert par un clic sur le bureau Pharo. Dans la première colonne, sélectionnez &lt;b&gt;Unpackaged&lt;/b&gt; (tout en bas) pour créer une nouvelle classe sans la placer dans un package particulier. Dans la zone de saisie du bas, saisissez le code suivant pour déclarer la classe &lt;b&gt;Additionneur&lt;/b&gt; qui hérite &lt;b&gt;WATask&lt;/b&gt;.&lt;br /&gt;&lt;pre&gt;WATask subclass: #Additionneur&lt;br /&gt;    instanceVariableNames: ''&lt;br /&gt;    classVariableNames: ''&lt;br /&gt;    poolDictionaries: ''&lt;br /&gt;    category: 'Unpackaged'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ouvrez le menu contextuel de la zone de saisie via un clic droit et sélectionnez &lt;b&gt;accept&lt;/b&gt; pour enregistrer la classe.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgNW6IJb3I/AAAAAAAAAEg/D_r-g_7L93A/s1600-h/pharo6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgNW6IJb3I/AAAAAAAAAEg/D_r-g_7L93A/s400/pharo6.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Ceci fait, notre classe doit se déclarer comme application accessible directement via un point d'entrée. Pour trouver ce type d'application, Seaside parcours toutes les classes héritant &lt;b&gt;WAComponent&lt;/b&gt; (qu'hérite WATask et donc Additionneur) et sélectionne celles dont le retour de la méthode &lt;b&gt;canBeRoot&lt;/b&gt; est &lt;b&gt;true&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;(Mise à jour: d'autres moyens sont disponibles, voir le &lt;a href="http://magaloma.blogspot.com/2009/06/jouons-avec-pharo-et-seaside.html?showComment=1245224522797#c9218567583895906352"&gt;commentaire de Damien&lt;/a&gt;) &lt;br /&gt;&lt;br /&gt;Pour ajouter une méthode de classe, sélectionnez la classe &lt;b&gt;Additionneur&lt;/b&gt; dans la seconde colonne puis cliquez sur le bouton &lt;b&gt;class&lt;/b&gt; pour visualiser les méthodes de classe. Sélectionnez la catégorie &lt;b&gt;-- all --&lt;/b&gt; dans la troisième colonne, puis entrez le code suivant dans la zone de saisie:&lt;br /&gt;&lt;pre&gt;canBeRoot &lt;br /&gt;    ^true&lt;br /&gt;&lt;/pre&gt;Enregistrez alors la méthode via la commande &lt;b&gt;accept&lt;/b&gt; du menu contextuel.&lt;br /&gt;&lt;br /&gt;Vérifions que notre Additionneur peut être accessible depuis l'interface de configuration de Seaside. Ouvrez la page en question depuis votre navigateur Web à l'adresse suivante: &lt;a href="http:" target="_blank"&gt;http://localhost:8080/seaside/config&lt;/a&gt;. Le compte par défaut est admin / seaside.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgL6chjsCI/AAAAAAAAAEQ/jfPY2hYLu5E/s1600-h/pharo3.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JLK6EDfW4so/SjgL6chjsCI/AAAAAAAAAEQ/jfPY2hYLu5E/s320/pharo3.png" /&gt;&lt;/a&gt;Dans la section &lt;b&gt;Settings&lt;/b&gt;, &lt;b&gt;Add an entry point&lt;/b&gt;, saisissez le nom de votre nouveau point d'entrée, par exemple &lt;b&gt;additionneur&lt;/b&gt;. Je précise que ce nom est indépendant du nom de la classe (au cas où vous seriez un développeur Rails ;) ). Sélectionnez &lt;b&gt;Application&lt;/b&gt; comme &lt;b&gt;Type&lt;/b&gt; puis cliquez sur &lt;b&gt;Add&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SjgMCQ78oMI/AAAAAAAAAEY/GCvO7fuF0mw/s1600-h/pharo2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SjgMCQ78oMI/AAAAAAAAAEY/GCvO7fuF0mw/s320/pharo2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Vous entrez alors dans la page de configuration de l'application. Sous la section &lt;b&gt;Configuration/General&lt;/b&gt;, modifiez le paramètre &lt;b&gt;Root Component&lt;/b&gt; pour sélectionnez la classe &lt;b&gt;Additionneur&lt;/b&gt; dans la liste déroulante. Cliquez enfin sur &lt;b&gt;Save&lt;/b&gt; en bas de la page.&lt;br /&gt;&lt;br /&gt;En cliquant sur &lt;b&gt;Close&lt;/b&gt; vous revenez à la page principale de configuration de Seaside. &lt;br /&gt;&lt;br /&gt;Vous devriez voir apparaître le point d'entrée &lt;b&gt;additionneur&lt;/b&gt; associé à la classe &lt;b&gt;Additionneur&lt;/b&gt; dans la liste&lt;b&gt; /seaside/&lt;/b&gt;. &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgReRbO8SI/AAAAAAAAAEw/BnTRf2OsKfQ/s1600-h/pharo8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgReRbO8SI/AAAAAAAAAEw/BnTRf2OsKfQ/s320/pharo8.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Si vous cliquez sur le lien maintenant, vous aurez une erreur car nous n'avons pas encore implémenté le fonctionnement de notre application, ce que nous allons écrire de suite.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SjikVj7bHjI/AAAAAAAAAFI/fD4nHR_jhyc/s1600-h/pharo11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SjikVj7bHjI/AAAAAAAAAFI/fD4nHR_jhyc/s320/pharo11.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Retournez dans Pharo. Seaside cherche à évaluer la méthode &lt;b&gt;go&lt;/b&gt; d'une instance de notre classe &lt;b&gt;Additionneur&lt;/b&gt;. Pour l'ajouter, sélectionnez d'abord les méthodes d'instance en cliquant sur le bouton &lt;b&gt;instance&lt;/b&gt; en dessous de la seconde colonne du &lt;b&gt;Class Browser&lt;/b&gt;. Cliquez ensuite sur la catégorie&lt;b&gt; -- all --&lt;/b&gt; et saisissez le code présenté précédemment:&lt;br /&gt;&lt;pre&gt;| value1 value2 |&lt;br /&gt;value1 := self request: ‘Premier nombre’.&lt;br /&gt;value2 := self request: ‘Second nombre’.&lt;br /&gt;self inform: value1 asNumber + value2 asNumber.&lt;br /&gt;&lt;/pre&gt;puis enregistrez-la (clic droit, accept).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JLK6EDfW4so/SjgLa1kuE5I/AAAAAAAAAEA/2vdMqOOhUdI/s1600-h/pharo4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JLK6EDfW4so/SjgLa1kuE5I/AAAAAAAAAEA/2vdMqOOhUdI/s400/pharo4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Maintenant pointez votre navigateur sur l'application à l'adresse suivante: &lt;a href="http://localhost:8080/seaside/additionneur%20"&gt;http://localhost:8080/seaside/additionneur &lt;/a&gt;et goûtez à la joie d'avoir une application qui fonctionne du premier coup.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgLUx3YGVI/AAAAAAAAAD4/5YNxDSFn9ag/s1600-h/pharo5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JLK6EDfW4so/SjgLUx3YGVI/AAAAAAAAAD4/5YNxDSFn9ag/s320/pharo5.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Si ce n'est pas le cas, je décline toute responsabilité quant aux dommages irréversibles que pourraient causer mes indications sur la santé de votre ordinateur...&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3283305245341925627?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3283305245341925627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/06/jouons-avec-pharo-et-seaside.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3283305245341925627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3283305245341925627'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/06/jouons-avec-pharo-et-seaside.html' title='Jouons avec Pharo et Seaside'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JLK6EDfW4so/SjgS6Rn7VII/AAAAAAAAAE4/YqUVvEzLKAU/s72-c/pharo9.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8560005400469425813</id><published>2009-06-05T22:41:00.009+02:00</published><updated>2009-06-07T19:39:54.589+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Pages du matin</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;a href="http://ecx.images-amazon.com/images/I/51xArZnegaL._SL160_AA115_.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="96" src="http://ecx.images-amazon.com/images/I/51xArZnegaL._SL160_AA115_.jpg" width="96" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SimDPU6Iq7I/AAAAAAAAADY/8Ts2bWPqFKE/s1600-h/mindperfhks.s.gif" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SimDPU6Iq7I/AAAAAAAAADY/8Ts2bWPqFKE/s200/mindperfhks.s.gif" /&gt;&lt;/a&gt;Dans "&lt;a href="http://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;Pragmatic Thinking and Learning&lt;/a&gt;", Andy Hunt expose plusieurs techniques pour simuler notre prodution d'idées et ne pas les perdre. L'écriture revets une impordance primordiale, et A.Hunt nous conseille de garder constamment à portée de main de quoi écrire - que ce soit un PDA, un carnet ou tout autre support. On retrouve des approches similaires dans &lt;a href="http://www.amazon.com/Mind-Performance-Hacks-Tools-Overclocking/dp/0596101538"&gt;"Mind Performance Hacks"&lt;/a&gt; de Ron Hale-Hevans (tip 13: Catch Your Ideas)&lt;br /&gt;&lt;br /&gt;Pour arriver à imprimer notre créativité sur papier, A.Hunt nous présente la technique des &lt;i&gt;Morning Pages&lt;/i&gt;. Cette technique s'appuie sur le constat que notre cerveau droit (le plus créatif, voir mon billet précédent "&lt;a href="http://magaloma.blogspot.com/2009/05/pomodoro-technique-et-cerveau-droit.html" target="_blank"&gt;Pomodoro technique et cerveau droit&lt;/a&gt;") bouillone d'activité la nuit. Nous rêvons, pensons et génêrons des hypothèses, situations improbables, du moins pour notre cerveau gauche. Lorsque nous nous levons, notre cerveau droit laisse peu à peu le cerveau gauche prendre la majorité de nos pensées.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Les &lt;i&gt;Morning Pages&lt;/i&gt; sont décrites comme un moyen de capter cette effervescence de notre cerveau droit. Le principe s'avère simple. Tous les matins, la première chose à effectuer (avant même de préparer le café, réveiller les enfants, ...) est de s'asseoir à table, prendre un stylo et écrire trois pages. On ne doit pas réfléchir à ce qu'on écrit, ne pas tenter de diriger ses idées ou essayer de formuler ses phrases. Ecrivez vos pensées telles qu'elles arrivent, même si cela semble stupide, illisible, ...&lt;br /&gt;&lt;br /&gt;Vous verrez que progressivement se dessinent des choses auxquelles vous n'avez jamais pensé, relatives au travail ou à votre quotidien. Des idées de développement de logiciels, plans de production, organisation d'un week-end en famille, ... &lt;br /&gt;&lt;br /&gt;De plus, je trouve que cette manière d'écrire très agréable, rapide et nous donne l'impression d'avoir accomplit quelque chose dès le matin. Si vous voulez avoir un fou rire en soirée, il suffit parfois de relire vos Morning Pages de quelques jours passés, on s'étonne soi-même ;)&lt;br /&gt;&lt;br /&gt;Ceci dit, la régularité m'apparaît plus importante que la quantité. On peut faire une analogie avec la musique; il est préférable de travailler son instrument 20 mn tous les jours que 5 heures seulement le dimanche. J'ai trouvé assez difficile d'écrire trois pages régulièrement tout de suite. J'ai finalement commencé par faire une page, puis ma rapidité d'écriture s'améliorant, la quantité progresse naturellement.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8560005400469425813?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8560005400469425813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/06/pages-du-matin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8560005400469425813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8560005400469425813'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/06/pages-du-matin.html' title='Pages du matin'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/SimDPU6Iq7I/AAAAAAAAADY/8Ts2bWPqFKE/s72-c/mindperfhks.s.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4703845594708232209</id><published>2009-06-01T22:15:00.009+02:00</published><updated>2009-06-02T09:15:11.399+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Ruby et bases MS Access: Olé !!</title><content type='html'>&lt;p&gt;J'ai eu besoin d'automatiser quelques manipulations de bases de données MS-Access. Une possibilité en Ruby est d'utiliser les ActiveX ADO grâce au module &lt;strong&gt;win32ole&lt;/strong&gt;. Je vais montrer l'utilisation de ce module avant d'attaquer MS Access&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Voici un exemple de pilotage d'Internet Explorer via OLE. Commençons par lancer &lt;strong&gt;irb&lt;/strong&gt; et importer le module &lt;strong&gt;win32ole&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;C:\sandbox&amp;gt;irb&lt;br /&gt;irb(main):001:0&amp;gt; require "win32ole"&lt;br /&gt;=&amp;gt; true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Créons une nouvelle instance d'Internet Explorer:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;ie = WIN32OLE.new("InternetExplorer.Application")&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Pour connaître la liste des attributs d'un objet OLE, on utilise &lt;strong&gt;WIN32OLE::ole_get_methods&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;irb(main):002:0&amp;gt; ie.Application.ole_get_methods&lt;br /&gt;=&amp;gt; [Application, Parent, Container, Document, TopLevelContainer, Type, Left, Top, &lt;br /&gt;Width, Height, LocationName, LocationURL, Busy, Name, HWND, FullName, Path, &lt;br /&gt;Visible, StatusBar, StatusText, ToolBar, MenuBar, FullScreen, ReadyState, Offline,&lt;br /&gt;Silent, RegisterAsBrowser, RegisterAsDropTarget, TheaterMode, AddressBar, &lt;br /&gt;Resizable]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Affichons la fenêtre et allons sur un site au hasard :)&lt;/p&gt;&lt;pre&gt;irb(main):003:0&amp;gt; ie.Visible=true&lt;br /&gt;=&amp;gt; true&lt;br /&gt;irb(main):004:0&amp;gt; ie.Navigate("http://magaloma.blogspot.com")&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Fini de jouer:&lt;/p&gt;&lt;pre&gt;irb(main):005:0&amp;gt; ie.Quit&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p dir="ltr"&gt;Pour se connecter à une base MS Access, nous utilisons l'ActiveX &lt;strong&gt;ADODB.Connection&lt;/strong&gt;. Par exemple:&lt;/p&gt;&lt;pre&gt;dbcon = WIN32OLE.new("ADODB.Connection")&lt;br /&gt;dbcon.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=bibliotheque.mdb")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p dir="ltr"&gt;&lt;strong&gt;ADOX.Catalog&lt;/strong&gt; perment d'inspecter la base:&lt;/p&gt;&lt;pre&gt;catalog = WIN32OLE.new('ADOX.Catalog')&lt;br /&gt;catalog.ActiveConnection = dbcon&lt;br /&gt;catalog.Tables.count&lt;br /&gt;=&amp;gt; 11&lt;br /&gt;catalog.Tables.Item(0).Name&lt;br /&gt;=&amp;gt; "auteurs"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p dir="ltr"&gt;On peut ensuite récupérer quelques enregistrements avec &lt;strong&gt;ADODB.Recordset&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;recordset = WIN32OLE.new('ADODB.Recordset')&lt;br /&gt;recordset.Open("SELECT * FROM livres", dbcon)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p dir="ltr"&gt;Pour ressortir les noms des champs:&lt;/p&gt;&lt;pre&gt;recordset.Fields.each {|f| puts f.Name}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Ce qui nous retourne:&lt;/p&gt;id&lt;br /&gt;titre&lt;br /&gt;autheur_id&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;GetRows&lt;/strong&gt; permet de récupérer les données:&lt;/p&gt;&lt;pre&gt;recordset.GetRows.transpose&lt;br /&gt;=&amp;gt; [[1, "Pragmatic Thinking And Learning", 1], [2, "Art Of Unix Programming", 2]&lt;br /&gt;, [3, "Practices Of An Agile Developper", 1]]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Voilà. Merci au site &lt;a href="http://rubyonwindows.blogspot.com" title="Ruby On Windows" target="_blank"&gt;Ruby On Windows&lt;/a&gt; pour m'avoir bien aidé.&lt;/p&gt;&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4703845594708232209?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4703845594708232209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/06/ruby-et-bases-ms-access-ole.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4703845594708232209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4703845594708232209'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/06/ruby-et-bases-ms-access-ole.html' title='Ruby et bases MS Access: Olé !!'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8158194760405582583</id><published>2009-05-26T23:11:00.002+02:00</published><updated>2009-06-01T22:48:31.017+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><title type='text'>Fin d'une journée Pomodoresque</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div&gt;&lt;p&gt;La journée de travail touche à sa fin, il ne me reste pas assez de temps pour commencer un nouveau Pomodoro. Il est temps d'analyser puis de synthétiser les activités du jour.&lt;/p&gt;&lt;p&gt;Dans un premier temps, je reporte sur la feuille d'&lt;strong&gt;Enregistrements&lt;/strong&gt; les activités pour lesquelles j'ai alloué des Pomodori. Je note la date du jour dans une première colonne.&lt;/p&gt;&lt;p&gt;En face de chaque activité, j'indique ensuite l'écart entre l'estimation du nombre de Pomodori nécessaires à l'accomplissement de l'activité de ceux réellement utilisés. Par exemple en début de journée j'ai estimé la tâche "Logiciel XXX: en-tête, afficher la date + heure du serveur" à 2 Pomodori et j'en ai finalement utilisé 3. J'obtiens donc un écart de -1.&lt;/p&gt;&lt;p&gt;Je note ensuite le nombre total de Pomodori réalisés ainsi que le nombre total d'interruptions.&lt;/p&gt;&lt;p&gt;Je prends alors mes stabilos de couleur et je surligne ce qui me paraît important (gros écarts, activité qui s'est mal déroulée, ...). Je cherche à décrire les causes des écarts et interruptions ainsi que les solutions possibles à l'amélioration de mon process.&lt;/p&gt;&lt;p&gt;Finalement, je retourne ma feuille d'&lt;strong&gt;Activités du Jour&lt;/strong&gt; et je dessine un&lt;strong&gt; Mind Map &lt;/strong&gt;pour faire ressortir les points forts de la journée que je veux retenir.&lt;/p&gt;&lt;p&gt;Ce que j'aime dans ces pratiques est que d'une part on se sent construire une expérience, de l'autre on arrive trés vite à se remettre &lt;em&gt;dans le bain&lt;/em&gt; le lendemain ou après le week-end. En début de journée je relis le Mind Map de la veille et la feuille d'Enregistrements avant de planifier la journée. Le Lundi matin je relis la totalité des Mind Maps de la semaine précédente.&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8158194760405582583?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8158194760405582583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/fin-d-journee-pomodoresque.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8158194760405582583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8158194760405582583'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/fin-d-journee-pomodoresque.html' title='Fin d&amp;#39;une journée Pomodoresque'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1007157376167882457</id><published>2009-05-15T22:32:00.002+02:00</published><updated>2009-05-16T15:06:45.136+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><title type='text'>Un Pomodo est indivisible</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;p&gt;Un Pomodoro est indivisible, il n'existe pas de demi ou quart de Pomodoro. Deux évènements peuvent nous inciter à diviser un Pomodoro et nous faire basculer du côté obscur:&lt;br/&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;nous n'arrivons pas à gérer une interruption (question d'un collègue, coup de téléphone, ...) en plein Pomodoro.&lt;/li&gt;&lt;li&gt;nous terminons une activité avant la fin du Pomodoro.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Voyons ce que la règle d'indivisibilité nous apporte dans chaque cas.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;strong&gt;1. L'interruption&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;Si nous interrompons un Pomodoro, on doit le considérer annulé. Pas de croix sur sa feuille du jour, pas de récompense. Cela peut s'avérer trés frustrant lorsqu'il ne reste plus que deux minutes au Pomodoro. Ou bien quand cinq fois de suite nous commençons notre Pomodoro et à chaque fois quelqu'un a besoin de nous au bout d'une minute.&lt;br/&gt;Cette frustration va nous pousser à trouver des solutions pour ne pas être interrompu et augmenter le nombre de Pomodori réalisés par jour. En voici quelques unes:&lt;br/&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;le téléphone sonne: laissez le répondeur répondre à votre place. Vous pouvez toujours rappeler à la fin de votre Pomodoro. &lt;/li&gt;&lt;li&gt;un nouveau mail arrive: le mieux est de fermer sa messagerie.&lt;/li&gt;&lt;li&gt;un collègue viens vous voir: demandez si c'est vraiment urgent et s'il est possible de voir ça dans quelques minutes, le temps de finir la tâche en cours. &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Dans chaque cas, marquez sur votre feuille ce que vous devrez faire une fois le Pomodoro terminé pour ne pas l'oublier.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;strong&gt;2. L'activité terminée&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;Vous venez de terminer ce que vous étiez en train de faire (un devis, développement, ...) mais il reste encore dix minutes au Pomodoro. A ce moment là il faut rester sur la même activité et profiter de ce temps pour passer l'activité en revue:&lt;br/&gt;Pour un développement, relisez le code écrit. Vérifiez s'il est compréhensible, si des implémentations alternatives sont possibles (voire souhaitables), si les tests unitaires couvrent tous les cas. Pour un devis, vérifier les temps, coûts, comparer avec d'autres devis. Demandez-vous si vous auriez pu réaliser votre tâche en moins de temps, comment, notez-le.&lt;br/&gt;Le Pomodoro nous oblige à vérifier ce que nous venons de faire. On évite d'expédier un peu trop vite notre travail et cela nous donne confiance dans nos réalisations. Dans les périodes où la pression se fait ressentir, bien des erreurs peuvent ainsi être évitées.&lt;/p&gt;&lt;p&gt;&lt;img height='1' width='1' src='http://res1.blogblog.com/tracker/3454033395947617111-1007157376167882457?l=magaloma.blogspot.com'/&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1007157376167882457?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1007157376167882457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/un-pomodo-est-indivisible.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1007157376167882457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1007157376167882457'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/un-pomodo-est-indivisible.html' title='Un Pomodo est indivisible'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-3834278834932635163</id><published>2009-05-14T22:23:00.002+02:00</published><updated>2009-05-16T13:23:13.034+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><title type='text'>Le Pomodoro au quotidien</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;div dir="ltr"&gt;&lt;b&gt;Documents:&lt;/b&gt;&lt;/div&gt;A l'instar de Scrum avec son Backlog, Pomodo necessite tout d'abord un &lt;b&gt;Inventaire des Activités&lt;/b&gt;. On peut prendre une simple feuille A4 sur laquelle on écrit une activité / tâche par ligne. Lorsqu'une acitvité se termine, on la raye. Si la feuille est pleine (environ 25 activités) et qu'on a besoin d'en rajouter, on prends une nouvelle feuille vierge. On y reporte les activités toujours d'actualité ainsi que les nouvelles.&lt;br /&gt;Une nouvelle feuille des &lt;b&gt;Activités du jour&lt;/b&gt; est créée quotidiennement. On peut comparer cette feuille au Sprint Backlog de Scrum. On sélectionne quelques activités de l'&lt;b&gt;Inventaire des Activités&lt;/b&gt; pour les porter sur cette feuille. En face de chaque activité on écrit notre estimation de Pomodori pour cette tâche.&lt;br /&gt;La feuille d'&lt;b&gt;Enregistrements&lt;/b&gt; est mise à jour chaque fin de journée. Elle contient la synthése de la journée, une sorte de Sprint Retrospective. On y indique tout ce qui peut nous permettre d'améliorer le processus.&lt;br /&gt;&lt;hr /&gt;&lt;b&gt;Déroulement:&lt;/b&gt;&lt;br /&gt;La journée se décompose en 5 étapes quotidiennes [1]: &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Planification&lt;/b&gt;: au début de la journée, on sélectionne les activités les plus importantes de l'&lt;b&gt;Inventairedes Activités&lt;/b&gt; que l'on estime réalisables dans la journée. On écrit ces activités sur une nouvelle feuille d'&lt;b&gt;Activités du Jour&lt;/b&gt;.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Suivi&lt;/b&gt;: à la fin de chaque Pomoro (25 minutes), on ajoute un &lt;b&gt;X&lt;/b&gt; en face de l'activité dans les &lt;b&gt;Activités du Jour&lt;/b&gt; (si il n'a pas été interrompu). On écrit aussi les informations relative à l'activité qui nous semblent importantes (liste des interruptions et leur type).&lt;/li&gt;&lt;li&gt;&lt;b&gt;Enregistrement&lt;/b&gt;: à la fin de ma journée, je reporte la liste des Pomodori terminés et mes observations associées sur la feuille des &lt;b&gt;Enregistrements&lt;/b&gt;. &lt;/li&gt;&lt;li&gt;&lt;b&gt;Traitement&lt;/b&gt;:on analyse les données des la feuille des Enregistrements et on en extrait les informations. On va par exemple calculer l'écart entre le nombre de Pomodori estimés et réalisés par activité, la moyenne de Pomodori effectués par jour, le nombre d'interruptions rencontrées, leur nature.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Retrospective&lt;/b&gt;: on analyse les enregistrements dans l'objectif de nous améliorer. Quelles ont été mes erreurs d'estimation ? Comment pourrais-je éviter ces interruptions ?&lt;/li&gt;&lt;/ol&gt;La journée commence donc par la &lt;b&gt;Planification&lt;/b&gt;. Se succèdent ensuite plusieurs phases de &lt;b&gt;Suivi&lt;/b&gt;, après chaque Pomodoro. La journée se termine par l'&lt;b&gt;Enregistrement&lt;/b&gt;, le &lt;b&gt;Traitement&lt;/b&gt; et la &lt;b&gt;Retrospective&lt;/b&gt;.&lt;br /&gt;[1]: Staffan Nöteberg - "Pomodoro Technique Illustrated" DRAFT 27/04/2009&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-3834278834932635163?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/3834278834932635163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/le-pomodoro-au-quotidien_1429.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3834278834932635163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/3834278834932635163'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/le-pomodoro-au-quotidien_1429.html' title='Le Pomodoro au quotidien'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-390983628389945344</id><published>2009-05-09T22:33:00.010+02:00</published><updated>2009-10-28T20:33:37.176+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='livres'/><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><title type='text'>Pomodoro technique et cerveau droit</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;La &lt;a href="http://www.pomodorotechnique.com/"&gt;"Pomodoro technique"&lt;/a&gt; permet d'optimiser sa gestion du temps. Elle se présente comme un moyen d'éliminer son anxiété (de manque) du temps, d'améliorer son attention et sa concentration.&lt;br /&gt;Pomodoro signifie &lt;em&gt;tomate&lt;/em&gt; en italien et fait référence au minuteur de cuisine de l'auteur Francesco Cirillo en forme de tomate.&lt;br /&gt;La technique s'apparente aux méthodes agiles, itératives. Là où les méthodes agiles s'attachent beaucoup à l'équipe, Pomodoro s'intéresse plus à l'individu. La technique peut néanmoins s'utiliser en groupe. Henrik Kniberg décrit le processus comme un Scrum individuel basé sur des itérations de 25 mn, qui en garde la plupart des avantages (cf &lt;a href="http://blog.crisp.se/henrikkniberg/2009/01/19/1232358360000.html"&gt;le billet sur son blog&lt;/a&gt;).&lt;br /&gt;Le Pomodoro nous contraint (dans le bon sens du terme) à exercer la même activité sans interruptions pendant une tranche de temps courte et de durée fixe. L'objectif est de maximiser sa concentration sur ces 25 minutes et nous apprendre à gérer rapidement les interruptions (un mail qui arrive, coup de téléphone, question d'un collègue) et à les reporter sur un Pomodoro suivant.&lt;br /&gt;&lt;a href="http://www.pomodorotechnique.com/resources/cirillo/ThePomodoroTechnique_v1-3.pdf"&gt;Le livre téléchargeable "The Pomodoro Technique"&lt;/a&gt; en décrit les règles:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Un Pomodoro (unité de mesure) dure 25 minutes, plus 5 minutes de pause.&lt;/li&gt;&lt;li&gt;On prends une pause de 15 à 30 minutes tous les 4 Pomodori.&lt;/li&gt;&lt;li&gt;Un Pomodoro est indivisible. Il n'existe pas de demi ou quart de Pomodoro. &lt;/li&gt;&lt;li&gt;Un Pomodoro commencé doit obligatoirement se terminer: &lt;/li&gt;&lt;ul type="circle"&gt;&lt;li&gt;Si un Pomodoro est stoppé car on n'a pas su gérer la perturbation / interruption, on doit le considérer comme nul.&lt;/li&gt;&lt;li&gt;Si une activité se termine avant la fin d'un Pomodoro, on ne change pas d'activité et on utilise le temps restant pour passer en revue cette activité.&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Protégez le Pomodoro. Informez efficacement, négociez rapidement la planification de l'interruption, rappelez la personne qui vous a interrompu au moment convenu.&lt;/li&gt;&lt;li&gt;Si vous estimez une tâche à plus de 5-7 Pomodori, divisez la. Les tâches complexes doivent être décomposées en plusieurs tâches.&lt;/li&gt;&lt;li&gt;Si vous estimez une tâche à moins d'un Pomodoro, combinez la avec d'autres tâches simples.&lt;/li&gt;&lt;li&gt; Les résultats sont enregistrés après chaque Pomodoro.&lt;/li&gt;&lt;li&gt;Le Pomodoro suivant se déroulera mieux. &lt;/li&gt;&lt;/ul&gt;A noter que Staffan Nöteberg prépare un livre sur le sujet, "Pomodoro Technique Illustrated", et propose de télécharger les ébauches sur &lt;a href="http://www.pomodoro-book.com/"&gt;http://www.pomodoro-book.com/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;Quel rapport avec le cerveau droit ? L'obligation des pauses de 5 et 15-30 minutes m'a intéressé.&lt;br /&gt;Dans "Pragmatic Thinking &amp;amp; Learning", Andy Hunt détaille le fonctionnement du cerveau et explique les avantages qu'on peut tirer quotidiennement de cette connaissance, comment nous pouvons améliorer l'utilisation de notre matière grise. La distinction cerveau droit et gauche symbolise deux fonctionnements identifiés. En simplifiant à l'extrême:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;le cerveau gauche fonctionne par logique (si dans X je mets Y alors j'obtiens Z), s'occupe du langage, analyse les détails. C'est la voix qui nous parle.&lt;/li&gt;&lt;li&gt;le cerveau droit fonctionne par synthèse, avec une vision globale. Il nous fait rêver et nous apporte de nouvelles idées. C'est un cerveau créatif, riche. &lt;/li&gt;&lt;/ul&gt;Andy Hunt nomme ces deux manières d'utiliser notre cerveau L-Mode (pour left et linéaire) et R-Mode (pour right et riche). Si quelqu'un a un moyen mémotechnique en français...&lt;br /&gt;Nous ne pouvons pas utiliser nos deux cerveaux à la fois. Lorsqu'on programme, tape sur le clavier, on utilise notre cerveau gauche, linéaire, analytique. Lorsqu'on bloque sur notre programme, on s'éloigne du clavier, regarde ailleurs, arrêtons de nous parler. Nous laissons travailler notre cerveau droit pour qu'il nous donne une solution.&lt;br /&gt;Malheureusement, l'éducation et l'entreprise nous ont forgés à sur-utiliser notre cerveau gauche au détriment du droit. On ne nous paye pas à méditer, rêver, dessiner ou jouer de la musique. Ce sont pourtant ces activités qui nous permettent de développer nos idées et nos manières de penser. Ceci dit, des entreprises prennent conscience de ce phénomène et intègrent des activités sportives, artistiques ou de relaxation au quotidien.&lt;br /&gt;Mais revenons au Pomodoro. Une itération dure 25 minutes puis nous prenons 5 minutes de pause, ou 15-20 minutes tous les 4 Pomodori. Pendant ces pauses, il ne faut pas penser à l'activité que nous venons d'exercer. Rêvez, allez marcher dans un endroit tranquille, dessinez sur une feuille... activez votre cerveau droit ! C'est à ce moment là que les idées arrivent et peut-être une meilleure solution aux problêmes que nous tentions de résoudre. Dès qu'une idée apparaît, notez-là pour ne pas la perdre. Combien de fois avons nous eu des idées géniales sous la douche que nous avons perdu cinq minutes après...&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;Le pair-programming, deux développeurs pour un ordinateur, permet d'utiliser les deux modes de fonctionnement en même temps. La personne qui tape est en mode analytique (cerveau gauche) et celui qui pilote en mode synthétique, il garde le recul sur le développement. Il me reste à essayer la combinaison pair-programming + Pomodoro et voir si les deux pratiques se combinent bien...&lt;img height="1" src="http://res1.blogblog.com/tracker/3454033395947617111-390983628389945344?l=magaloma.blogspot.com" width="1" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-390983628389945344?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/390983628389945344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/pomodoro-technique-et-cerveau-droit.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/390983628389945344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/390983628389945344'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/pomodoro-technique-et-cerveau-droit.html' title='Pomodoro technique et cerveau droit'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-1165326105581330040</id><published>2009-05-02T11:43:00.007+02:00</published><updated>2009-05-02T11:58:26.196+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='développement'/><title type='text'>Vous êtes ce que vous faites</title><content type='html'>Un nouvel article sur le &lt;a href="http://blog.toolshed.com/2009/05/you-are-what-you-do.html"&gt;blog d'Andy Hunt&lt;/a&gt; concernant &lt;a href="http://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;"Pragmatic thinking &amp;amp; Learning"&lt;/a&gt; arrive comme par hasard.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Pour ceux que l'anglais rebute:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;On ne peut devenir expert sans expérience.&lt;/li&gt;&lt;li&gt;Devenir expert dans un domaine nécessite approximativement 10 ans, 10 000 heures de pratique.&lt;/li&gt;&lt;li&gt;Seule une pratique réfléchie, avec un objectif d'amélioration fait la différence. Suivre le mouvement ne suffit pas.&amp;nbsp; (Ça me rappelle un article sur la musique précisant que lorsque un musicien travaille vraiment son instrument, cela sonne très mal car il tente de jouer au delà de ses limites).&lt;/li&gt;&lt;li&gt;La pratique n'amène pas la perfection, mais amène la permanence: le cerveau se modifie en fonction de ce que l'on fait.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;On ne deviendra (peut-être) pas ce qu'on aspire à être. On deviens ce que l'on fait.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-1165326105581330040?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/1165326105581330040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/vous-etes-ce-que-vous-faites.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1165326105581330040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/1165326105581330040'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/vous-etes-ce-que-vous-faites.html' title='Vous êtes ce que vous faites'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-4522470087899306669</id><published>2009-05-01T15:15:00.007+02:00</published><updated>2009-10-21T23:02:23.258+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='livres'/><title type='text'>Modèle Dreyfus</title><content type='html'>&lt;a href="http://ecx.images-amazon.com/images/I/51xArZnegaL._SL160_AA115_.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://ecx.images-amazon.com/images/I/51xArZnegaL._SL160_AA115_.jpg" /&gt;&lt;/a&gt;Je viens de recevoir "&lt;a href="http://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;Pragmatic Thinking and Learning&lt;/a&gt;" de Andy Hunt (co-auteur du célèbre "&lt;a href="http://pragprog.com/titles/tpp/the-pragmatic-programmer"&gt;The Pragmatic Programmer: From Journeyman to Master&lt;/a&gt;"). Le livre s'annonce passionnant.&lt;br /&gt;&lt;br /&gt;Le second chapitre présente le modèle Dreyfus d'acquisition de compétences. Cette théorie dit que le passage de novice à expert d'un domaine donné passe par cinq étapes.&lt;br /&gt;&lt;br /&gt;étape 1: Novice&lt;br /&gt;&lt;ul&gt;&lt;li&gt;l'activité change sa manière de penser car il acquiert de l'expérience dans un domaine qu'il ne connaît pas, ou peu.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;il focalise  sur le besoin, l'objectif immédiat.&lt;/li&gt;&lt;li&gt;l'accomplissement de ses tâches nécessite  des recettes, une marche à suivre.&lt;br /&gt;&lt;/li&gt;&lt;li&gt; il n'a pas d'intuition sur la manière de résoudre les problématiques&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;étape 2: Débutant avancé&lt;br /&gt;&lt;ul&gt;&lt;li&gt; il fait preuve d'indépendance technique et sait chercher l'information nécessaire à l'accomplissement de ses tâches.&lt;br /&gt;&lt;/li&gt;&lt;li&gt; il sait choisir quelle démarche et technique appliquer selon le contexte.&lt;/li&gt;&lt;li&gt;il ne comprends pas ou peu la stratégie globale du système, de l'entreprise; il ne voit généralement pas les liens avec son travail quotidien.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;étape 3: Compétent&lt;br /&gt;&lt;ul&gt;&lt;li&gt; il peut développer et utiliser des modélisations des problématiques et travailler par abstractions.&lt;/li&gt;&lt;li&gt;il peut résoudre des problèmes qu'il n'a pas rencontré auparavant.&lt;/li&gt;&lt;li&gt;on dit d'eux qu'ils font preuve d'initiative.&lt;/li&gt;&lt;li&gt;il ne réfléchit pas ou peu sur ses méthodes de travail, ne rentre pas dans un processus d'amélioration continue.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;étape 4: Spécialiste&lt;br /&gt;&lt;ul&gt;&lt;li&gt;il ressent le besoin de comprendre la vue générale et les détails, la sur-simplification le frustre.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;il corrige ses erreurs.&lt;/li&gt;&lt;li&gt;il apprend par l'expérience des autres, étudie des cas, des projets et prends ce qui peut améliorer son expertise.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;il comprends et applique des maximes, vérités essentielles "Fait le minimum qui peut fonctionner", "Ne te répète pas", "Teste tout ce qui peut casser", ...&lt;/li&gt;&lt;li&gt;il sait utiliser les modèles de conception quand nécessaires (ce ne sont pas des recettes) et les adapter .&lt;/li&gt;&lt;li&gt;il utilise les avantages de la réflexion et du retour d'informations qui sont au cœur des méthodes agiles&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;étape 5: Expert&lt;br /&gt;&lt;ul&gt;&lt;li&gt;il cherche continuellement des nouvelles méthodes et manières de mieux faire les choses.&lt;/li&gt;&lt;li&gt; il travaille par (bonne) intuition.&lt;/li&gt;&lt;li&gt;il peut avoir du mal à expliquer ses choix: ils lui semblent "naturels".&lt;/li&gt;&lt;li&gt;il distingue les détails futiles de ceux très importants.&lt;br /&gt;&lt;/li&gt;&lt;li&gt; peu d'experts existent: certainement entre 1 et 5 pourcent de la population.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;A noter qu' on peut être expert dans un domaine et totalement novice dans un autre.&lt;br /&gt;&lt;br /&gt;Si le novice ne cherche pas à diversifier son expérience (ou ne suit pas de formation), il peut rester novice dans ce domaine quelque soit le nombre d'années d'activité. De même pour passer de débutant à compétent.&lt;br /&gt;&lt;br /&gt;Nous tendons à penser que la plupart d'entre nous est au niveau "Compétent". Or il apparaît en pratique que la grande majorité est au stade "Débutant avancé".&lt;br /&gt;&lt;br /&gt;Nous nous estimons aisément  expert d'un domaine et sûr de nos choix lorsqu'on possède peu de compétences et connaissances de ce domaine. Un novice ne connaît peu ou pas les limites de son savoir. Plus on est expert, plus on doute, on se sent ignorant.&lt;br /&gt;&lt;br /&gt;Cette citation (approximative) de Ghandi (je crois) me revient à l'esprit:&lt;br /&gt;&lt;i&gt;"Vivez comme si vous alliez mourir demain. Apprenez comme si vous étiez immortels."&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-4522470087899306669?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/4522470087899306669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/05/modele-dreyfus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4522470087899306669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/4522470087899306669'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/05/modele-dreyfus.html' title='Modèle Dreyfus'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-2217313575443011700</id><published>2009-04-28T22:38:00.005+02:00</published><updated>2009-05-16T14:24:43.790+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='musique'/><title type='text'>Génération d'accompagnement musical</title><content type='html'>&lt;a href="http://www.mellowood.ca/mma/"&gt;MMA&lt;/a&gt; génère des accompagnements en format midi, ce qui nous permet travailler son instrument en improvisant sur les arrangements que l'on veut.&lt;br /&gt;&lt;br /&gt;MMA génère l'accompagnement à partir d'un fichier texte dans lequel on spécifie au minimum le tempo, le style d'accompagnement et la grille du morceau. Par exemple:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Tempo 160&lt;br /&gt;&lt;br /&gt;Groove Swing1&lt;br /&gt;1 Cm7&lt;br /&gt;2 Cm7&lt;br /&gt;3 Ebm7&lt;br /&gt;4 D7b5&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;génèrera &lt;a href="http://lolgzs.free.fr/magaloma/mma/demo1.ogg"&gt;ceci&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;MMA est capable de générer des arrangements plus complets, comme &lt;a href="http://www.kara-moon.com/MMA/amadeus.mp3"&gt;celui-ci&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Avec un instrument dans une main, j'ai du mal à éditer des fichiers. J'ai généralement besoin d'un accompagnement minimal rapidement. &lt;a href="http://welltemperedstudio.wordpress.com/tag/lemma/"&gt;LEMMA&lt;/a&gt;, une interface graphique à MMA, nous facilite la tâche.&lt;br /&gt;&lt;br /&gt;L'utilisation de MMA&amp;nbsp;requiert au préalable l'installation d'un synthétiseur logiciel midi. Une solution est d'utiliser &lt;span style="font-weight: bold;"&gt;timidity&lt;/span&gt;. Voici la procédure sous Arch Linux, mais toutes les grandes distributions proposent les paquets nécessaires.&lt;br /&gt;&lt;br /&gt;Installation de timidity (synthétiseur midi):&lt;br /&gt;&lt;code&gt;pacman -S timidity++&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Installation des sound fonts:&lt;br /&gt;&lt;code&gt;yaourt -S fluidr3&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Ouvrez le fichier de configuration de timidity &lt;span style="font-weight: bold;"&gt;/etc/timidity++/timidity.cfg&lt;/span&gt; et ajoutez la ligne suivante pour indiquer d'utiliser la soundfont fluidr3:&lt;br /&gt;&lt;code&gt;soundfont /usr/share/soundfonts/fluidr3/FluidR3GM.SF2&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Pour valider le fonctionnement de timidity, téléchargez le fichier midi &lt;a href="http://lolgzs.free.fr/magaloma/mma/demo1.mid"&gt;demo1.mid&lt;/a&gt; et jouez le en tapant:&lt;br /&gt;&lt;code&gt; timidity demo1.mid&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Vous pouvez aussi afficher une interface graphique pour contrôler la lecture avec l'option &lt;b&gt;-ik&lt;/b&gt; (pour utiliser la bibliothèque Tk) ou &lt;b&gt;-ig&lt;/b&gt; (pour GTK):&lt;br /&gt;&lt;code&gt;timidity -ik demo1.mid&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A noter que l'on peut générer un fichier Ogg Vorbis depuis timidity avec l'option&lt;b&gt; -Ov&lt;/b&gt;, ou bien Wav avec &lt;b&gt;-Or&lt;/b&gt;. Pratique pour envoyer l'accompagnement à ses collègues.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ceci fait, nous pouvons installer MMA depuis AUR (si &lt;b&gt;python&lt;/b&gt; et &lt;b&gt;tk&lt;/b&gt; ne sont pas installés, profitez-en pour le faire):&lt;br /&gt;&lt;code&gt;yaourt -S mma&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Installation de Lemma:&lt;br /&gt;&lt;code&gt;yaourt -S lemma&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Lancez &lt;span style="font-weight: bold;"&gt;lemma&lt;/span&gt;. Allez dans le menu &lt;span style="font-weight: bold;"&gt;Settings&lt;/span&gt;:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;remplissez le champ &lt;span style="font-weight: bold;"&gt;Midi player:&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;timidity -ik&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;cliquez sur&lt;span style="font-weight: bold;"&gt; Refresh grooves library&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_JLK6EDfW4so/SfYWVoT6RdI/AAAAAAAAACg/dEcihRqZkK4/s1600-h/lemma_settings.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="Paramétrage de Lemma" border="0" id="BLOGGER_PHOTO_ID_5329471769893553618" src="http://1.bp.blogspot.com/_JLK6EDfW4so/SfYWVoT6RdI/AAAAAAAAACg/dEcihRqZkK4/s320/lemma_settings.png" style="cursor: pointer; height: 211px; width: 320px;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Vous êtes fin prêt à créer vos accompagnements.Saisissez quelques accords puis cliquez sur &lt;b&gt;play&lt;/b&gt; pour écouter votre œuvre. &lt;br /&gt;&lt;br /&gt;On peut changer le style de l'accompagnement au niveau de chaque mesure en cliquant sur Groove.&amp;nbsp; Pour la suite je vous laisse consulter la documentation de MMA.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-2217313575443011700?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/2217313575443011700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/04/generation-daccompagnement-musical.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2217313575443011700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/2217313575443011700'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/04/generation-daccompagnement-musical.html' title='Génération d&apos;accompagnement musical'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JLK6EDfW4so/SfYWVoT6RdI/AAAAAAAAACg/dEcihRqZkK4/s72-c/lemma_settings.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-8803445254721274767</id><published>2009-04-26T09:48:00.006+02:00</published><updated>2009-12-30T13:47:18.334+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Configuration dvorak-bépo sous Arch</title><content type='html'>J'utilise depuis quelques mois la disposition clavier &lt;a href="http://clavier-dvorak.org/wiki/Accueil"&gt;dvorak-bépo.&lt;/a&gt; Voulant activer cette disposition dès le démarrage de X, j'ai tout d'abord modifié mon &lt;span style="font-weight: bold;"&gt;xorg.conf&lt;/span&gt; ... sans succès. Le problème viens du démon &lt;span style="font-weight: bold;"&gt;hal&lt;/span&gt; qui s'occupe maintenant de la configuration du clavier (entre autres).&lt;br /&gt;&lt;br /&gt;Sous Arch, il faut procéder comme suit. Copiez tout d'abord le fichier de configuration du clavier de &lt;span style="font-weight: bold;"&gt;hal&lt;/span&gt; dans &lt;span style="font-weight: bold;"&gt;/etc&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cp /usr/share/hal/fdi/policy/10osvendor/10-keymap.fdi /etc/hal/fdi/policy/ &lt;/pre&gt;&lt;br /&gt;Editez ensuite le fichier  &lt;span style="font-weight: bold;"&gt;/etc/hal/fdi/policy/10-keymap.fdi&lt;/span&gt; et modifiez les valeurs des options &lt;span style="font-weight: bold;"&gt;input.xkb.layout&lt;/span&gt; et&lt;span style="font-weight: bold;"&gt; input.xkb.variant&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;merge key="input.xkb.layout" type="string"&amp;gt;fr&amp;lt;/merge&amp;gt;&lt;br /&gt;&amp;lt;merge key="input.xkb.variant" type="string"&amp;gt;bepo&amp;lt;/merge&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;puis redémarrez &lt;span style="font-weight: bold;"&gt;hal&lt;/span&gt; et votre gestionnaire de connexion (ici &lt;span style="font-weight: bold;"&gt;kdm&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/etc/rc.d/hal restart&lt;br /&gt;/etc/rc.d/kdm restart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Dans mon cas, je remplace la touche &lt;span style="font-weight: bold;"&gt;Caps Lock&lt;/span&gt; par un &lt;span style="font-weight: bold;"&gt;Ctrl&lt;/span&gt; (je trouve ça plus pratique) et je fait tourner Arch sur un Macbook. J'ai donc rajouté la ligne suivante:&lt;br /&gt;&lt;pre&gt;&amp;lt;merge key="input.xkb.options" type="string"&amp;gt;lv3:rwin_switch,ctrl:nocaps,apple:badmap&amp;lt;/merge&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-8803445254721274767?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/8803445254721274767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/04/configuration-dvorak-bepo-sous-arch.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8803445254721274767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/8803445254721274767'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/04/configuration-dvorak-bepo-sous-arch.html' title='Configuration dvorak-bépo sous Arch'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3454033395947617111.post-6287061865500440131</id><published>2009-04-24T22:17:00.009+02:00</published><updated>2009-12-30T13:48:01.910+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Au revoir Debian, bienvenue Arch</title><content type='html'>Neuf années que mon PC était fidèle à Debian (je me souviens avoir mis six heures à installer Potato dessus, grande victoire pour le novice Linuxien que j'étais).  Mais là, de bricolage en bricolage, l'envie d'une réinstallation me trottait dans la tête depuis longtemps.&lt;br /&gt;&lt;br /&gt;Quitte à ré-installer, autant essayer une autre distribution. D'autant plus qu'&lt;a href="http://www.archlinux.org/"&gt;ArchLinux &lt;/a&gt;m'attirait.... ni une ni deux je télécharge l'iso, grave et lance l'installation. Le programme d'installation sobre et direct (connaissance minimale de Linux conseillée) se déroule sans problèmes. J'en profite pour formater mes partitions /usr et /var en ext4. Noyau 2.6.29. &lt;br /&gt;&lt;br /&gt;Une fois installé et redémarré (ça démarre vite d'ailleurs), on se retrouve en mode console. Allons-y pour mettre en place le serveur X et  KDE4, rien de bien sorcier; &lt;span style="font-weight: bold;"&gt;pacman&lt;/span&gt; s'utilise comme apt-get pour les commandes de base.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;pacman -S xorg-server hal kdebase kdebase-workspace&lt;/pre&gt;&lt;br /&gt;Les paquets s'installent plus rapidement que sous Debian (pacman est toutefois moins avancé que apt).  Démarrons le minimum vital:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/etc/rc.d/hal start&lt;br /&gt;/etc/rc.d/kdm start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;et nous voilà sous l'écran de connexion.&lt;br /&gt;&lt;br /&gt;Pour avoir hal et kdm au redémarrage de la machine, le plus simple est de les déclarer dans le fichier &lt;span style="font-weight: bold;"&gt;/etc/rc.conf&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;DAEMONS=(hal kdm syslog-ng network netfs crond)&lt;/pre&gt;&lt;br /&gt;Après deux semaine sous Arch j'apprécie la simplicité de ce Linux avec des fichiers minimaux de configuration. Du coup mon portable a aussi adopté Arch sans regrets.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3454033395947617111-6287061865500440131?l=magaloma.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://magaloma.blogspot.com/feeds/6287061865500440131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://magaloma.blogspot.com/2009/04/au-revoir-debian-bienvenue-arch.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/6287061865500440131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3454033395947617111/posts/default/6287061865500440131'/><link rel='alternate' type='text/html' href='http://magaloma.blogspot.com/2009/04/au-revoir-debian-bienvenue-arch.html' title='Au revoir Debian, bienvenue Arch'/><author><name>Laurent Laffont</name><uri>http://www.blogger.com/profile/03349273143701067848</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_JLK6EDfW4so/SqjtvCk841I/AAAAAAAAAO0/F7HJ3tNGQNk/S220/IMG_1783.JPG'/></author><thr:total>2</thr:total></entry></feed>
