From 70314df976ab744212ec8e274e7f023912fdb854 Mon Sep 17 00:00:00 2001 From: Yann Esposito Date: Wed, 2 May 2012 17:43:56 +0200 Subject: [PATCH] Recompiled --- content/html/en/about/contact.md | 2 +- content/html/en/blog/Password-Management.md | 12 +- .../en/blog/Yesod-tutorial-for-newbies.md | 2 +- .../en/error/401-authorization_required.md | 4 +- content/html/en/error/403-forbidden.md | 2 +- content/html/en/error/404-not_found.md | 4 +- .../html/en/error/408-request_timed_out.md | 2 +- .../en/error/500-internal_server_error.md | 2 +- .../html/en/error/503-service_unavailable.md | 2 +- content/html/fr/about/contact.md | 2 +- content/html/fr/blog/Password-Management.md | 12 +- .../fr/blog/Yesod-tutorial-for-newbies.md | 2 +- lib/macros.rb | 1 + multi/about/contact.md | 2 +- output/Scratch/assets/css/main.css | 2 +- output/Scratch/css/solarized.css | 2 +- output/Scratch/en/about/contact/index.html | 2 +- output/Scratch/en/about/index.html | 4 +- output/Scratch/en/blog/01_nanoc/index.html | 7 +- output/Scratch/en/blog/02_ackgrep/code/ack | 1 - output/Scratch/en/blog/02_ackgrep/index.html | 46 +- .../03_losthighway_1/index.html | 27 +- .../03_losthighway_2/index.html | 13 +- .../03_losthighway_3/index.html | 7 +- .../03_losthighway_4/index.html | 9 +- .../Scratch/en/blog/03_losthighway/index.html | 25 +- output/Scratch/en/blog/04_drm/index.html | 9 +- .../code/git-create-new-branch.sh | 1 - .../05_git_create_remote_branch/index.html | 32 +- .../code/git-create-new-branch | 1 - .../code/git-get-remote-branches | 1 - .../en/blog/06_How_I_use_git/index.html | 161 +- .../index.html | 11 +- .../code/ssh-443.plist | 1 - .../index.html | 92 +- .../index.html | 3 + .../code/publish | 1 - .../index.html | 303 ++- .../11_Load_Disqus_Asynchronously/index.html | 35 +- .../index.html | 31 +- .../blog/2009-09-jQuery-Tag-Cloud/index.html | 356 +-- .../index.html | 103 +- .../code/publish | 1 - .../code/webdav-framework | 1 - .../index.html | 355 +-- .../code/ie.js | 1 - .../index.html | 54 +- .../2009-10-Focus-vs-Minimalism/index.html | 84 +- .../index.html | 42 +- .../index.html | 118 +- .../index.html | 14 +- .../2009-10-untaught-git-usage/index.html | 116 +- .../Git-pour-quoi-faire/index.html | 58 +- .../c-est-parti-pour-l-aventure/index.html | 46 +- .../commandes-avancees/index.html | 156 +- .../comprendre/index.html | 7 +- .../conf-et-install/code/gitconfig | 1 - .../conf-et-install/index.html | 132 +- .../blog/2009-11-12-Git-for-n00b/index.html | 43 +- .../2009-12-06-iphone-call-filter/index.html | 5 +- .../2009-12-14-Git-vs--Bzr/code/gitconfig | 1 - .../en/blog/2009-12-14-Git-vs--Bzr/index.html | 154 +- .../index.html | 12 +- .../code/local.conf | 1 - .../index.html | 101 +- .../index.html | 83 +- .../index.html | 99 +- .../index.html | 43 +- .../code/regex_benchmark_ext.rb | 1 - .../code/regex_benchmark_strip.rb | 1 - .../index.html | 128 +- .../en/blog/2010-03-22-Git-Tips/index.html | 72 +- .../blog/2010-03-23-Encapsulate-git/code/eng | 1 - .../2010-03-23-Encapsulate-git/index.html | 231 +- .../index.html | 13 +- .../code/repair_xml.rb | 1 - .../index.html | 139 +- .../graph/The_destination_tree.png | Bin 24274 -> 24490 bytes .../graph/The_source_tree.png | Bin 17763 -> 19344 bytes .../index.html | 139 +- .../index.html | 7 +- .../2010-06-15-Get-my-blog-engine/index.html | 23 +- .../code/become_hidden.html | 1 - .../code/become_visible.html | 1 - .../index.html | 110 +- .../code/yga.js | 1 - .../index.html | 89 +- .../code/essai.js | 1 - .../index.html | 44 +- .../index.html | 54 +- .../index.html | 9 +- .../2010-07-09-Indecidabilities/index.html | 59 +- .../index.html | 3 + .../code/.gems | 1 - .../code/config.ru | 1 - .../index.html | 100 +- .../index.html | 23 +- .../code/gitmtime.rb | 1 - .../index.html | 40 +- .../code/iphone_base64_sha1.c | 1 - .../index.html | 66 +- .../index.html | 5 +- .../index.html | 11 +- .../2010-10-14-Fun-with-wav/code/wavsum.c | 1 - .../2010-10-14-Fun-with-wav/code/wavsum.py | 1 - .../2010-10-14-Fun-with-wav/code/wavsum.rb | 1 - .../2010-10-14-Fun-with-wav/code/wavsum2.c | 1 - .../blog/2010-10-14-Fun-with-wav/index.html | 392 ++- .../code/macros.rb | 1 - .../index.html | 103 +- .../blog/2011-01-03-Happy-New-Year/index.html | 3 + .../index.html | 59 +- .../index.html | 5 +- .../en/blog/A-more-convenient-diff/code/ydiff | 1 - .../en/blog/A-more-convenient-diff/index.html | 43 +- .../blog/Haskell-Mandelbrot/code/animandel.hs | 1 - .../en/blog/Haskell-Mandelbrot/index.html | 113 +- .../en/blog/Haskell-the-Hard-Way/index.html | 2365 ++++++++-------- .../code/functional.sh | 1 - .../Higher-order-function-in-zsh/index.html | 173 +- .../blog/Learn-Vim-Progressively/index.html | 61 +- .../en/blog/Password-Management/index.html | 20 +- .../SVG-and-m4-fractals/code/yesodlogo.m4 | 1 - .../en/blog/SVG-and-m4-fractals/index.html | 172 +- .../en/blog/Typography-and-the-Web/index.html | 38 +- .../en/blog/Yesod-excellent-ideas/index.html | 59 +- .../code/.gitignore | 1 - .../Yesod-tutorial-for-newbies/code/Echo.hs | 1 - .../Yesod-tutorial-for-newbies/code/Mirror.hs | 1 - .../code/article.hamlet | 1 - .../code/articles.hamlet | 1 - .../code/default-layout.lucius | 1 - .../code/echo.hamlet | 1 - .../code/mirror.hamlet | 1 - .../code/posted.hamlet | 1 - .../Yesod-tutorial-for-newbies/index.html | 476 ++-- output/Scratch/en/blog/feed/feed.xml | 379 ++- output/Scratch/en/blog/index.html | 24 +- output/Scratch/en/blog/mvc/index.html | 11 +- .../index.html | 129 +- .../401-authorization_required/index.html | 4 +- .../Scratch/en/error/403-forbidden/index.html | 2 +- .../Scratch/en/error/404-not_found/index.html | 8 +- .../en/error/408-request_timed_out/index.html | 2 +- .../500-internal_server_error/index.html | 2 +- .../error/503-service_unavailable/index.html | 2 +- output/Scratch/en/index.html | 2 +- output/Scratch/en/rss/index.html | 6 +- .../en/softwares/yaquabubbles/index.html | 2 +- output/Scratch/en/softwares/yclock/index.html | 2 +- .../Scratch/en/softwares/ypassword/index.html | 2 +- output/Scratch/en/validation/index.html | 6 +- output/Scratch/fr/about/contact/index.html | 4 +- output/Scratch/fr/about/index.html | 20 +- .../fr/about/technical_details/index.html | 6 +- output/Scratch/fr/blog/01_nanoc/index.html | 15 +- output/Scratch/fr/blog/02_ackgrep/code/ack | 1 - output/Scratch/fr/blog/02_ackgrep/index.html | 58 +- .../03_losthighway_1/index.html | 39 +- .../03_losthighway_2/index.html | 27 +- .../03_losthighway_3/index.html | 7 +- .../03_losthighway_4/index.html | 27 +- .../Scratch/fr/blog/03_losthighway/index.html | 37 +- output/Scratch/fr/blog/04_drm/index.html | 9 +- .../code/git-create-new-branch.sh | 1 - .../05_git_create_remote_branch/index.html | 40 +- .../code/git-create-new-branch | 1 - .../code/git-get-remote-branches | 1 - .../fr/blog/06_How_I_use_git/index.html | 183 +- .../index.html | 15 +- .../code/ssh-443.plist | 1 - .../index.html | 108 +- .../index.html | 11 +- .../code/publish | 1 - .../index.html | 323 +-- .../11_Load_Disqus_Asynchronously/index.html | 35 +- .../index.html | 37 +- .../blog/2009-09-jQuery-Tag-Cloud/index.html | 348 +-- .../index.html | 103 +- .../code/publish | 1 - .../code/webdav-framework | 1 - .../index.html | 363 +-- .../code/ie.js | 1 - .../index.html | 54 +- .../2009-10-Focus-vs-Minimalism/index.html | 88 +- .../index.html | 52 +- .../index.html | 122 +- .../index.html | 16 +- .../2009-10-untaught-git-usage/index.html | 168 +- .../Git-pour-quoi-faire/index.html | 86 +- .../c-est-parti-pour-l-aventure/index.html | 54 +- .../commandes-avancees/index.html | 170 +- .../comprendre/index.html | 17 +- .../conf-et-install/code/gitconfig | 1 - .../conf-et-install/index.html | 144 +- .../blog/2009-11-12-Git-for-n00b/index.html | 45 +- .../2009-12-06-iphone-call-filter/index.html | 7 +- .../2009-12-14-Git-vs--Bzr/code/gitconfig | 1 - .../fr/blog/2009-12-14-Git-vs--Bzr/index.html | 192 +- .../index.html | 14 +- .../code/local.conf | 1 - .../index.html | 103 +- .../index.html | 101 +- .../index.html | 119 +- .../index.html | 53 +- .../code/regex_benchmark_ext.rb | 1 - .../code/regex_benchmark_strip.rb | 1 - .../index.html | 138 +- .../fr/blog/2010-03-22-Git-Tips/index.html | 74 +- .../blog/2010-03-23-Encapsulate-git/code/eng | 1 - .../2010-03-23-Encapsulate-git/index.html | 251 +- .../index.html | 27 +- .../code/repair_xml.rb | 1 - .../index.html | 149 +- .../graph/The_destination_tree.png | Bin 24274 -> 24490 bytes .../graph/The_source_tree.png | Bin 17763 -> 19344 bytes .../index.html | 209 +- .../index.html | 25 +- .../2010-06-15-Get-my-blog-engine/index.html | 29 +- .../code/become_hidden.html | 1 - .../code/become_visible.html | 1 - .../index.html | 118 +- .../code/yga.js | 1 - .../index.html | 93 +- .../code/essai.js | 1 - .../index.html | 50 +- .../index.html | 100 +- .../index.html | 25 +- .../2010-07-09-Indecidabilities/index.html | 137 +- .../index.html | 11 +- .../code/.gems | 1 - .../code/config.ru | 1 - .../index.html | 110 +- .../index.html | 41 +- .../code/gitmtime.rb | 1 - .../index.html | 52 +- .../code/iphone_base64_sha1.c | 1 - .../index.html | 76 +- .../index.html | 25 +- .../index.html | 23 +- .../2010-10-14-Fun-with-wav/code/wavsum.c | 1 - .../2010-10-14-Fun-with-wav/code/wavsum.py | 1 - .../2010-10-14-Fun-with-wav/code/wavsum.rb | 1 - .../2010-10-14-Fun-with-wav/code/wavsum2.c | 1 - .../blog/2010-10-14-Fun-with-wav/index.html | 422 ++- .../code/macros.rb | 1 - .../index.html | 113 +- .../blog/2011-01-03-Happy-New-Year/index.html | 13 +- .../index.html | 99 +- .../index.html | 7 +- .../fr/blog/A-more-convenient-diff/code/ydiff | 1 - .../fr/blog/A-more-convenient-diff/index.html | 49 +- .../blog/Haskell-Mandelbrot/code/animandel.hs | 1 - .../fr/blog/Haskell-Mandelbrot/index.html | 113 +- .../fr/blog/Haskell-the-Hard-Way/index.html | 2407 +++++++++-------- .../code/functional.sh | 1 - .../Higher-order-function-in-zsh/index.html | 189 +- .../blog/Learn-Vim-Progressively/index.html | 127 +- .../fr/blog/Password-Management/index.html | 59 +- .../SVG-and-m4-fractals/code/yesodlogo.m4 | 1 - .../fr/blog/SVG-and-m4-fractals/index.html | 216 +- .../fr/blog/Typography-and-the-Web/index.html | 60 +- .../fr/blog/Yesod-excellent-ideas/index.html | 111 +- .../code/.gitignore | 1 - .../Yesod-tutorial-for-newbies/code/Echo.hs | 1 - .../Yesod-tutorial-for-newbies/code/Mirror.hs | 1 - .../code/article.hamlet | 1 - .../code/articles.hamlet | 1 - .../code/default-layout.lucius | 1 - .../code/echo.hamlet | 1 - .../code/mirror.hamlet | 1 - .../code/posted.hamlet | 1 - .../Yesod-tutorial-for-newbies/index.html | 476 ++-- output/Scratch/fr/blog/feed/feed.xml | 679 +++-- output/Scratch/fr/blog/index.html | 16 +- output/Scratch/fr/blog/mvc/index.html | 11 +- .../index.html | 332 ++- output/Scratch/fr/index.html | 2 +- output/Scratch/fr/rss/index.html | 20 +- .../fr/softwares/yaquabubbles/index.html | 6 +- output/Scratch/fr/softwares/yclock/index.html | 6 +- .../Scratch/fr/softwares/ypassword/index.html | 2 +- output/Scratch/fr/validation/index.html | 16 +- output/Scratch/sitemap.xml | 1490 +++++----- 284 files changed, 11447 insertions(+), 9944 deletions(-) diff --git a/content/html/en/about/contact.md b/content/html/en/about/contact.md index 085ece9bb..38bad64af 100644 --- a/content/html/en/about/contact.md +++ b/content/html/en/about/contact.md @@ -6,7 +6,7 @@ menupriority: 1 Avatar -<%= mail_perso %> +%mailperso Follow me on [twitter](http://twitter.com/yogsototh) My preferred bookmarks [pinboard](http://pinboard.in/u:yogsototh) Open Source [github](http://github.com/yogsototh) diff --git a/content/html/en/blog/Password-Management.md b/content/html/en/blog/Password-Management.md index 51db759f3..0e4aa101f 100644 --- a/content/html/en/blog/Password-Management.md +++ b/content/html/en/blog/Password-Management.md @@ -12,11 +12,9 @@ tags: ----- blogimage("main.png","Title image") -<% password="P45sW0r|)" %> - begindiv(intro) -<%= tldr %> How I manage safely my password with success for some years now. +%tldr How I manage safely my password with success for some years now. **`sha1( password + domain_name )`** I memorize only one password. I use a different password on all website. @@ -67,9 +65,9 @@ A _bad_ solution would be to chose passwords like this: -- twitter: `<%=password%>Twitter` -- gmail: `<%=password%>gmail` -- badonlinegame: `<%=password%>badonlinegame` +- twitter: `P45sW0r|)Twitter` +- gmail: `P45sW0r|)gmail` +- badonlinegame: `P45sW0r|)badonlinegame` Unfortunately, if someone get your password on @@ -88,7 +86,7 @@ hash("P45sW0r|)") = 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63 If someone has `9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63`, -he will have hard time to recover `<%=password%>`. +he will have hard time to recover `P45sW0r|)`. Let choose SHA1 as hash function. diff --git a/content/html/en/blog/Yesod-tutorial-for-newbies.md b/content/html/en/blog/Yesod-tutorial-for-newbies.md index 0c9352f66..a5ddbc872 100644 --- a/content/html/en/blog/Yesod-tutorial-for-newbies.md +++ b/content/html/en/blog/Yesod-tutorial-for-newbies.md @@ -25,7 +25,7 @@ begindiv(intro) _update_: updated for yesod 0.10 -<%= tldr %> A simple yesod tutorial. +%tldr A simple yesod tutorial. Yesod is a Haskell web framework. You shouldn't need to know Haskell. diff --git a/content/html/en/error/401-authorization_required.md b/content/html/en/error/401-authorization_required.md index 655b47416..c91ef2116 100644 --- a/content/html/en/error/401-authorization_required.md +++ b/content/html/en/error/401-authorization_required.md @@ -7,8 +7,8 @@ error_message: Authorization required noSubMenu: true ----- -If you don't have the password or believe it is an error you can mail me at <% mail_perso %>. +If you don't have the password or believe it is an error you can mail me at %mailperso. newcorps -Vous pouvez me contacter par mail: <% mail_perso %>. +Vous pouvez me contacter par mail: %mailperso. diff --git a/content/html/en/error/403-forbidden.md b/content/html/en/error/403-forbidden.md index 477b7b5e5..7003ce93c 100644 --- a/content/html/en/error/403-forbidden.md +++ b/content/html/en/error/403-forbidden.md @@ -7,4 +7,4 @@ error_message: Forbidden noSubMenu: true ----- -Contact: <% mail_perso %> +Contact: %mailperso diff --git a/content/html/en/error/404-not_found.md b/content/html/en/error/404-not_found.md index ef3964c8f..2fc5885e5 100644 --- a/content/html/en/error/404-not_found.md +++ b/content/html/en/error/404-not_found.md @@ -8,9 +8,9 @@ noSubMenu: true layout: error ----- The page you're looking at doesn't exists on the server. -If you have followed an internal link, you can tell me by mail <% mail_perso %> and I'll fix it as soon as possible. +If you have followed an internal link, you can tell me by mail %mailperso and I'll fix it as soon as possible. newcorps La page que vous recherchez n'est pas présente sur le site. -Si vous avez suivi un lien vous pouvez me prévenir par mail <% mail_perso %> et je réparerai ça dès que possible. +Si vous avez suivi un lien vous pouvez me prévenir par mail %mailperso et je réparerai ça dès que possible. diff --git a/content/html/en/error/408-request_timed_out.md b/content/html/en/error/408-request_timed_out.md index 0ed645626..56445019b 100644 --- a/content/html/en/error/408-request_timed_out.md +++ b/content/html/en/error/408-request_timed_out.md @@ -7,4 +7,4 @@ error_message: Request Timed Out noSubMenu: true ----- -Contact: <% mail_perso %> +Contact: %mailperso diff --git a/content/html/en/error/500-internal_server_error.md b/content/html/en/error/500-internal_server_error.md index 5aa911ca0..377f754de 100644 --- a/content/html/en/error/500-internal_server_error.md +++ b/content/html/en/error/500-internal_server_error.md @@ -7,4 +7,4 @@ error_message: Internal Server Error noSubMenu: true ----- -Contact: <% mail_perso %> +Contact: %mailperso diff --git a/content/html/en/error/503-service_unavailable.md b/content/html/en/error/503-service_unavailable.md index 419c1e0e5..eb45a87fd 100644 --- a/content/html/en/error/503-service_unavailable.md +++ b/content/html/en/error/503-service_unavailable.md @@ -7,4 +7,4 @@ error_message: Service Unaviable noSubMenu: true ----- -Contact: <% mail_perso %> +Contact: %mailperso diff --git a/content/html/fr/about/contact.md b/content/html/fr/about/contact.md index 3056950f4..73a628731 100644 --- a/content/html/fr/about/contact.md +++ b/content/html/fr/about/contact.md @@ -6,7 +6,7 @@ menupriority: 1 Avatar -<%= mail_perso %> +%mailperso Suivez moi sur [twitter](http://twitter.com/yogsototh) Mes "bookmarks" [pinboard](http://pinboard.in/u:yogsototh) Open Source [github](http://github.com/yogsototh) diff --git a/content/html/fr/blog/Password-Management.md b/content/html/fr/blog/Password-Management.md index 35ed484ca..cb1f72068 100644 --- a/content/html/fr/blog/Password-Management.md +++ b/content/html/fr/blog/Password-Management.md @@ -12,12 +12,10 @@ tags: ----- blogimage("main.png","Title image") -<% password="P45sW0r|)" %> - begindiv(intro) -<%= tlal %> Une méthode de gestion des mots de passes que j'utilise avec succès depuis quelques années. +%tlal Une méthode de gestion des mots de passes que j'utilise avec succès depuis quelques années. **`sha1( mot_de_passe + nom_de_domaine )`** Je ne mémorise qu'un seul mot de passe de très bonne qualité. J'utilise des mots de passe différents sur tous les sites. @@ -67,9 +65,9 @@ Un _mauvaise_ solution peut être de choisir ses mots de passes de la façon suivante : -- twitter: `<%=password%>Twitter` -- gmail: `<%=password%>gmail` -- badonlinegame: `<%=password%>badonlinegame` +- twitter: `P45sW0r|)Twitter` +- gmail: `P45sW0r|)gmail` +- badonlinegame: `P45sW0r|)badonlinegame` Malheureusement, si quelqu'un récupère votre mot de passe sur @@ -86,7 +84,7 @@ Prenons un exemple : Si quelqu'un possède `9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63`, -il va avoir de grande difficulté pour retrouver `<%=password%>`. +il va avoir de grande difficulté pour retrouver `P45sW0r|)`. Choisisson la fonction de hashage `sha1`. diff --git a/content/html/fr/blog/Yesod-tutorial-for-newbies.md b/content/html/fr/blog/Yesod-tutorial-for-newbies.md index c36146ceb..e90c1243a 100644 --- a/content/html/fr/blog/Yesod-tutorial-for-newbies.md +++ b/content/html/fr/blog/Yesod-tutorial-for-newbies.md @@ -26,7 +26,7 @@ begindiv(intro) _mise à jour_: mise à jour pour la version 0.10 de yesod. -<%= tlal %> Un tutoriel pour yesod, un framework web Haskell. +%tlal Un tutoriel pour yesod, un framework web Haskell. Vous ne devriez pas avoir besoin de savoir programmer en Haskell. Par contre je suis désolé pour les francophones, mais je n'ai pas eu le courage de traduire cet article en Français. diff --git a/lib/macros.rb b/lib/macros.rb index e5c5d4b54..c54fce37f 100644 --- a/lib/macros.rb +++ b/lib/macros.rb @@ -15,6 +15,7 @@ class Macros < Nanoc3::Filter @macro={} @macro[:tlal] = %{tlàl : } @macro[:tldr] = %{tl;dr: } + @macro[:mailperso] = %{yann.esposito@gmail.com} if @item.nil? if not arg.nil? @macro.merge!( arg ) diff --git a/multi/about/contact.md b/multi/about/contact.md index 0b6620bbb..2ff5421bc 100644 --- a/multi/about/contact.md +++ b/multi/about/contact.md @@ -9,7 +9,7 @@ fr: # Comment me contacter en: Avatar fr: Avatar -<%= mail_perso %> +%mailperso en: Follow me on [twitter](http://twitter.com/yogsototh) en: My preferred bookmarks [pinboard](http://pinboard.in/u:yogsototh) en: Open Source [github](http://github.com/yogsototh) diff --git a/output/Scratch/assets/css/main.css b/output/Scratch/assets/css/main.css index c67a253aa..19b0fa2d9 100644 --- a/output/Scratch/assets/css/main.css +++ b/output/Scratch/assets/css/main.css @@ -1 +1 @@ -table.description tr td{border:1px solid #eeeef1}.assombris20{background-color:#eeeef1}body{color:#002b36;background-color:#fafafc}::selection{background:#002b36;color:#93a1a1}::-moz-selection{background:#002b36;color:#93a1a1}pre,code{font-family:Monaco,monospace}pre::selection{background:#fdf6e3;color:#586e75}pre *::selection{background:#fdf6e3;color:#586e75}pre::-moz-selection{background:#fdf6e3;color:#586e75}pre *::-moz-selection{background:#fdf6e3;color:#586e75}a:hover{text-shadow:0 0 2px#faa}a,a:link,a:visited,a:active,a:hover{text-decoration:none;outline:none}a,a:link,a:visited,a:active{color:#002b36}a:hover{color:#cb4b16}hr{color:#eeeef1;border-top:1px solid #eeeef1;border-bottom:none;border-left:none;border-right:none}ul{list-style:square}ol,ul{padding-left:0}ol li,ul li{margin:.5em 0}ol li ul,ol li ol,ul li ol,ul li ul{margin:.5em 1.5em;list-style:circle}body,h1,h2,h3,h4,#entete,.tag{font-family:Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;line-height:1.4em}.article #afterheader{counter-reset:niv02}.article #afterheader h2{counter-increment:niv02;counter-reset:niv03;marker-offset:3em}.article #afterheader h2:before{content:counter(niv02) ". "}.article #afterheader h3{counter-increment:niv03;counter-reset:niv04}.article #afterheader h3:before{content:counter(niv02) "." counter(niv03) ". "}.article #afterheader h4{counter-increment:niv04}.article #afterheader h4:before{content:counter(niv02) "." counter(niv03) "." counter(niv04) ". "}pre{background-color:#002b36;color:#839496;box-shadow:0 0 1em#001 inset;border-radius:3px;padding:1em;line-height:1.2em;font-size:.9em}p{margin-bottom:1.2em}blockquote{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;font-style:italic;padding:.5em 1em;color:#556}blockquote a:hover{color:#cb4b16}blockquote strong,blockquote b,blockquote i,blockquote em{font-weight:400;font-style:normal;color:#002b36}blockquote ul{padding-left:1.5em}abbr,acronym{font-variant:small-caps;text-decoration:none;border-bottom-width:0}#titre{letter-spacing:-0.06em;border-bottom:4px double #ccccd0;border-top:4px double #ccccd0}#liens .active,#sousliens{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}#liens .active a,#sousliens a{color:#667}#liens .active a:hover,#sousliens a:hover{color:#cb4b16}#liens .active a:hover strong,#liens .active a:hover b,#liens .active a:hover i,#liens .active a:hover em,#liens .active a:hover .nicer,#sousliens a:hover strong,#sousliens a:hover b,#sousliens a:hover i,#sousliens a:hover em,#sousliens a:hover .nicer{color:#ffb17c}#liens .active hr,#sousliens hr{color:#667;border-top:1px solid#667}#liens .active strong,#liens .active b,#liens .active i,#liens .active em,#sousliens strong,#sousliens b,#sousliens i,#sousliens em{color:#002b36}#liens a{border:1px solid#eee;background:rgba(0,0,0,0.05);box-shadow:0 0 2px white,0 0 3px#ccc inset;border:1px solid rgba(0,0,0,0.1);border-radius:3px}#liens a:hover{background:rgba(0,0,0,0.1);box-shadow:0 0 6px#555 inset}#liens .active{text-shadow:0 0 2px rgba(0,0,0,0.5);background-color:#f7f7f9;border:1px solid #e9e9eb;box-shadow:0 0 3px #c7c7c9 inset;border-radius:3px;border-top:none}#lastmod{font-size:.9em}.nojsbutton{font-size:2.5em}#clickcomment,#choixlang > a,#choixrss > a,.return > a{display:block;width:25%;cursor:pointer;margin:1em 0;padding:1em;font-size:16px;line-height:1.4em;border:1px solid #fafafc;color:#ccccd0}#clickcomment:hover,#choixlang > a:hover,#choixrss > a:hover,.return > a:hover{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa}#clickcomment:active,#choixlang > a:active,#choixrss > a:active,.return > a:active{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa;background:#f4f4f6}.return > a,#choixrss > a{float:right}#choix .return > a,#choix #choixrss > a{margin-top:0}.small{font-size:.8em}.sc{font-variant:small-caps}.impact,.darkimpact{font-size:2em;margin:0 auto 1em auto;line-height:1.3em}h1 > .date{font-size:.6em;color:#002b36}.date{font-size:.8em;color:#fafafc;border:1px solid #002b36;text-align:center;width:4.1em;line-height:1.5em;display:inline-block;vertical-align:middle;margin-right:1em}.date .day,.date .month,.date .year{display:block}.date .day{color:#002b36;background-color:#fafafc;float:left;width:1.7em}.date .month{float:right;width:2.3em;background-color:#002b36;color:#fafafc}.date .year{line-height:3ex;clear:both;color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1;border-radius:0}.date .year a{color:#667}.date .year a:hover{color:#cb4b16}.date .year a:hover strong,.date .year a:hover b,.date .year a:hover i,.date .year a:hover em,.date .year a:hover .nicer{color:#ffb17c}.date .year hr{color:#667;border-top:1px solid#667}.date .year strong,.date .year b,.date .year i,.date .year em{color:#002b36}body{text-align:center;font-size:16px}body > #entete{position:absolute;left:0;top:.5em;width:100%;min-width:50em;z-index:8000;padding-bottom:1em;margin-bottom:3em}#titre h2{width:80%;margin-left:auto;margin-right:auto;text-align:center;color:#ccccd0}#titre{text-align:center;width:100%}#titre h1,#titre h2{padding-left:1em;padding-right:1em}#bottom{clear:right;margin-right:0;padding:1.5em;line-height:1.5em;color:#224d58;margin-top:2em;text-align:center}#bottom a{color:#113c47}#bottom a:hover{color:#cb4b16}#sousliens{padding:1em 0;line-height:2em}#sousliens ul{list-style:none;margin-left:4em}ul.horizontal li{display:inline;font-size:.9em}ul.horizontal{margin-top:0;margin-bottom:0}#entete{padding-top:.1em;border-top:1px solid #ccccd0;border-bottom:1px solid #ccccd0}#liens{width:100%;padding:0;clear:both;margin-top:.5em}#liens ul{width:100%;clear:both;padding:0;margin:0}#liens ul li{display:inline-block;height:4em;margin-left:.2em;margin-right:.2em;width:23%}#liens ul li a,#liens ul li span{width:100%;display:block;line-height:4em}.clear{clear:both}#content{line-height:4em;margin-left:auto;margin-right:auto;margin-top:0;position:relative;clear:both;width:52em}.encadre,.black,.red,.intro,.resume,.shadow{padding:2em;margin-top:2em;margin-bottom:2em}.encadre,.black,.red,.shadow{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}.encadre a,.black a,.red a,.shadow a{color:#667}.encadre a:hover,.black a:hover,.red a:hover,.shadow a:hover{color:#cb4b16}.encadre a:hover strong,.encadre a:hover b,.encadre a:hover i,.encadre a:hover em,.encadre a:hover .nicer,.black a:hover strong,.black a:hover b,.black a:hover i,.black a:hover em,.black a:hover .nicer,.red a:hover strong,.red a:hover b,.red a:hover i,.red a:hover em,.red a:hover .nicer,.shadow a:hover strong,.shadow a:hover b,.shadow a:hover i,.shadow a:hover em,.shadow a:hover .nicer{color:#ffb17c}.encadre hr,.black hr,.red hr,.shadow hr{color:#667;border-top:1px solid#667}.encadre strong,.encadre b,.encadre i,.encadre em,.black strong,.black b,.black i,.black em,.red strong,.red b,.red i,.red em,.shadow strong,.shadow b,.shadow i,.shadow em{color:#002b36}pre .red{background:none;padding:0;margin:auto;border:none;box-shadow:none}.intro,.resume{font-size:.9em;font-style:italic;padding:.5em 1em;color:#556}.intro a:hover,.resume a:hover{color:#cb4b16}.intro strong,.intro b,.intro i,.intro em,.resume strong,.resume b,.resume i,.resume em{font-weight:400;font-style:normal;color:#002b36}#afterheader > h1{width:100%;padding-top:1.5em;text-align:left}#afterheader{padding-left:0;padding-right:0}#sousliens{margin-top:3em;margin-bottom:3em;font-size:1.2em;letter-spacing:1px;text-align:left;clear:both}.twilight{line-height:1.1em}.corps{font-size:1.25em;line-height:1.6em;text-align:justify;text-align:left;padding:3em 3em;margin:0;clear:both}.corps img{max-width:30em;border:1px solid #ccccd0;background-color:#fafafc;padding:.5em;box-shadow:0 10px 15px#ccc;border-radius:3px}.corps a:hover img{background-color:#dc3a05}figure{margin:3em 0}figure img{box-shadow:0 10px 15px#ccc inset}figure figcaption{text-align:center;margin:.5em 0}img.clean{border:none}#address{clear:both}.definitionCell{width:5em;vertical-align:top;font-weight:700;text-align:center}.valueCell{text-align:right}.smallblock{float:left;width:50%;font-size:1em;font-weight:700}.largeblock{float:right;width:70%;font-size:1em}#blackpage,#nojsredirect{top:0;left:0;width:100%;height:100%;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:fixed;text-align:center}#blackpage{color:#666;padding-top:10em;background-color:#eee;z-index:9000;cursor:wait}#blackpage img{background:none;border:none}#blackpage a{cursor:pointer}#nojsredirect{z-index:9001}.nojsbutton{width:50%;padding:1em;border:solid 3px white;margin-left:auto;margin-right:auto;margin-top:2em;z-index:9002}.file{font-size:.8em;text-align:right;padding-right:1em;margin-right:.1;margin-bottom:0}.flush{clear:both}table.description{border-spacing:5px;border-collapse:separate;margin-right:auto;margin-left:auto}table.description tr td{padding-left:.5em;padding-right:.5em;padding-top:.5ex;padding-bottom:.5ex;vertical-align:middle;margin-right:5px}ul.long li{margin-bottom:1em}img{display:block;margin:1.2em auto;background:none;border:none}img.left{float:left;max-width:30%;margin-top:.6em;margin-right:2em}img.inside{display:inline;vertical-align:middle}pre{overflow-x:auto;overflow-y:hidden}.withfile pre{margin-top:0}.navigationprev,.navigationnext{padding:0;margin-left:.2em;margin-right:.2em;margin-bottom:0;margin-top:3em;width:45%}.navigation .navigationprev,.navigation .navigationnext{width:30%;margin-top:0}.navigation{height:4em;border-bottom:#ccccd0 solid 1px}.presarticleleft,.presarticleright{font-size:1em}.navigationprev{float:left;text-align:left}.navigationnext{float:right;text-align:right}.impact,.darkimpact{text-align:left;width:66%;padding-left:.25em;padding-right:.25em}table.impact{text-align:left}table.impact tr td{padding-left:.25em;padding-right:.25em}#liens{font-size:1.2em}#iemessage{font-size:1.2em;color:#ccc;margin:-10px;padding:1px 0;background:#333}#iemessage strong,#iemessage b,#iemessage i,#iemessage em{color:#ccc}#iemessage a,#iemessage a:visited{color:#eca}.tag{display:inline;cursor:pointer;margin-left:.5em;margin-right:.5em}.list{margin-top:3em}#menuMessage{font-size:1.2em;line-height:1.5em;width:100%;text-align:center}#next_before_articles{clear:both;width:100%;font-size:1.2em;padding-top:1em;padding-bottom:1em}#previous_articles,#next_articles{color:#889;font-style:italic;font-size:.8em}#previous_articles{float:left;margin-left:1em;width:45%;text-align:left}.previous_article,.next_article{margin-top:1em}#next_articles{float:right;width:45%;margin-right:1em;text-align:right}#rss{font-size:1.2em;text-align:center;display:block;width:100%;float:right;padding:1em .1em}.corps .return a{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;float:left;font-size:2em;margin-top:-0.5em;margin-left:-2em;width:1.5em}a.return{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;font-size:2em;width:1.5em;display:block}a.return:hover{color:#889}.corps .return a:hover{color:#cb4b16}.footnotes{font-size:.8em}.footnotes ol{color:#839496;font-weight:700}.footnotes ol p{color:#002b36;font-weight:400}.fontnotes ol{margin-left:0}.typeset img{display:inline;border:none;margin:0;padding:0}strong,b,i,em{font-weight:400;color:#889}strong a,b a,i a,em a{color:#002b36}strong a:hover,b a:hover,i a:hover,em a:hover{color:#cb4b16}.corps p strong,.corps p b,.corps p i,.corps p em{color:#556}a:hover strong,a:hover b,a:hover i,a:hover em{color:#dc5c27}a:hover .nicer{color:#ffb17c}.nicer{color:#ccccd0;font-family:"Lucida Grande",Tahoma}.block{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;width:26.5%;padding:1em;border-radius:2px;text-align:left;line-height:1em;margin-left:1%;margin-right:1%;font-size:.8em;height:9em}.block a{color:#002b36}.block a:hover{color:#cb4b16}.block h3{margin:0;font-size:1.3em}.block p{line-height:1.2em}.left{float:left}.right{float:right}.corps p a,.corps ul a{color:#556}.corps p a:hover,.corps ul a:hover{color:#cb4b16}ul.bloglist,.archive ul{list-style-type:none;margin:0}ul.bloglist li,.archive ul li{margin-bottom:1em}.button{cursor:pointer;text-align:center}#tagcloud{font-size:.8em;background:#f2f2f4;box-shadow:0 0 6px #ccccd0;border-radius:3px;line-height:2.5em;padding:2em;text-align:justify}.pala{font-family:Palatino}.article .corps a:after{content:"†";vertical-align:super;line-height:0;font-size:.66em;color:#889}.article .corps .footnotes a:after,.article .corps sup a:after{content:""}.article .corps sup a{font-weight:700;background:#839496;padding:0 .3em;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;color:#fafafc}.article .corps sup a:hover{background:#cb4b16}ul#markdown-toc,.intro .toc ul{font-variant:small-caps;list-style:none;padding-left:1.5em}ul#markdown-toc a:after,.intro .toc ul a:after{content:""}ul#markdown-toc ul ul,.intro .toc ul ul ul{font-variant:normal;line-height:1em;margin-bottom:1em}table{border:1px solid #ccccd0}table tr td{padding:2px .5em}table tr:nth-child(odd){background-color:#f2f2f4}table tr:nth-child(even){background-color:#fafafc}p pre code,ul li pre code,ol li pre code{background:none;border:none;padding:0}p code,ul li code,ol li code{background:#f2f2f4;border:solid 1px #ccccd0;padding:2px}ul.sameline{list-style:none}ul.sameline li{float:left;margin-left:.5em}.resumearticle{background-color:#f2f2f4;border-radius:7px;box-shadow:0 0 5px #c7c7b8 inset,0 0 5px white;margin:1em 0;padding:1em}a.cut{font-size:12px;font-family:Monaco,monospace;text-align:right;display:block;width:100%;opacity:.5;border:1px solid #fafafc;border-radius:3px}a.cut:hover{opacity:1;background-color:#f2f2f4;border-color:#ccccd0;box-shadow:0 0 3px #ccccd0 inset}a.cut strong{font-weight:700}.codehighlight pre{border:4px solid #ccccd0}#social{text-align:left;opacity:.3}#social:hover{opacity:1}.popularblock{width:30.333%;margin:0 1.5%;float:left}.popularblock figure{margin:0}.popularblock figure img{max-width:80%;max-height:6em} \ No newline at end of file +@charset "UTF-8";table.description tr td{border:1px solid #eeeef1}.assombris20{background-color:#eeeef1}body{color:#002b36;background-color:#fafafc}::selection{background:#002b36;color:#93a1a1}::-moz-selection{background:#002b36;color:#93a1a1}pre,code{font-family:Incosolata,Monaco,monospace}pre::selection{background:#fdf6e3;color:#586e75}pre *::selection{background:#fdf6e3;color:#586e75}pre::-moz-selection{background:#fdf6e3;color:#586e75}pre *::-moz-selection{background:#fdf6e3;color:#586e75}a:hover{text-shadow:0 0 2px#faa}a,a:link,a:visited,a:active,a:hover{text-decoration:none;outline:none}a,a:link,a:visited,a:active{color:#002b36}a:hover{color:#cb4b16}hr{color:#eeeef1;border-top:1px solid #eeeef1;border-bottom:none;border-left:none;border-right:none}ul{list-style:square}ol,ul{padding-left:0}ol li,ul li{margin:.5em 0}ol li ul,ol li ol,ul li ol,ul li ul{margin:.5em 1.5em;list-style:circle}body,h1,h2,h3,h4,#entete,.tagname{font-family:Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;line-height:1.4em}.article #afterheader{counter-reset:niv02}.article #afterheader h2{counter-increment:niv02;counter-reset:niv03;marker-offset:3em}.article #afterheader h2:before{content:counter(niv02) ". "}.article #afterheader h3{counter-increment:niv03;counter-reset:niv04}.article #afterheader h3:before{content:counter(niv02) "." counter(niv03) ". "}.article #afterheader h4{counter-increment:niv04}.article #afterheader h4:before{content:counter(niv02) "." counter(niv03) "." counter(niv04) ". "}pre{background-color:#002b36;color:#839496;box-shadow:0 0 1em#001 inset;border-radius:3px;padding:1em;line-height:1.2em;font-size:.9em}p{margin-bottom:1.2em}blockquote{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;font-style:italic;padding:.5em 1em;color:#556}blockquote a:hover{color:#cb4b16}blockquote strong,blockquote b,blockquote i,blockquote em{font-weight:400;font-style:normal;color:#002b36}blockquote ul{padding-left:1.5em}abbr,acronym{font-variant:small-caps;text-decoration:none;border-bottom-width:0}#titre{letter-spacing:-0.06em;border-bottom:4px double #ccccd0;border-top:4px double #ccccd0}#liens .active,#sousliens{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}#liens .active a,#sousliens a{color:#667}#liens .active a:hover,#sousliens a:hover{color:#cb4b16}#liens .active a:hover strong,#liens .active a:hover b,#liens .active a:hover i,#liens .active a:hover em,#liens .active a:hover .nicer,#sousliens a:hover strong,#sousliens a:hover b,#sousliens a:hover i,#sousliens a:hover em,#sousliens a:hover .nicer{color:#ffb17c}#liens .active hr,#sousliens hr{color:#667;border-top:1px solid#667}#liens .active strong,#liens .active b,#liens .active i,#liens .active em,#sousliens strong,#sousliens b,#sousliens i,#sousliens em{color:#002b36}#liens a{border:1px solid#eee;background:rgba(0,0,0,0.05);box-shadow:0 0 2px white,0 0 3px#ccc inset;border:1px solid rgba(0,0,0,0.1);border-radius:3px}#liens a:hover{background:rgba(0,0,0,0.1);box-shadow:0 0 6px#555 inset}#liens .active{text-shadow:0 0 2px rgba(0,0,0,0.5);background-color:#f7f7f9;border:1px solid #e9e9eb;box-shadow:0 0 3px #c7c7c9 inset;border-radius:3px;border-top:none}#lastmod{font-size:.9em}.nojsbutton{font-size:2.5em}#clickcomment,#choixlang > a,#choixrss > a,.return > a{display:block;width:25%;cursor:pointer;margin:1em 0;padding:1em;font-size:16px;line-height:1.4em;border:1px solid #fafafc;color:#ccccd0}#clickcomment:hover,#choixlang > a:hover,#choixrss > a:hover,.return > a:hover{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa}#clickcomment:active,#choixlang > a:active,#choixrss > a:active,.return > a:active{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa;background:#f4f4f6}.return > a,#choixrss > a{float:right}#choix .return > a,#choix #choixrss > a{margin-top:0}.small{font-size:.8em}.sc{font-variant:small-caps}.impact,.darkimpact{font-size:2em;margin:0 auto 1em auto;line-height:1.3em}h1 > .date{font-size:.6em;color:#002b36}.date{font-size:.8em;color:#fafafc;border:1px solid #002b36;text-align:center;width:4.1em;line-height:1.5em;display:inline-block;vertical-align:middle;margin-right:1em}.date .day,.date .month,.date .year{display:block}.date .day{color:#002b36;background-color:#fafafc;float:left;width:1.7em}.date .month{float:right;width:2.3em;background-color:#002b36;color:#fafafc}.date .year{line-height:3ex;clear:both;color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1;border-radius:0}.date .year a{color:#667}.date .year a:hover{color:#cb4b16}.date .year a:hover strong,.date .year a:hover b,.date .year a:hover i,.date .year a:hover em,.date .year a:hover .nicer{color:#ffb17c}.date .year hr{color:#667;border-top:1px solid#667}.date .year strong,.date .year b,.date .year i,.date .year em{color:#002b36}body{text-align:center;font-size:16px}body > #entete{position:absolute;left:0;top:.5em;width:100%;min-width:50em;z-index:8000;padding-bottom:1em;margin-bottom:3em}#titre h2{width:80%;margin-left:auto;margin-right:auto;text-align:center;color:#ccccd0}#titre{text-align:center;width:100%}#titre h1,#titre h2{padding-left:1em;padding-right:1em}#bottom{clear:right;margin-right:0;padding:1.5em;line-height:1.5em;color:#224d58;margin-top:2em;text-align:center}#bottom a{color:#113c47}#bottom a:hover{color:#cb4b16}#sousliens{padding:1em 0;line-height:2em}#sousliens ul{list-style:none;margin-left:4em}ul.horizontal li{display:inline;font-size:.9em}ul.horizontal{margin-top:0;margin-bottom:0}#entete{padding-top:.1em;border-top:1px solid #ccccd0;border-bottom:1px solid #ccccd0}#liens{width:100%;padding:0;clear:both;margin-top:.5em}#liens ul{width:100%;clear:both;padding:0;margin:0}#liens ul li{display:inline-block;height:4em;margin-left:.2em;margin-right:.2em;width:23%}#liens ul li a,#liens ul li span{width:100%;display:block;line-height:4em}.clear{clear:both}#content{line-height:4em;margin-left:auto;margin-right:auto;margin-top:0;position:relative;clear:both;width:52em}.encadre,.black,.red,.intro,.resume,.shadow{padding:2em;margin-top:2em;margin-bottom:2em}.encadre,.black,.red,.shadow{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}.encadre a,.black a,.red a,.shadow a{color:#667}.encadre a:hover,.black a:hover,.red a:hover,.shadow a:hover{color:#cb4b16}.encadre a:hover strong,.encadre a:hover b,.encadre a:hover i,.encadre a:hover em,.encadre a:hover .nicer,.black a:hover strong,.black a:hover b,.black a:hover i,.black a:hover em,.black a:hover .nicer,.red a:hover strong,.red a:hover b,.red a:hover i,.red a:hover em,.red a:hover .nicer,.shadow a:hover strong,.shadow a:hover b,.shadow a:hover i,.shadow a:hover em,.shadow a:hover .nicer{color:#ffb17c}.encadre hr,.black hr,.red hr,.shadow hr{color:#667;border-top:1px solid#667}.encadre strong,.encadre b,.encadre i,.encadre em,.black strong,.black b,.black i,.black em,.red strong,.red b,.red i,.red em,.shadow strong,.shadow b,.shadow i,.shadow em{color:#002b36}pre .red{background:none;padding:0;margin:auto;border:none;box-shadow:none}.intro,.resume{font-size:.9em;font-style:italic;padding:.5em 1em;color:#556}.intro a:hover,.resume a:hover{color:#cb4b16}.intro strong,.intro b,.intro i,.intro em,.resume strong,.resume b,.resume i,.resume em{font-weight:400;font-style:normal;color:#002b36}#afterheader > h1{width:100%;padding-top:1.5em;text-align:left}#afterheader{padding-left:0;padding-right:0}#sousliens{margin-top:3em;margin-bottom:3em;font-size:1.2em;letter-spacing:1px;text-align:left;clear:both}.twilight{line-height:1.1em}.corps{font-size:1.25em;line-height:1.6em;text-align:justify;text-align:left;padding:3em 3em;margin:0;clear:both}.corps img{max-width:30em;border:1px solid #ccccd0;background-color:#fafafc;padding:.5em;box-shadow:0 10px 15px#ccc;border-radius:3px}.corps a:hover img{background-color:#dc3a05}figure{margin:3em 0}figure img{box-shadow:0 10px 15px#ccc inset}figure figcaption{text-align:center;margin:.5em 0}img.clean{border:none}#address{clear:both}.definitionCell{width:5em;vertical-align:top;font-weight:700;text-align:center}.valueCell{text-align:right}.smallblock{float:left;width:50%;font-size:1em;font-weight:700}.largeblock{float:right;width:70%;font-size:1em}#blackpage,#nojsredirect{top:0;left:0;width:100%;height:100%;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:fixed;text-align:center}#blackpage{color:#666;padding-top:10em;background-color:#eee;z-index:9000;cursor:wait}#blackpage img{background:none;border:none}#blackpage a{cursor:pointer}#nojsredirect{z-index:9001}.nojsbutton{width:50%;padding:1em;border:solid 3px white;margin-left:auto;margin-right:auto;margin-top:2em;z-index:9002}.codefile{font-size:.8em;text-align:right;padding-right:1em;margin-right:.1;margin-bottom:-1em}.flush{clear:both}table.description{border-spacing:5px;border-collapse:separate;margin-right:auto;margin-left:auto}table.description tr td{padding-left:.5em;padding-right:.5em;padding-top:.5ex;padding-bottom:.5ex;vertical-align:middle;margin-right:5px}ul.long li{margin-bottom:1em}img{display:block;margin:1.2em auto;background:none;border:none}img.left{float:left;max-width:30%;margin-top:.6em;margin-right:2em}img.inside{display:inline;vertical-align:middle}pre{overflow-x:auto;overflow-y:hidden}.navigationprev,.navigationnext{padding:0;margin-left:.2em;margin-right:.2em;margin-bottom:0;margin-top:3em;width:45%}.navigation .navigationprev,.navigation .navigationnext{width:30%;margin-top:0}.navigation{height:4em;border-bottom:#ccccd0 solid 1px}.presarticleleft,.presarticleright{font-size:1em}.navigationprev{float:left;text-align:left}.navigationnext{float:right;text-align:right}.impact,.darkimpact{text-align:left;width:66%;padding-left:.25em;padding-right:.25em}table.impact{text-align:left}table.impact tr td{padding-left:.25em;padding-right:.25em}#liens{font-size:1.2em}#iemessage{font-size:1.2em;color:#ccc;margin:-10px;padding:1px 0;background:#333}#iemessage strong,#iemessage b,#iemessage i,#iemessage em{color:#ccc}#iemessage a,#iemessage a:visited{color:#eca}.tagname{display:inline;cursor:pointer;margin-left:.5em;margin-right:.5em}.list{margin-top:3em}#menuMessage{font-size:1.2em;line-height:1.5em;width:100%;text-align:center}#next_before_articles{clear:both;width:100%;font-size:1.2em;padding-top:1em;padding-bottom:1em}#previous_articles,#next_articles{color:#889;font-style:italic;font-size:.8em}#previous_articles{float:left;margin-left:1em;width:45%;text-align:left}.previous_article,.next_article{margin-top:1em}#next_articles{float:right;width:45%;margin-right:1em;text-align:right}#rss{font-size:1.2em;text-align:center;display:block;width:100%;float:right;padding:1em .1em}.corps .return a{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;float:left;font-size:2em;margin-top:-0.5em;margin-left:-2em;width:1.5em}a.return{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;font-size:2em;width:1.5em;display:block}a.return:hover{color:#889}.corps .return a:hover{color:#cb4b16}.footnotes{font-size:.8em}.footnotes ol{color:#839496;font-weight:700}.footnotes ol p{color:#002b36;font-weight:400}.fontnotes ol{margin-left:0}.typeset img{display:inline;border:none;margin:0;padding:0}strong,b,i,em{font-weight:400;color:#889}strong a,b a,i a,em a{color:#002b36}strong a:hover,b a:hover,i a:hover,em a:hover{color:#cb4b16}.corps p strong,.corps p b,.corps p i,.corps p em{color:#556}a:hover strong,a:hover b,a:hover i,a:hover em{color:#dc5c27}a:hover .nicer{color:#ffb17c}.nicer{color:#ccccd0;font-family:"Lucida Grande",Tahoma}.block{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;width:26.5%;padding:1em;border-radius:2px;text-align:left;line-height:1em;margin-left:1%;margin-right:1%;font-size:.8em;height:9em}.block a{color:#002b36}.block a:hover{color:#cb4b16}.block h3{margin:0;font-size:1.3em}.block p{line-height:1.2em}.left{float:left}.right{float:right}.corps p a,.corps ul a{color:#556}.corps p a:hover,.corps ul a:hover{color:#cb4b16}ul.bloglist,.archive ul{list-style-type:none;margin:0}ul.bloglist li,.archive ul li{margin-bottom:1em}.button{cursor:pointer;text-align:center}#tagcloud{font-size:.8em;background:#f2f2f4;box-shadow:0 0 6px #ccccd0;border-radius:3px;line-height:2.5em;padding:2em;text-align:justify}.pala{font-family:Palatino}.article .corps a:after{content:"†";vertical-align:super;line-height:0;font-size:.66em;color:#889}.article .corps .footnotes a:after,.article .corps sup a:after{content:""}.article .corps sup a{font-weight:700;background:#839496;padding:0 .3em;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;color:#fafafc}.article .corps sup a:hover{background:#cb4b16}ul#markdown-toc,.intro .toc ul{font-variant:small-caps;list-style:none;padding-left:1.5em}ul#markdown-toc a:after,.intro .toc ul a:after{content:""}ul#markdown-toc ul ul,.intro .toc ul ul ul{font-variant:normal;line-height:1em;margin-bottom:1em}table{border:1px solid #ccccd0}table tr td{padding:2px .5em}table tr:nth-child(odd){background-color:#f2f2f4}table tr:nth-child(even){background-color:#fafafc}p pre code,ul li pre code,ol li pre code{background:none;border:none;padding:0}p code,ul li code,ol li code{background:#f2f2f4;border:solid 1px #ccccd0;padding:2px}ul.sameline{list-style:none}ul.sameline li{float:left;margin-left:.5em}.resumearticle{background-color:#f2f2f4;border-radius:7px;box-shadow:0 0 5px #c7c7b8 inset,0 0 5px white;margin:1em 0;padding:1em}a.cut{font-size:12px;font-family:Monaco,monospace;text-align:right;display:block;width:100%;opacity:.5;border:1px solid #fafafc;border-radius:3px}a.cut:hover{opacity:1;background-color:#f2f2f4;border-color:#ccccd0;box-shadow:0 0 3px #ccccd0 inset}a.cut strong{font-weight:700}.codehighlight pre{border:4px solid #ccccd0}#social{text-align:left;opacity:.3}#social:hover{opacity:1}.popularblock{width:30.333%;margin:0 1.5%;float:left}.popularblock figure{margin:0}.popularblock figure img{max-width:80%;max-height:6em} \ No newline at end of file diff --git a/output/Scratch/css/solarized.css b/output/Scratch/css/solarized.css index 44f428adb..ec21ae4a8 100644 --- a/output/Scratch/css/solarized.css +++ b/output/Scratch/css/solarized.css @@ -133,7 +133,7 @@ Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull Avatar

-

yann.esposito@gmail.com
+

yann.esposito@gmail.com Follow me on twitter
My preferred bookmarks pinboard
Open Source github
diff --git a/output/Scratch/en/about/index.html b/output/Scratch/en/about/index.html index a395b0809..8c5ed405a 100644 --- a/output/Scratch/en/about/index.html +++ b/output/Scratch/en/about/index.html @@ -104,7 +104,7 @@

  • Computer Security: I designed a secure web protocol (similar to TOR), a method to securely remember strong passwords (programmed a dashboard widget and a shell script to use this method).
  • -

    But before all, I love to learn. For example, I learned many programming languages: C, C++, Objective-C, Python, Java, Perl, awk, bash, zsh, LaTeX, Metapost, camL… And I’ve got the same passion about computer science research, computer security, philosophy and many other things.

    +

    But before all, I love to learn. For example, I learned many programming languages: C, C++, Objective-C, Python, Java, Perl, awk, bash, zsh, LaTeX, Metapost, camL… And I’ve got the same passion about computer science research, computer security, philosophy and many other things.

    @@ -118,7 +118,7 @@

    Then I had a post Ph. D. Degree position in the Hubert Curien Laboratory at St-Etienne. My mission consisted to develop a scientific application (SEDiL). This application should be used by biologist and should have a simple and nice user interface.

    -

    Today I work for AirFrance© via Astek. This job need many differents skills, CMS and Web technologies, Perl, JSP, meta-programming…

    +

    Today I work for AirFrance© via Astek. This job need many differents skills, CMS and Web technologies, Perl, JSP, meta-programming…

    diff --git a/output/Scratch/en/blog/01_nanoc/index.html b/output/Scratch/en/blog/01_nanoc/index.html index 0197c8832..f07327e0f 100644 --- a/output/Scratch/en/blog/01_nanoc/index.html +++ b/output/Scratch/en/blog/01_nanoc/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -59,11 +62,11 @@ But a Framework to generate static web pages.

    You have to program yourself webpages, the code -to generate the menu…

    +to generate the menu…

    I added feature to make my website multilingual for example

    -

    You’ll can find many informations on the +

    You’ll can find many informations on the official nanoc website.

    diff --git a/output/Scratch/en/blog/02_ackgrep/code/ack b/output/Scratch/en/blog/02_ackgrep/code/ack index 38a093374..8b80a5e6b 100644 --- a/output/Scratch/en/blog/02_ackgrep/code/ack +++ b/output/Scratch/en/blog/02_ackgrep/code/ack @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh (($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 } diff --git a/output/Scratch/en/blog/02_ackgrep/index.html b/output/Scratch/en/blog/02_ackgrep/index.html index bc7110a45..a68b7cd72 100644 --- a/output/Scratch/en/blog/02_ackgrep/index.html +++ b/output/Scratch/en/blog/02_ackgrep/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,11 +57,11 @@

    update

    -

    As Andy Lester told me ack is a simple file you only have to copy in your ~/bin folder. Now I’ve got ack on my professional server.

    +

    As Andy Lester told me ack is a simple file you only have to copy in your ~/bin folder. Now I’ve got ack on my professional server.

    Go on http://betterthangrep.com to download it.

    -

    Sincerely, I don’t understand ack don’t become a common command on all UNIX systems. I can no more live without. For me it is as essential as which or find.

    +

    Sincerely, I don’t understand ack don’t become a common command on all UNIX systems. I can no more live without. For me it is as essential as which or find.

    @@ -71,35 +74,38 @@

    One of the my main usage of grep is

    -
    -grep 'pattern' **/*(.)
    -
    + + +
    grep 'pattern' **/*(.)
    + +

    Most of time it is enough. But it is far better with colored output. ack-grep in Ubuntu does that. -As I couldn’t install it on my ‘Evil Company Server’, +As I couldn’t install it on my ‘Evil Company Server’, I had done one myself in very few lines:

    -
    -
    -#!/usr/bin/env zsh
    -(($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 }
    +    
    +
    +
    +
    #!/usr/bin/env zsh
    +(($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 }
     
     listeFic=( **/*(.) )
     autoload zargs
    -zargs -- $listeFic -- grep $1 | perl -ne 'use Term::ANSIColor;
    -if (m/([^:]*)(:.*)('$1')(.*)/) {
    -    print color("green").$1;
    -    print color("reset").$2;
    -    print color("black","on_yellow").$3;
    -    print color("reset").$4."\n";
    -} '
    -    
    -
    -
    +zargs -- $listeFic -- grep $1 | perl -ne 'use Term::ANSIColor; +if (m/([^:]*)(:.*)('$1')(.*)/) { + print color("green").$1; + print color("reset").$2; + print color("black","on_yellow").$3; + print color("reset").$4."\n"; +} ' + + +

    For my team and I it is usable enough. diff --git a/output/Scratch/en/blog/03_losthighway/03_losthighway_1/index.html b/output/Scratch/en/blog/03_losthighway/03_losthighway_1/index.html index ff431986f..ccd15282f 100644 --- a/output/Scratch/en/blog/03_losthighway/03_losthighway_1/index.html +++ b/output/Scratch/en/blog/03_losthighway/03_losthighway_1/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -56,7 +59,7 @@

    -

    Movie’s keys

    +

    Movie’s keys

    @@ -64,32 +67,32 @@
    -

    In a first, it is clear for me, it is not a fantastic movie. If you follow this line, you’ll face many problem explaining some scenes.

    +

    In a first, it is clear for me, it is not a fantastic movie. If you follow this line, you’ll face many problem explaining some scenes.

    -

    My hypothesis is the movie describe the Fred’s representation of reality. +

    My hypothesis is the movie describe the Fred’s representation of reality. Each of his tries to escape reality will fail.

    -

    Fred had commited an horrible act, a murder, and try to repair his memory to accepts it. He’ll then create alternative realities.

    +

    Fred had commited an horrible act, a murder, and try to repair his memory to accepts it. He’ll then create alternative realities.

    • In a first time he kills his wife (Renee) because he believes she cheated at him.
    • -
    • In the second part, he’s weaker and will be manipulated by the blond equivalent of Renee to kill Dick Laurent.
    • +
    • In the second part, he’s weaker and will be manipulated by the blond equivalent of Renee to kill Dick Laurent.
    • In a third part, he kills Dick Laurent

    Why this interpretation can be valid?

    -

    Because of the dialog at the begining of the movie. Cops ask Fred if he’s own a video camera:

    +

    Because of the dialog at the begining of the movie. Cops ask Fred if he’s own a video camera:

    -

    “Do you own a video camera?”
    -“No, Fred hates them.”
    -“I like to remember things my own way.”
    -“What do you mean by that?”
    -“How I remember them, not necessarily the way they happened.”

    +

    “Do you own a video camera?”
    +“No, Fred hates them.”
    +“I like to remember things my own way.”
    +“What do you mean by that?”
    +“How I remember them, not necessarily the way they happened.”

    -

    Then, what we see is not reality but the Fred’s perception. Fred is the God of the reality we see. This is why some God/Devil interpretation of the movie works not so bad.

    +

    Then, what we see is not reality but the Fred’s perception. Fred is the God of the reality we see. This is why some God/Devil interpretation of the movie works not so bad.

    diff --git a/output/Scratch/en/blog/03_losthighway/03_losthighway_2/index.html b/output/Scratch/en/blog/03_losthighway/03_losthighway_2/index.html index 0d5c24c09..0cc08c220 100644 --- a/output/Scratch/en/blog/03_losthighway/03_losthighway_2/index.html +++ b/output/Scratch/en/blog/03_losthighway/03_losthighway_2/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -58,17 +61,17 @@

    Who is the mysterious man?

    -

    l'homme mystérieux

    +

    l'homme mystérieux

    -

    Who’s this mysterious man? He tells Fred it’s him who invited him in his house. He’s present at the party and in the house of Fred in the same time. Eyes wide open, looking everything Fred’s doing?

    +

    Who’s this mysterious man? He tells Fred it’s him who invited him in his house. He’s present at the party and in the house of Fred in the same time. Eyes wide open, looking everything Fred’s doing?

    -

    It’s a key of the movie. In my humble opinion, I believe it represents the bad part of Fred. Certainly jalousy. If I was catholic, I’ll said he’s Satan. He observe, film but don’t act. He helps Fred to kill Dick Laurent. +

    It’s a key of the movie. In my humble opinion, I believe it represents the bad part of Fred. Certainly jalousy. If I was catholic, I’ll said he’s Satan. He observe, film but don’t act. He helps Fred to kill Dick Laurent. Fred had let him enter and cannot let him go. As Iago of Shakespeare is imprisonned by its own jalousy. The Mysterious Man help Fred doing the acts of violence. It also force Fred to remember the reality.

    -

    When he makes love to his wife (Renee), he sees the face of the Mysterious Man instead of his wife’s face. In reality, it’s the same person for Fred. It should be her who’s the origin of his interior badness.

    +

    When he makes love to his wife (Renee), he sees the face of the Mysterious Man instead of his wife’s face. In reality, it’s the same person for Fred. It should be her who’s the origin of his interior badness.

    @@ -173,7 +176,7 @@ It also force Fred to remember the reality.

    Created: 08/04/2009 - Modified: 05/09/2010 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/03_losthighway/03_losthighway_3/index.html b/output/Scratch/en/blog/03_losthighway/03_losthighway_3/index.html index 428cb9141..2a58fd521 100644 --- a/output/Scratch/en/blog/03_losthighway/03_losthighway_3/index.html +++ b/output/Scratch/en/blog/03_losthighway/03_losthighway_3/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -56,9 +59,9 @@
    -

    Who’s at the origin of the video tapes?

    +

    Who’s at the origin of the video tapes?

    -

    Certainly it’s the mysterious man (Fred himself) who makes them. +

    Certainly it’s the mysterious man (Fred himself) who makes them. Their reason should be:

      diff --git a/output/Scratch/en/blog/03_losthighway/03_losthighway_4/index.html b/output/Scratch/en/blog/03_losthighway/03_losthighway_4/index.html index 6ea53aefd..d2fa4dfa4 100644 --- a/output/Scratch/en/blog/03_losthighway/03_losthighway_4/index.html +++ b/output/Scratch/en/blog/03_losthighway/03_losthighway_4/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -71,7 +74,7 @@

      which one then?

      The second hypothesis seems better. We can make much more interpretation with it. It explain in most part the strange phone call from Dick Laurent to Pete. -But the first hypothesis remain coherent. And, we should probably make an in depth explanantion using the first hypothesis. And I’m not sure it would be better.

      +But the first hypothesis remain coherent. And, we should probably make an in depth explanantion using the first hypothesis. And I’m not sure it would be better.

      One of the strength of this movie is to understand there is many other coherent hypothesis. It is an expression of the Rashomon effect. Many different persons could describe in a coherent manner what they saw. But each description contradicts the others.

      @@ -85,9 +88,9 @@ But the first hypothesis remain coherent. And, we should probably make an in dep

      There is much to tell about this movie. But I believe I put all essential keys here. It is a proof this movie is not a random one.

      -

      I believe it is essential to remember the “test of Rorschach effet” when watching this movie.

      +

      I believe it is essential to remember the “test of Rorschach effet” when watching this movie.

      -

      I’d like to know or opinion ; is my interpration wrong?

      +

      I’d like to know or opinion ; is my interpration wrong?

    diff --git a/output/Scratch/en/blog/03_losthighway/index.html b/output/Scratch/en/blog/03_losthighway/index.html index bc407da6f..29ce1428a 100644 --- a/output/Scratch/en/blog/03_losthighway/index.html +++ b/output/Scratch/en/blog/03_losthighway/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@
    -

    Lost Highway

    +

    blogimage( ‘intro.jpg’ , ‘Lost Highway’ )

    ...this movie must be watched knowing you'll cannot resolve the solution. At his best you'll can suggest an interpretation close to the one of David Lynch.
    I believe I had found a coherent interpretation which allow to follow the movie without being totally lost. I believed it can give the keys necessary to make its own idea of the movie... @@ -72,10 +75,10 @@ Here some of explanations of Lost Highway I found on the Internet:

    • Fred make a pact with the devil incarnated by the Mysterious Man,
    • Mysterious Man is a video camera,
    • -
    • Just the first part of the story is real. The rest is in the Fred’s imagination,
    • +
    • Just the first part of the story is real. The rest is in the Fred’s imagination,
    -

    and I don’t speak about many point of view found in forums.

    +

    and I don’t speak about many point of view found in forums.

    I finished to find two good site talking about this movie. But none of them still totally convinced me:

    @@ -84,24 +87,24 @@ Here some of explanations of Lost Highway I found on the Internet:

  • the second which state almost the same interpretation about the movie and explain with even more details is on jasonweb
  • -

    Nonetheless, this movie must be watched knowing you’ll cannot resolve the solution. At his best you’ll can suggest an interpretation close to the one of David Lynch.

    +

    Nonetheless, this movie must be watched knowing you’ll cannot resolve the solution. At his best you’ll can suggest an interpretation close to the one of David Lynch.

    I believe I had found a coherent interpretation which allow to follow the movie without being totally lost. I believed it can give the keys necessary to make its own idea of the movie.

    The Rorschach test

    -

    test de Rorschach

    +

    leftblogimage(‘rorschach.gif’ , ‘test de Rorschach’)

    -

    Like the protagonist, everybody see what he want to see in this movie. It is an invitation to think. Watch this movie is a little like watch a Rorschach’s test. What do we see in it? Everybody put its own personnality in the interpretation of the movie.

    +

    Like the protagonist, everybody see what he want to see in this movie. It is an invitation to think. Watch this movie is a little like watch a Rorschach’s test. What do we see in it? Everybody put its own personnality in the interpretation of the movie.

      -
    • If you are mystic, you’ll see in the mysterious man a devil,
    • -
    • If you are more psychanalytics, you’ll see an inconscient part of the protagonist…
    • +
    • If you are mystic, you’ll see in the mysterious man a devil,
    • +
    • If you are more psychanalytics, you’ll see an inconscient part of the protagonist…
    -

    Generally, we stay in this movie and we fail explaining everything. There is almost always a point that don’t fit within the interpretation of the movie. This is why trying to find a unique good interpretation of this movie is a mistake.

    +

    Generally, we stay in this movie and we fail explaining everything. There is almost always a point that don’t fit within the interpretation of the movie. This is why trying to find a unique good interpretation of this movie is a mistake.

    -

    Interprétation ≠ Explanation

    +

    Interprétation ≠ Explanation

    I give an interpretation and not an explanation. Just to tell my vision of the movie should be very different from yours. There is certainly many coherent explanations.

    @@ -219,7 +222,7 @@ There is certainly many coherent explanations.

    Created: 08/04/2009 - Modified: 05/09/2010 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/04_drm/index.html b/output/Scratch/en/blog/04_drm/index.html index d3ef52c9a..cd09f6e39 100644 --- a/output/Scratch/en/blog/04_drm/index.html +++ b/output/Scratch/en/blog/04_drm/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,7 +69,7 @@

    WTF?

    -

    Result, my wife would never buy any TV show on iTunes. She don’t like DVD because it is not as easy to buy and to use than to simply download episodes.

    +

    Result, my wife would never buy any TV show on iTunes. She don’t like DVD because it is not as easy to buy and to use than to simply download episodes.

    @@ -74,8 +77,8 @@ Therefore far less money for you EVIL Copyrighter!!!!!
    -

    My wife won’t see these episodes.
    -This is a ‘LOSE-LOSE’ cooperation.

    +

    My wife won’t see these episodes.
    +This is a ‘LOSE-LOSE’ cooperation.

    diff --git a/output/Scratch/en/blog/05_git_create_remote_branch/code/git-create-new-branch.sh b/output/Scratch/en/blog/05_git_create_remote_branch/code/git-create-new-branch.sh index b72b97815..6b36930d3 100644 --- a/output/Scratch/en/blog/05_git_create_remote_branch/code/git-create-new-branch.sh +++ b/output/Scratch/en/blog/05_git_create_remote_branch/code/git-create-new-branch.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh if (($#<1)); then diff --git a/output/Scratch/en/blog/05_git_create_remote_branch/index.html b/output/Scratch/en/blog/05_git_create_remote_branch/index.html index d62006edc..4970151d4 100644 --- a/output/Scratch/en/blog/05_git_create_remote_branch/index.html +++ b/output/Scratch/en/blog/05_git_create_remote_branch/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -63,23 +66,24 @@ branch to be created remotely.

    Here is the script I use to achieve that:

    -
    -
    -#!/usr/bin/env zsh
    +    
     
    -if (($#<1)); then
    -    print -- "usage: $0:t branch_name" >&2
    +
    +
    #!/usr/bin/env zsh
    +
    +if (($#<1)); then
    +    print -- "usage: $0:t branch_name" >&2
         exit 1
    -fi
    +fi
    +
    +branch=$1
    +git br ${branch}
    +git co ${branch}
    +git config branch.${branch}.remote origin
    +git config branch.${branch}.merge refs/heads/${branch}
    +    
    + -branch=$1 -git br ${branch} -git co ${branch} -git config branch.${branch}.remote origin -git config branch.${branch}.merge refs/heads/${branch} - -
    -

    Of course, I suppose origin is already configured.

    diff --git a/output/Scratch/en/blog/06_How_I_use_git/code/git-create-new-branch b/output/Scratch/en/blog/06_How_I_use_git/code/git-create-new-branch index add959c52..4ce31d7cd 100644 --- a/output/Scratch/en/blog/06_How_I_use_git/code/git-create-new-branch +++ b/output/Scratch/en/blog/06_How_I_use_git/code/git-create-new-branch @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh if (($#<1)); then diff --git a/output/Scratch/en/blog/06_How_I_use_git/code/git-get-remote-branches b/output/Scratch/en/blog/06_How_I_use_git/code/git-get-remote-branches index ca1ef8c3b..dd2b3ebc3 100644 --- a/output/Scratch/en/blog/06_How_I_use_git/code/git-get-remote-branches +++ b/output/Scratch/en/blog/06_How_I_use_git/code/git-get-remote-branches @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # recup branches not on local diff --git a/output/Scratch/en/blog/06_How_I_use_git/index.html b/output/Scratch/en/blog/06_How_I_use_git/index.html index 609f49896..02310d213 100644 --- a/output/Scratch/en/blog/06_How_I_use_git/index.html +++ b/output/Scratch/en/blog/06_How_I_use_git/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@

    I use Git to manage my personnal projects. I have a centralized repository which all my computer should synchronize with. -Unfortunately I didn’t find clearly what I needed on the official Git documentation.

    +Unfortunately I didn’t find clearly what I needed on the official Git documentation.

    In two words, if you want to use an SVN workflow with Git (and all its advantages) here is how to proceed.

    @@ -70,15 +73,18 @@ Unfortunately I didn’t find clearly what I needed on the official Git docu

    Initialisation

    -

    Suppose I’ve got a directory on my local computer containing a project I want to manage via Git. Here what to do:

    +

    Suppose I’ve got a directory on my local computer containing a project I want to manage via Git. Here what to do:

    -
    -cd to/project/directory/
    +
    +
    +
    cd to/project/directory/
     git init
     git add
     git commit
    -
    +
    + +

    Now all files in the to/project/directory/ are versionned. @@ -87,31 +93,40 @@ If you want not to follow some just edit the file .gitignore

    for example mine is:

    -
    -*.swp
    +
    +
    +
    *.swp
     .DS_Store
     ikog.py.bak
     output/Scratch/assets
     output/Scratch/en
     output/Scratch/fr
     output/Scratch/multi
    -
    +
    + +

    Next, you want to put your project on a directory accessible from the web:

    -
    -git clone --bare . /path/to/repository
    -
    + + +
    git clone --bare . /path/to/repository
    +
    + +

    Now on any computer you can do:

    -
    -git clone protocol://path/to/repository local_directory
    -
    + + +
    git clone protocol://path/to/repository local_directory
    +
    + +

    and local_directory will contain an up-to-date project.

    @@ -135,27 +150,36 @@ You should make this operation also on the computer used to create the repositor

    Before begining your work, the first thing to do is to get all modification from the Internet to your local host:

    -
    -git pull
    -
    + + +
    git pull
    +
    + +

    After that you can do (many times):

    -
    -hack, hack, hack...
    +
    +
    +
    hack, hack, hack...
     git add some files
     git commit
    -
    +
    + +

    When you want your local modification to be on the Internet just do a simple:

    -
    -git push
    -
    + + +
    git push
    +
    + +

    All should be ok.

    @@ -163,16 +187,19 @@ git push

    If you have some trouble with the push and pull verify your .git/config file ; it should contain the following lines:

    -
    -...
    -[remote "origin"]
    +
    +
    +
    ...
    +[remote "origin"]
     	url = protocol://url/of/the/repository
     	fetch = +refs/heads/*:refs/remotes/origin/*
    -[branch "master"]
    +[branch "master"]
     	remote = origin
     	merge = refs/heads/master
     ...
    -
    +
    + +

    Branches Synchronisation

    @@ -183,53 +210,63 @@ git push

    Then when you want to create a new branch (locally and remotely) ; you simply have to do a:

    -
    git-create-new-branch branch_name
    -
    +
    + +
    git-create-new-branch branch_name
    + +

    and when you are on another computer and want to get locally all the remote branches you execute:

    -
    git-get-remote-branches
    -
    +
    + +
    git-get-remote-branches
    + +

    Here are the code of theese two scripts:

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -if (($#<1)); then
    -    print -- "usage: $0:t branch_name" >&2
    +
    +
    #!/usr/bin/env zsh
    +
    +if (($#<1)); then
    +    print -- "usage: $0:t branch_name" >&2
         exit 1
    -fi
    +fi
    +
    +branch=$1
    +git br ${branch}
    +git co ${branch}
    +git config branch.${branch}.remote origin
    +git config branch.${branch}.merge refs/heads/${branch}
    +
    + -branch=$1 -git br ${branch} -git co ${branch} -git config branch.${branch}.remote origin -git config branch.${branch}.merge refs/heads/${branch} -
    -
    -
    -
    -#!/usr/bin/env zsh
    +
    +
    +
    +
    #!/usr/bin/env zsh
    +
    +# recup branches not on local
    +localbranches=( $(git br | sed 's/\*/ /') )
    +remoteMissingBranches=( $(git br -r | \
    +    egrep -v "origin/HEAD|(${(j:|:)localbranches})" ) )
    +for br in $remoteMissingBranches; do
    +  branch=${br#origin/}
    +  print "get remote branch $branch"
    +  git br ${branch}
    +  git config branch.${branch}.remote origin
    +  git config branch.${branch}.merge refs/heads/${branch}
    +done
    +
    + -# recup branches not on local -localbranches=( $(git br | sed 's/\*/ /') ) -remoteMissingBranches=( $(git br -r | \ - egrep -v "origin/HEAD|(${(j:|:)localbranches})" ) ) -for br in $remoteMissingBranches; do - branch=${br#origin/} - print "get remote branch $branch" - git br ${branch} - git config branch.${branch}.remote origin - git config branch.${branch}.merge refs/heads/${branch} -done -
    -
    @@ -347,7 +384,7 @@ remoteMissingBranches=( $(git b
    Created: 08/18/2009 - Modified: 04/20/2011 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html b/output/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html index 478a62d66..ee7b8c59f 100644 --- a/output/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html +++ b/output/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,15 +59,15 @@

    How to recompile your screensaver to be Snow Leopard© compatible

    -

    I upgraded to Mac OS X 10.6 Snow Leopard©, and my YClock screensaver didn’t work on it. After searching on google, the problem seems to be just a recompilation away. -Unfortunately, even recompiling it in 64 bit it didn’t work either. +

    I upgraded to Mac OS X 10.6 Snow Leopard©, and my YClock screensaver didn’t work on it. After searching on google, the problem seems to be just a recompilation away. +Unfortunately, even recompiling it in 64 bit it didn’t work either. After a bit more research (thanks to ElectricSheep ).

    I discovered the good parameters for compilation.

    XCode configuration

    -

    For now I didn’t compiled it to work also on Tiger and Leopard. I don’t know XCode enought to know how to make the Garbage collector to be disabled on 32 bits version and enabled on 64 bits version.

    +

    For now I didn’t compiled it to work also on Tiger and Leopard. I don’t know XCode enought to know how to make the Garbage collector to be disabled on 32 bits version and enabled on 64 bits version.

    It was a bit difficult to discover these informations. Hope this post helped someone.

    @@ -183,7 +186,7 @@ After a bit more research (thanks to Created: 09/06/2009 - Modified: 01/11/2012 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist b/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist index 9cccfce9d..555186218 100644 --- a/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist +++ b/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html b/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html index bc292c560..837698af7 100644 --- a/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html +++ b/output/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -61,9 +64,12 @@

    Then from my laptop or my local computer I just have to launch the marvelous

    -
    -ssh -p 443 -D 9050 username@host
    -
    + + +
    ssh -p 443 -D 9050 username@host
    +
    + +

    and a local socks proxy listening on port 9050 is launched. The socks proxy will transfer local requests via the ssh tunnel. Therefore I can surf locally as if I was on my own computer. I can put password and card number without fear the local wifi network to be sniffed. I simply need to configure my web browser to user the socks proxy on localhost and port 9050.

    @@ -72,7 +78,7 @@ ssh -p 443 -D 9050 username@host

    Ssh and Snow Leopard©

    -

    Here I don’t want to talk about how great socks proxy via ssh tunneling is but how to configure my local server.

    +

    Here I don’t want to talk about how great socks proxy via ssh tunneling is but how to configure my local server.

    I have Mac with Snow Leopard© at home and it is far from enough to modify the /etc/sshd.config file. The system use launchd to launch starting daemons.

    @@ -81,44 +87,46 @@ ssh -p 443 -D 9050 username@host

    Create the file /Library/LaunchDaemons/ssh-443.plist containing:

    -
    -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    -<plist version="1.0">
    -<dict>
    -	<key>Disabled</key>
    -	<false/>
    -	<key>Label</key>
    -	<string>local.sshd</string>
    -	<key>Program</key>
    -	<string>/usr/libexec/sshd-keygen-wrapper</string>
    -	<key>ProgramArguments</key>
    -	<array>
    -		<string>/usr/sbin/sshd</string>
    -		<string>-i</string>
    -	</array>
    -	<key>Sockets</key>
    -	<dict>
    -		<key>Listeners</key>
    -		<dict>
    -			<key>SockServiceName</key>
    -			<string>https</string>
    -		</dict>
    -	</dict>
    -	<key>inetdCompatibility</key>
    -	<dict>
    -		<key>Wait</key>
    -		<false/>
    -	</dict>
    -	<key>StandardErrorPath</key>
    -	<string>/dev/null</string>
    -        <key>SHAuthorizationRight</key>
    -        <string>system.preferences</string>
    -</dict>
    -</plist>
    -
    -
    + + + +
    <?xml version="1.0" encoding="UTF-8"?>
    +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    +<plist version="1.0">
    +<dict>
    +	<key>Disabled</key>
    +	<false/>
    +	<key>Label</key>
    +	<string>local.sshd</string>
    +	<key>Program</key>
    +	<string>/usr/libexec/sshd-keygen-wrapper</string>
    +	<key>ProgramArguments</key>
    +	<array>
    +		<string>/usr/sbin/sshd</string>
    +		<string>-i</string>
    +	</array>
    +	<key>Sockets</key>
    +	<dict>
    +		<key>Listeners</key>
    +		<dict>
    +			<key>SockServiceName</key>
    +			<string>https</string>
    +		</dict>
    +	</dict>
    +	<key>inetdCompatibility</key>
    +	<dict>
    +		<key>Wait</key>
    +		<false/>
    +	</dict>
    +	<key>StandardErrorPath</key>
    +	<string>/dev/null</string>
    +        <key>SHAuthorizationRight</key>
    +        <string>system.preferences</string>
    +</dict>
    +</plist>
    +
    + +

    It is a copy of /System/Library/LaunchDaemons/ssh.plist with some modifications:

    diff --git a/output/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/index.html b/output/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/index.html index a08d441df..52b8511b4 100644 --- a/output/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/index.html +++ b/output/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + diff --git a/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish b/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish index da6ce6b0c..0ea3558bf 100644 --- a/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish +++ b/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Author: Yann Esposito diff --git a/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html b/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html index 2685da16f..fec6383a2 100644 --- a/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html +++ b/output/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -83,232 +86,236 @@

    The idea are:

      -
    • synchronize to a temporary folder then swap the name therefore the website isn’t accessible only during the swap time. It takes only the time of two rename.
    • +
    • synchronize to a temporary folder then swap the name therefore the website isn’t accessible only during the swap time. It takes only the time of two rename.
    • reiterate all operations until they work (for example, renaming).

    For now I use rsync which in fact is no more efficient than a simple cp with WebDav. And I should use a method to keep track of elements who have changed. before the publication.

    -

    In fact when I’m on a Mac, I use Transmit which is very cool and far more efficient than the Finder to synchronize files. After the synchronization, I swap the directories.

    +

    In fact when I’m on a Mac, I use Transmit which is very cool and far more efficient than the Finder to synchronize files. After the synchronization, I swap the directories.

    My script take a -s option in order to make only the swap option. It also take a -a in order to put the new index.html which should point to the new homepage (not the iWeb one).

    In order to keep this script working for you, just modify the username by yours (the value of the mobileMeUser).

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Script synchronisant le site sur me.com
    -# normalement, le site est indisponible le moins de temps possible
    -# le temps de deux renommages de répertoire
     
    -mobileMeUser="yann.esposito"
    -siteName="siteName"
    +
    #!/usr/bin/env zsh
     
    -# Depending of my hostname the 
    -if [[ $(hostname) == 'ubuntu' ]]; then
    -    iDisk='/mnt/iDisk'
    -else
    -    iDisk="/Volumes/$mobileMeUser"
    -fi
    +# Script synchronisant le site sur me.com
    +# normalement, le site est indisponible le moins de temps possible
    +# le temps de deux renommages de répertoire
     
    -root=$HOME/Sites/$siteName
    -destRep=$iDisk/Web/Sites/$siteName
    +mobileMeUser="yann.esposito"
    +siteName="siteName"
     
    -[[ ! -d $root ]] && { 
    -    print -- "$root n'existe pas ; vérifiez la conf" >&2; 
    +# Depending of my hostname the 
    +if [[ $(hostname) == 'ubuntu' ]]; then
    +    iDisk='/mnt/iDisk'
    +else
    +    iDisk="/Volumes/$mobileMeUser"
    +fi
    +
    +root=$HOME/Sites/$siteName
    +destRep=$iDisk/Web/Sites/$siteName
    +
    +[[ ! -d $root ]] && { 
    +    print -- "$root n'existe pas ; vérifiez la conf" >&2; 
         exit 1 
     }
     
    -[[ ! -d $destRep ]] && { 
    -    print -- "$destRep n'existe pas, veuillez remonter le FS" >&2; 
    +[[ ! -d $destRep ]] && { 
    +    print -- "$destRep n'existe pas, veuillez remonter le FS" >&2; 
         exit 1 
     }
     
    -if [[ $1 == '-h' ]]; then
    -    print -- "usage: $0:h [-h|-a|-s]"
    -    print -- "  -a sychronise aussi l'index"
    -    print -- "  -h affiche l'aide"
    -    print -- "  -s swappe simplement les répertoires"
    -fi
    +if [[ $1 == '-h' ]]; then
    +    print -- "usage: $0:h [-h|-a|-s]"
    +    print -- "  -a sychronise aussi l'index"
    +    print -- "  -h affiche l'aide"
    +    print -- "  -s swappe simplement les répertoires"
    +fi
     
    -if [[ $1 == '-a' ]]; then
    -    print -- "Synchronisation de l'index (${destRep:h})"
    -    rsync -av $root/index.html ${destRep:h}/index.html
    -fi
    +if [[ $1 == '-a' ]]; then
    +    print -- "Synchronisation de l'index (${destRep:h})"
    +    rsync -av $root/index.html ${destRep:h}/index.html
    +fi
     
    -print -- "Root = $root"
    -print -- "Dest = $destRep"
    +print -- "Root = $root"
    +print -- "Dest = $destRep"
     
    -if [[ ! $1 = '-s' ]]; then
    -    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    -    print -P -- "%B[Sync => tmp]%b"
    +if [[ ! $1 = '-s' ]]; then
    +    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    +    print -P -- "%B[Sync => tmp]%b"
         result=1
         essai=1
    -    while (( $result > 0 )); do
    -        rsync -arv $root/Scratch/ $destRep.tmp
    -        result=$?
    -        if (( $result > 0 )); then
    -            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
    -        fi
    +    while (( $result > 0 )); do
    +        rsync -arv $root/Scratch/ $destRep.tmp
    +        result=$?
    +        if (( $result > 0 )); then
    +            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
    +        fi
             ((essai++))
    -    done
    -fi
    +    done
    +fi
     
    -# SWAP
    -print -P -- "%B[Swap des Répertoires (tmp <=> target)]%b"
    +# SWAP
    +print -P -- "%B[Swap des Répertoires (tmp <=> target)]%b"
     essai=1
    -while [[ -e $destRep.old ]]; do
    -    print -n -- "suppression de $destRep.old"
    -    if ((essai>1)); then 
    -        print " (essai n°$essai)"
    -    else
    +while [[ -e $destRep.old ]]; do
    +    print -n -- "suppression de $destRep.old"
    +    if ((essai>1)); then 
    +        print " (essai n°$essai)"
    +    else
             print
    -    fi
    +    fi
         ((essai++))
    -    \rm -rf $destRep.old
    -done
    +    \rm -rf $destRep.old
    +done
     
    -print -- "  renommage du repertoire sandard vers le .old"
    +print -- "  renommage du repertoire sandard vers le .old"
     essai=1
    -while [[ -e $destRep ]]; do
    -    mv $destRep $destRep.old 
    -    (($?)) && print -- "Echec du renommage (essai n°$essai)" >&2
    +while [[ -e $destRep ]]; do
    +    mv $destRep $destRep.old 
    +    (($?)) && print -- "Echec du renommage (essai n°$essai)" >&2
         ((essai++))
    -done
    +done
     
    -print -- "  renommage du repertoire tmp (nouveau) vers le standard"
    -print -P -- "  %BSite Indisponible%b $(date)"
    +print -- "  renommage du repertoire tmp (nouveau) vers le standard"
    +print -P -- "  %BSite Indisponible%b $(date)"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.tmp $destRep
    -    (($?)) && print -P -- "%B[Site Indisponible]%b(essai n°$essai) Echec du renommage (mv $destRep.tmp $destRep)" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.tmp $destRep
    +    (($?)) && print -P -- "%B[Site Indisponible]%b(essai n°$essai) Echec du renommage (mv $destRep.tmp $destRep)" >&2
         ((essai++))
    -done
    +done
     
    -print -P -- "\t===\t%BSITE DISPONIBLE%b\t==="
    +print -P -- "\t===\t%BSITE DISPONIBLE%b\t==="
     
    -print -- "  renommage du repertoire old vers le tmp"
    +print -- "  renommage du repertoire old vers le tmp"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.old $destRep.tmp
    -    (($?)) && print -P -- "Echec du renommage n°$essai" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.old $destRep.tmp
    +    (($?)) && print -P -- "Echec du renommage n°$essai" >&2
         ((essai++))
    -done
    +done
    +
    +print -P -- "  publication terminée"
    +
    + -print -P -- " publication terminée" -
    -
    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Author: Yann Esposito
    -#   Mail: yann.esposito@gmail.com
    -# Synchronize with "mobileMe" iDisk account.
     
    -mobileMeUser="firstname.lastname"
    -siteName="siteName"
    +
    #!/usr/bin/env zsh
     
    -# Depending of my hostname the 
    -if [[ $(hostname) == 'ubuntu' ]]; then
    -    iDisk='/mnt/iDisk'
    -else
    -    iDisk="/Volumes/$mobileMeUser"
    -fi
    +# Author: Yann Esposito
    +#   Mail: yann.esposito@gmail.com
    +# Synchronize with "mobileMe" iDisk account.
     
    -root=$HOME/Sites/$siteName
    -destRep=$iDisk/Web/Sites/$siteName
    +mobileMeUser="firstname.lastname"
    +siteName="siteName"
     
    -[[ ! -d $root ]] && { 
    -    print -- "$root does not exist ; please verify the configuration ($0)" >&2; 
    +# Depending of my hostname the 
    +if [[ $(hostname) == 'ubuntu' ]]; then
    +    iDisk='/mnt/iDisk'
    +else
    +    iDisk="/Volumes/$mobileMeUser"
    +fi
    +
    +root=$HOME/Sites/$siteName
    +destRep=$iDisk/Web/Sites/$siteName
    +
    +[[ ! -d $root ]] && { 
    +    print -- "$root does not exist ; please verify the configuration ($0)" >&2; 
         exit 1 
     }
     
    -[[ ! -d $destRep ]] && { 
    -    print -- "$destRep does not exist, please mount the filesystem" >&2; 
    +[[ ! -d $destRep ]] && { 
    +    print -- "$destRep does not exist, please mount the filesystem" >&2; 
         exit 1 
     }
     
    -if [[ $1 == '-h' ]]; then
    -    print -- "usage: $0:h [-h|-a|-s]"
    -    print -- "  -a sychronize primary index"
    -    print -- "  -h show this help"
    -    print -- "  -s only swap directories"
    -fi
    +if [[ $1 == '-h' ]]; then
    +    print -- "usage: $0:h [-h|-a|-s]"
    +    print -- "  -a sychronize primary index"
    +    print -- "  -h show this help"
    +    print -- "  -s only swap directories"
    +fi
     
    -if [[ $1 == '-a' ]]; then
    -    print -- "Index synchronisation (${destRep:h})"
    -    rsync -av $root/index.html ${destRep:h}/index.html
    -fi
    +if [[ $1 == '-a' ]]; then
    +    print -- "Index synchronisation (${destRep:h})"
    +    rsync -av $root/index.html ${destRep:h}/index.html
    +fi
     
    -print -- "Root = $root"
    -print -- "Dest = $destRep"
    +print -- "Root = $root"
    +print -- "Dest = $destRep"
     
    -if [[ ! $1 = '-s' ]]; then
    -    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    -    print -P -- "%B[Sync => tmp]%b"
    +if [[ ! $1 = '-s' ]]; then
    +    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    +    print -P -- "%B[Sync => tmp]%b"
         result=1
         essai=1
    -    while (( $result > 0 )); do
    -        rsync -arv $root/Scratch/ $destRep.tmp
    -        result=$?
    -        if (( $result > 0 )); then
    -            print -P -- "%Brsync failed%b (try n°$essai)" >&2
    -        fi
    +    while (( $result > 0 )); do
    +        rsync -arv $root/Scratch/ $destRep.tmp
    +        result=$?
    +        if (( $result > 0 )); then
    +            print -P -- "%Brsync failed%b (try n°$essai)" >&2
    +        fi
             ((essai++))
    -    done
    -fi
    +    done
    +fi
     
    -# SWAP
    -print -P -- "%B[Directory Swap (tmp <=> target)]%b"
    +# SWAP
    +print -P -- "%B[Directory Swap (tmp <=> target)]%b"
     essai=1
    -while [[ -e $destRep.old ]]; do
    -    print -n -- "remove $destRep.old"
    -    if ((essai>1)); then 
    -        print " (try n°$essai)"
    -    else
    +while [[ -e $destRep.old ]]; do
    +    print -n -- "remove $destRep.old"
    +    if ((essai>1)); then 
    +        print " (try n°$essai)"
    +    else
             print
    -    fi
    +    fi
         ((essai++))
    -    \rm -rf $destRep.old
    -done
    +    \rm -rf $destRep.old
    +done
     
    -print -- "  renommage du repertoire sandard vers le .old"
    +print -- "  renommage du repertoire sandard vers le .old"
     essai=1
    -while [[ -e $destRep ]]; do
    -    mv $destRep $destRep.old 
    -    (($?)) && print -- "Failed to rename (try n°$essai)" >&2
    +while [[ -e $destRep ]]; do
    +    mv $destRep $destRep.old 
    +    (($?)) && print -- "Failed to rename (try n°$essai)" >&2
         ((essai++))
    -done
    +done
     
    -print -- "  renaming folder tmp (new) to the standard one"
    -print -P -- "  %BThe WebSite isn't working%b $(date)"
    +print -- "  renaming folder tmp (new) to the standard one"
    +print -P -- "  %BThe WebSite isn't working%b $(date)"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.tmp $destRep
    -    (($?)) && print -P -- "%B[WebSite not working]%b(try n°$essai) Failed to rename (mv $destRep.tmp $destRep)" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.tmp $destRep
    +    (($?)) && print -P -- "%B[WebSite not working]%b(try n°$essai) Failed to rename (mv $destRep.tmp $destRep)" >&2
         ((essai++))
    -done
    +done
     
    -print -P -- "\t===\t%BWEBSITE SHOULD WORK NOW%b\t==="
    +print -P -- "\t===\t%BWEBSITE SHOULD WORK NOW%b\t==="
     
    -print -- "  rename old folder to tmp folder"
    +print -- "  rename old folder to tmp folder"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.old $destRep.tmp
    -    (($?)) && print -P -- "Failed to rename n°$essai" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.old $destRep.tmp
    +    (($?)) && print -P -- "Failed to rename n°$essai" >&2
         ((essai++))
    -done
    +done
    +
    +print -P -- "  Publish terminated"
    +
    + -print -P -- " Publish terminated" -
    -
    diff --git a/output/Scratch/en/blog/11_Load_Disqus_Asynchronously/index.html b/output/Scratch/en/blog/11_Load_Disqus_Asynchronously/index.html index 1745c5482..8ab332a78 100644 --- a/output/Scratch/en/blog/11_Load_Disqus_Asynchronously/index.html +++ b/output/Scratch/en/blog/11_Load_Disqus_Asynchronously/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@

    In fact this method works for old threads. But it fails to create new post threads. This is why I tried and be conquered by intensedebate, as you can see in the bottom of this page.

    -

    Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).

    +

    Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).

    @@ -72,7 +75,7 @@

    I also know there is a jQuery plugin to make just that. Unfortunately I had some issue with CSS.

    -

    Now let’s begin.

    +

    Now let’s begin.

    @@ -85,7 +88,7 @@

    Why should I want to load the disqus javascript asynchronously?

    @@ -97,27 +100,33 @@

    How?

    -

    I give a solution with jQuery, but I’m certain it will work with many other js library.

    +

    I give a solution with jQuery, but I’m certain it will work with many other js library.

    Javascript

    replace:

    -
    -<script type="text/javascript" src="http://disqus.com/forums/YOUR_DISQUS_ID/embed.js"></script>
    -
    + + +
    <script type="text/javascript" src="http://disqus.com/forums/YOUR_DISQUS_ID/embed.js"></script>
    +
    + +

    by

    -
    -window.disqus_no_style=true;
    -$(document).ready(function(){
    -    $.getScript("http://disqus.com/forums/YOUR_DISQUS_ID/embed.js");
    +
    +
    +
    window.disqus_no_style=true;
    +$(document).ready(function(){
    +    $.getScript("http://disqus.com/forums/YOUR_DISQUS_ID/embed.js");
     });
    -
    +
    + +

    If you forget the window.disqus_no_style=true; then your page will be blank. Simply because without this option, the javascript use a document.write action after the document was closed, which cause a complete erasing of it.

    @@ -127,7 +136,7 @@

    But with this option you still need to provide a CSS. This is why you have to copy the css code from the embed.js file and rewrite it in a CSS file. You can download the CSS I obtained.


    -

    Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.

    +

    Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.

    diff --git a/output/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html b/output/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html index 27527d4d9..7c74b74c7 100644 --- a/output/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html +++ b/output/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,7 +59,7 @@

    Disqus vs. Intense Debate

    -

    I made a blog entry about how I tried to integrate Disqus. I had to wait Disqus comment to be displayed before loading correctly my page. This is why I tried to include it in a “non-blocking” way. Unfortunately, I had difficulties to make it works correctly.

    +

    I made a blog entry about how I tried to integrate Disqus. I had to wait Disqus comment to be displayed before loading correctly my page. This is why I tried to include it in a “non-blocking” way. Unfortunately, I had difficulties to make it works correctly.

    Furthermore, it was not trivial to make comment to be shared between multiple version of the same page (each page has three differents representations, one for each language and one more for the multi-language version).

    @@ -64,22 +67,28 @@

    During the time I tried to integrate Disqus I never tried Intense Debate. Now that I have tried, i must confess it does exactly what I needed.

    -

    In order to make it fully asynchronous, you’ve just to download their common js and replace the following line:

    +

    In order to make it fully asynchronous, you’ve just to download their common js and replace the following line:

    -
    -document.getElementsByTagName("head")[0].appendChild(commentScript);
    -
    + + +
    document.getElementsByTagName("head")[0].appendChild(commentScript);
    +
    + +

    by:

    -
    -$(document).ready( function() {
    -    document.getElementsByTagName("head")[0].appendChild(commentScript);
    +
    +
    +
    $(document).ready( function() {
    +    document.getElementsByTagName("head")[0].appendChild(commentScript);
     });
    -
    +
    + +

    And the Winner is: Intense Debate

    @@ -87,8 +96,8 @@

    To conclude, main advantages (for me) of Intense Debate over Disqus:

      -
    • Load Asynchronously ; don’t block my website
    • -
    • Add for free buttons like “share to any” and load them asynchronously.
    • +
    • Load Asynchronously ; don’t block my website
    • +
    • Add for free buttons like “share to any” and load them asynchronously.

    Voilà.

    diff --git a/output/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/index.html b/output/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/index.html index fc4077f8e..8551825f8 100644 --- a/output/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/index.html +++ b/output/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,7 +62,7 @@ All my site is static and pages are generated with -

    This is why I’ll give only a Ruby Generator, not a full javascript generator. +

    This is why I’ll give only a Ruby Generator, not a full javascript generator. But you can easily translate from Ruby to Javascript.

    Here is what you should obtain:

    @@ -79,7 +82,7 @@ But you can easily translate from Ruby to Javascript.

    $('.tag.selected').removeClass('selected'); $('#tag_'+id).addClass('selected'); } -

    alternate reality

      +

      alternate reality

      Mac

      mac

      Mac

      macros

      • @@ -2271,15 +2274,18 @@ But you can easily translate from Ruby to Javascript.

        Here is the simple jQuery code:

        -
        -    $(document).ready( function(){$('.list').hide();} );
        -    function tagSelected(id) {
        -        $('.list').hide();
        -        $('#'+id).fadeIn();
        -        $('.tag.selected').removeClass('selected');
        -        $('#tag_'+id).addClass('selected');
        +
        +
        +
            $(document).ready( function(){$('.list').hide();} );
        +    function tagSelected(id) {
        +        $('.list').hide();
        +        $('#'+id).fadeIn();
        +        $('.tag.selected').removeClass('selected');
        +        $('#tag_'+id).addClass('selected');
             }
        -
        +
        + +

        This code will hide all the div containing links to articles containing the tag. And create a function do show the div containing the tag.

        @@ -2287,28 +2293,34 @@ But you can easily translate from Ruby to Javascript.

        For each tag I create a span element:

        -
        -    <span   style="font-size: 1.0em;" 
        -            class="tag" 
        -            onClick="tagSelected('[TAG]')" 
        -            id="tag_[TAG]">
        +
        +
        +
            <span   style="font-size: 1.0em;" 
        +            class="tag" 
        +            onClick="tagSelected('[TAG]')" 
        +            id="tag_[TAG]">
                 [TAG]
        -    </span> 
        -
        + </span> +
        + +

        and a div containing links associtated to this tag:

        -
        -    <div id="[TAG]">
        -        <h4>[TAG]</h4>
        -        <ul>
        -            <li> LINK 1 </li>
        -            <li> LINK 2 </li>
        -        </ul>
        -    </div> 
        -
        + + +
            <div id="[TAG]">
        +        <h4>[TAG]</h4>
        +        <ul>
        +            <li> LINK 1 </li>
        +            <li> LINK 2 </li>
        +        </ul>
        +    </div> 
        +
        + +
        @@ -2321,179 +2333,197 @@ But you can easily translate from Ruby to Javascript.

        Here is how I generate this using nanoc 2.

        -

        If you want to make it fully jQuery one, it shouldn’t be +

        If you want to make it fully jQuery one, it shouldn’t be too difficult, to use my ruby code and translate it into javascript.

        In a first time tags correpond of the list of all tags.

        -
        -def tags
        -    return @items.tags.join(', ')
        -end
        -
        + + +
        def tags
        +    return @items.tags.join(', ')
        +end
        +
        + +

        A function to create a data structure associating to each tag its occurence.

        -
        -# generate an hash tag => number of occurence of tag
        -def tagNumber
        -    tags={}
        -    @items.each do |p|
        -        if p.tags.nil?
        -            next
        -        end
        -        p.tags.each do |t|
        -            if tags[t]
        -                tags[t]+=1
        -            else
        -                tags[t]=1
        -            end
        -        end
        -    end
        -    return tags
        -end
        -
        + + +
        # generate an hash tag => number of occurence of tag
        +def tagNumber
        +    tags={}
        +    @items.each do |p|
        +        if p.tags.nil?
        +            next
        +        end
        +        p.tags.each do |t|
        +            if tags[t]
        +                tags[t]+=1
        +            else
        +                tags[t]=1
        +            end
        +        end
        +    end
        +    return tags
        +end
        +
        + +

        I also need a data structure who associate to each tag a list of pages (at least url and title).

        -
        -# generate an hash tag => [ page1, page2 ... ]
        -def tagRefs
        -    tagLinks={}
        -    @items.each do |p|
        -        if p.tags.nil?
        -            next
        -        end
        -        p.tags.each do |t|
        -            if tagLinks[t].nil?
        -                tagLinks[t]=[ p ]
        -            else
        -                tagLinks[t] <<= p
        -            end
        -        end
        -    end
        -    return tagLinks
        -end
        -
        + + +
        # generate an hash tag => [ page1, page2 ... ]
        +def tagRefs
        +    tagLinks={}
        +    @items.each do |p|
        +        if p.tags.nil?
        +            next
        +        end
        +        p.tags.each do |t|
        +            if tagLinks[t].nil?
        +                tagLinks[t]=[ p ]
        +            else
        +                tagLinks[t] <<= p
        +            end
        +        end
        +    end
        +    return tagLinks
        +end
        +
        + +

        Calculate the real size of each tag to be displayed.

        I choosen not to use the full range of size for all the tag. Because if no -tag has more than n (here 10) occurences, then it doesn’t deserve to be +tag has more than n (here 10) occurences, then it doesn’t deserve to be of the maximal size.

        -
        -def tagRealSize
        -    tags=tagNumber
        -    max=tags.values.max
        -    min=tags.values.min
        -    # size in CSS em.
        -    minSize=1.0
        -    maxSize=2.5
        -    tagSize={}
        -    tags.each do |t,n|
        -        if ( max == min )
        -            tagSize[t]=minSize
        -        else
        -            # normalized value between 0 and 1
        -            # if not tag appear more than 10 times, 
        -            # then it cannot have the maximal size
        -            tagSize[t]=[ ( n - min + 0.0 ) / ( max - min ) , 
        -                         (n - min) / 10.0 ].min
        -            # from normalized size to real size
        -            tagSize[t]=( tagSize[t] ) * (maxSize - minSize) + minSize
        -        end
        -    end
        -    return tagSize
        -end
        -
        + + +
        def tagRealSize
        +    tags=tagNumber
        +    max=tags.values.max
        +    min=tags.values.min
        +    # size in CSS em.
        +    minSize=1.0
        +    maxSize=2.5
        +    tagSize={}
        +    tags.each do |t,n|
        +        if ( max == min )
        +            tagSize[t]=minSize
        +        else
        +            # normalized value between 0 and 1
        +            # if not tag appear more than 10 times, 
        +            # then it cannot have the maximal size
        +            tagSize[t]=[ ( n - min + 0.0 ) / ( max - min ) , 
        +                         (n - min) / 10.0 ].min
        +            # from normalized size to real size
        +            tagSize[t]=( tagSize[t] ) * (maxSize - minSize) + minSize
        +        end
        +    end
        +    return tagSize
        +end
        +
        + +

        Finaly a function to generate the XHTML/jQuery code

        -
        -# generate an XHTML/jQuery code for tag cloud
        -def tagCloud
        -    tagLinks=tagRefs
        -    tagSize=tagRealSize
         
        -    # begin to write the code
        -    tagCloud=%{<script type="text/javascript">
        -        $(document).ready( function(){$('.list').hide();} );
        -        function tagSelected(id) {
        -            $('.list').hide();
        -            $('#'+id).fadeIn();
        -            $('.tag.selected').removeClass('selected');
        -            $('#tag_'+id).addClass('selected');
        -        }
        -    </script><div id="tagcloud">}
        -    # Creation of the tags <span>
        -    tagSize.sort{|a,b| a[0].downcase <=> b[0].downcase}.each do |t,s|
        -        tag_in_id=t.gsub(/\W/,'_')
        -        # HTML protected version of the tag
        -        # for example, replace ' ' by '&nbsp;'
        -        protected=t.gsub(/&/,'&amp;').gsub(/ /,'&nbsp;').gsub(/</,'&lt;').gsub(/>/,'&gt;')
        -        tagCloud <<= %{
        -            <span style="font-size: #{s}em;" 
        -                  class="tag" 
        -                  onClick="tagSelected('#{tag_in_id}')" 
        -                  id="tag_#{tag_in_id}">
        -                #{protected}
        -            </span> }
        -    end
        -    tagCloud <<= %{</div><div id="hiddenDivs" >}
        -    # Creation of the divs containing links associated to a tag.
        -    tagLinks.each do |t,l|
        -        tag_in_id=t.gsub(/\W/,'_')
        -        tagCloud <<= %{
        -            <div id="#{tag_in_id}" class="list">
        -                <h4>#{t}</h4><ul>}
        -        # generate the link list
        -        l.each do |p|
        -            tagCloud <<= %{<li><a href="#{p.path}">#{p.title}</a></li>}
        -        end
        -        tagCloud <<= %{</ul></div>}
        -    end
        -    tagCloud <<= %{</div>}
        -    return tagCloud # yeah I know it is not necessary
        -end
        -
        + +
        # generate an XHTML/jQuery code for tag cloud
        +def tagCloud
        +    tagLinks=tagRefs
        +    tagSize=tagRealSize
        +
        +    # begin to write the code
        +    tagCloud=%{<script type="text/javascript">
        +        $(document).ready( function(){$('.list').hide();} );
        +        function tagSelected(id) {
        +            $('.list').hide();
        +            $('#'+id).fadeIn();
        +            $('.tag.selected').removeClass('selected');
        +            $('#tag_'+id).addClass('selected');
        +        }
        +    </script><div id="tagcloud">}
        +    # Creation of the tags <span>
        +    tagSize.sort{|a,b| a[0].downcase <=> b[0].downcase}.each do |t,s|
        +        tag_in_id=t.gsub(/\W/,'_')
        +        # HTML protected version of the tag
        +        # for example, replace ' ' by ' '
        +        protected=t.gsub(/&/,'&').gsub(/ /,' ').gsub(/</,'<').gsub(/>/,'>')
        +        tagCloud <<= %{
        +            <span style="font-size: #{s}em;" 
        +                  class="tag" 
        +                  onClick="tagSelected('#{tag_in_id}')" 
        +                  id="tag_#{tag_in_id}">
        +                #{protected}
        +            </span> }
        +    end
        +    tagCloud <<= %{</div><div id="hiddenDivs" >}
        +    # Creation of the divs containing links associated to a tag.
        +    tagLinks.each do |t,l|
        +        tag_in_id=t.gsub(/\W/,'_')
        +        tagCloud <<= %{
        +            <div id="#{tag_in_id}" class="list">
        +                <h4>#{t}</h4><ul>}
        +        # generate the link list
        +        l.each do |p|
        +            tagCloud <<= %{<li><a href="#{p.path}">#{p.title}</a></li>}
        +        end
        +        tagCloud <<= %{</ul></div>}
        +    end
        +    tagCloud <<= %{</div>}
        +    return tagCloud # yeah I know it is not necessary
        +end
        +
        + +
        -

        You can download the complete file to put in your ‘lib’ directory.

        +

        You can download the complete file to put in your ‘lib’ directory.

        Of course to be nice you need the associated CSS

        -
         
        +
        +
        
         // Change the color when mouse over
        -.tag:hover {
        -  color: #cc0000; }
        +.tag:hover {
        +  color: #cc0000; }
         
         // Change the color when tag selected
        -.tag.selected {
        -  color: #6c0000; }
        +.tag.selected {
        +  color: #6c0000; }
         
         // a bit of space and pointer cursor
        -.tag {
        -  cursor: pointer;
        -  margin-left: .5em;
        -  margin-right: .5em; }
        -
        +.tag { + cursor: pointer; + margin-left: .5em; + margin-right: .5em; } +
        + +
        -

        That’s all folks.

        +

        That’s all folks.

      diff --git a/output/Scratch/en/blog/2009-09-replace-all-except-some-part/index.html b/output/Scratch/en/blog/2009-09-replace-all-except-some-part/index.html index c52d9b6df..0dd5e18d5 100644 --- a/output/Scratch/en/blog/2009-09-replace-all-except-some-part/index.html +++ b/output/Scratch/en/blog/2009-09-replace-all-except-some-part/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,11 +59,12 @@

      My problem is simple:

      -

      I want to filter a text except some part of it. I can match easily the part I don’t want to be filtered. For example

      +

      I want to filter a text except some part of it. I can match easily the part I don’t want to be filtered. For example

      -
      -...
      +
      +
      +
      ...
       text
       ...
       BEGIN not to filter
      @@ -71,66 +75,77 @@ END not to filter
       ...
       text
       ...
      -
      +
      + +

      I searched a better way to do that, but the best I can do is using split and scan.

      -
      -def allExceptCode( f, content )
      -    # Beware the behaviour will change if you add
      -    # parenthesis (groups) to the regexp!
      -    regexp=/<code[^>]*>.*?<\/code>|<pre[^>]*>.*?<\/pre>/m
      -    tmp=""
      -    mem=[]
      -    content.scan(regexp).each do |c|
      -        mem <<= c
      -    end
      -    i=0
      -    content.split(regexp).each do |x|
      -        tmp <<= send(f,x) 
      -        if not mem[i].nil? 
      -            tmp <<= mem[i]
      -            i+=1
      -        end
      -    end
      +
      +
      +
      def allExceptCode( f, content )
      +    # Beware the behaviour will change if you add
      +    # parenthesis (groups) to the regexp!
      +    regexp=/<code[^>]*>.*?<\/code>|<pre[^>]*>.*?<\/pre>/m
      +    tmp=""
      +    mem=[]
      +    content.scan(regexp).each do |c|
      +        mem <<= c
      +    end
      +    i=0
      +    content.split(regexp).each do |x|
      +        tmp <<= send(f,x) 
      +        if not mem[i].nil? 
      +            tmp <<= mem[i]
      +            i+=1
      +        end
      +    end
           tmp
      -end
      -
      +end +
      + +

      An usage is:

      -
      -def filter(content)
      -    content.gsub(/e/,'X')
      -end
      +
      +
      +
      def filter(content)
      +    content.gsub(/e/,'X')
      +end
       ...
      -allExceptCode(:filter, content)
      +allExceptCode(:filter, content)
       ...
      -
      +
      + +

      A better syntax would be:

      -
      -# !!!!!!!!!! THIS SYNTAX DOES NOT WORK !!!!!!! #
      -def allExceptCode( f, content )
      -    regexp=/<code[^>]*>.*?<\/code>/m
      -    tmp=""
      -    content.split(regexp).each do |x|
      -        separator=$&
      -        tmp <<= send(f,x) 
      -        if not separator.nil?
      -            tmp <<= separator
      -        end
      -    end
      +
      +
      +
      # !!!!!!!!!! THIS SYNTAX DOES NOT WORK !!!!!!! #
      +def allExceptCode( f, content )
      +    regexp=/<code[^>]*>.*?<\/code>/m
      +    tmp=""
      +    content.split(regexp).each do |x|
      +        separator=$&
      +        tmp <<= send(f,x) 
      +        if not separator.nil?
      +            tmp <<= separator
      +        end
      +    end
           tmp
      -end
      -
      +end +
      + +

      I would expect the split make a search on a regular expression and then give the matched expression into the $& variable. But it is not the case.

      diff --git a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish index 884fc5ee3..d0284728c 100644 --- a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish +++ b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Script synchronisant le site sur me.com diff --git a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework index f83af7601..4ec7888af 100644 --- a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework +++ b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh function samelineprint { diff --git a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html index ddad80e3d..e19d44005 100644 --- a/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html +++ b/output/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,263 +61,273 @@

      Here is my new script, it first create a map which associate to each file its hash. After that it compare this file to the remote one. Then for each different file, update the content.

      -

      Even with this script I also have some problem. Mostly due to ‘webdav’ issues. For example, renaming a folder work really badly (on Linux at least). I use webdavfs. For example:

      +

      Even with this script I also have some problem. Mostly due to ‘webdav’ issues. For example, renaming a folder work really badly (on Linux at least). I use webdavfs. For example:

      -
      -mv folder folder2
      -
      +
      -

      It returns OK and I’ve got:

      +
      mv folder folder2
      +
      -
      -$ ls 
      +
      + +

      It returns OK and I’ve got:

      + +
      + +
      $ ls 
       folder folder2
      -
      + -

      Booh….

      +
      -

      In order to handle most webdav issues I use a framework in zsh. It handle almost all except the correct renaming of folder. Working on it… Anyway here is the code I use.

      +

      Booh….

      -
      -
      -#!/usr/bin/env zsh
      +

      In order to handle most webdav issues I use a framework in zsh. It handle almost all except the correct renaming of folder. Working on it… Anyway here is the code I use.

      + +
      + + +
      #!/usr/bin/env zsh
       
       function samelineprint {
      -    print -n -P -- "\r$*"
      +    print -n -P -- "\r$*"
       }
       
      -# avec 1 essai par seconde: 300 = 5 minutes
      +# avec 1 essai par seconde: 300 = 5 minutes
       maxessais=300
       
      -# try to create a directory until success
      +# try to create a directory until success
       function trymkdir {
      -    target="$1"
      -    print -- mkdir -p $target
      +    target="$1"
      +    print -- mkdir -p $target
           local essai=1
      -    while ! mkdir -p $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    while ! mkdir -p $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to copy until success
      +# try to copy until success
       function trycp {
      -    element="$1"
      -    target="$2"
      -    if [[ ! -d ${target:h} ]]; then
      -        trymkdir ${target:h}
      -    fi
      +    element="$1"
      +    target="$2"
      +    if [[ ! -d ${target:h} ]]; then
      +        trymkdir ${target:h}
      +    fi
           local essai=1
      -    print -- cp $element $target
      -    while ! \cp $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    print -- cp $element $target
      +    while ! \cp $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to remove until success
      +# try to remove until success
       function tryrm {
      -    target="$1"
      +    target="$1"
           local essai=1
      -    local options=''
      -    [[ -d $target ]] && options='-rf'
      -    print -- rm $options $target
      -    while ! rm $options $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    local options=''
      +    [[ -d $target ]] && options='-rf'
      +    print -- rm $options $target
      +    while ! rm $options $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "rm reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "rm reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to rename until success
      +# try to rename until success
       function tryrename {
      -    element="$1"
      -    target="$2"
      +    element="$1"
      +    target="$2"
           local essai=1
      -    while [[ -e $target ]]; do
      -        samelineprint "Echec n°$essai le fichier $target existe déjà"
      +    while [[ -e $target ]]; do
      +        samelineprint "Echec n°$essai le fichier $target existe déjà"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      +        ((essai>maxessais)) && exit 5
               sleep 1
      -    done
      -    print -- mv $element $target
      -    while ! mv $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    done
      +    print -- mv $element $target
      +    while ! mv $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 4
      -    done
      +        ((essai>maxessais)) && exit 4
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "mv reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "mv reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to move until success
      +# try to move until success
       function trymv {
      -    element="$1"
      -    target="$2"
      +    element="$1"
      +    target="$2"
           local essai=1
      -    print -- mv $element $target
      -    while ! mv $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    print -- mv $element $target
      +    while ! mv $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "mv reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "mv reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
      -
      -
      + + +

      And here is the code on how I synchronize my website. There is a little cryptic code. It correspond a problem caused by the bluecloth filter which is a markdown program made in ruby. Each time my email is written it is transformed differently. This is why I remove this part from the content of each html file. Without it, all my files containing email are different at each regeneration of my website.

      -
      -
      -#!/usr/bin/env zsh
      +
      -# Script synchronisant le site sur me.com -# normalement, le site est indisponible le moins de temps possible -# le temps de deux renommages de répertoire -# get configuration -# mostly directories -source $0:h/config +
      #!/usr/bin/env zsh
       
      -# get trycp function (copy until success)
      -source $0:h/webdav-framework
      +# Script synchronisant le site sur me.com
      +# normalement, le site est indisponible le moins de temps possible
      +# le temps de deux renommages de répertoire
       
      -if [[ $1 == '-h' ]]; then
      -    print -- "usage : $0:h [-h|-s|-d]"
      -    print -- "  -a sychronise aussi l'index"
      -    print -- "  -h affiche l'aide"
      -    print -- "  -d modification directe (pas de swap)"
      -    print -- "  -s swappe simplement les répertoires"
      -fi
      +# get configuration
      +# mostly directories
      +source $0:h/config
       
      -# publication incrementale
      +# get trycp function (copy until success)
      +source $0:h/webdav-framework
      +
      +if [[ $1 == '-h' ]]; then
      +    print -- "usage : $0:h [-h|-s|-d]"
      +    print -- "  -a sychronise aussi l'index"
      +    print -- "  -h affiche l'aide"
      +    print -- "  -d modification directe (pas de swap)"
      +    print -- "  -s swappe simplement les répertoires"
      +fi
      +
      +# publication incrementale
       function incrementalPublish {
      -    local ydestRep=$destRep$suffix
      -    localRef="$srcRep/map.yrf"
      -    print -- "Creation du fichier de references"
      -    create-reference-file.sh > $localRef
      -    remoteRef="/tmp/remoteSiteMapRef.$$.yrf"
      -    if [[ ! -e "$ydestRep/map.yrf" ]]; then
      -        # pas de fichier de reference sur la cible
      -        print -- "pas de fichier de reference sur la cible, passage en mode rsync"
      +    local ydestRep=$destRep$suffix
      +    localRef="$srcRep/map.yrf"
      +    print -- "Creation du fichier de references"
      +    create-reference-file.sh > $localRef
      +    remoteRef="/tmp/remoteSiteMapRef.$$.yrf"
      +    if [[ ! -e "$ydestRep/map.yrf" ]]; then
      +        # pas de fichier de reference sur la cible
      +        print -- "pas de fichier de reference sur la cible, passage en mode rsync"
               rsyncPublish
               swap
      -    else
      -        trycp "$ydestRep/map.yrf" "$remoteRef"
      +    else
      +        trycp "$ydestRep/map.yrf" "$remoteRef"
               typeset -U filesToUpdate
      -        filesToUpdate=( $(diff $localRef $remoteRef | awk '/^[<>]/ {print $2}' ) )
      -        if ((${#filesToUpdate} == 1)); then
      -            print -- "Seul le fichier ${filesToUpdate} sera téléversé"
      -        elif ((${#filesToUpdate}<10)); then
      -            print -- "${#filesToUpdate} fichiers seront téléversés :"
      -            print -- "${filesToUpdate}"
      -        else
      -            print -- "${#filesToUpdate} fichiers seront téléversés"
      -        fi
      -        # copy all file with some differences
      -        # except the map in case of error
      -        for element in $filesToUpdate; do
      -            if [[ $element == "/map.yrf" ]]; then
      -                continue
      -            fi
      -            if [[ -e $srcRep$element ]]; then
      -                trycp $srcRep$element $ydestRep$element
      -            else
      -                tryrm $ydestRep$element
      -            fi
      -        done
      -        # if all went fine, copy the map file
      -        trycp $srcRep/map.yrf $ydestRep/map.yrf
      -        # remove the temporary file
      -        \rm $remoteRef
      -        # if we have used the tmp directory we swap
      -        if [[ "$suffix" != "" ]]; then
      +        filesToUpdate=( $(diff $localRef $remoteRef | awk '/^[<>]/ {print $2}' ) )
      +        if ((${#filesToUpdate} == 1)); then
      +            print -- "Seul le fichier ${filesToUpdate} sera téléversé"
      +        elif ((${#filesToUpdate}<10)); then
      +            print -- "${#filesToUpdate} fichiers seront téléversés :"
      +            print -- "${filesToUpdate}"
      +        else
      +            print -- "${#filesToUpdate} fichiers seront téléversés"
      +        fi
      +        # copy all file with some differences
      +        # except the map in case of error
      +        for element in $filesToUpdate; do
      +            if [[ $element == "/map.yrf" ]]; then
      +                continue
      +            fi
      +            if [[ -e $srcRep$element ]]; then
      +                trycp $srcRep$element $ydestRep$element
      +            else
      +                tryrm $ydestRep$element
      +            fi
      +        done
      +        # if all went fine, copy the map file
      +        trycp $srcRep/map.yrf $ydestRep/map.yrf
      +        # remove the temporary file
      +        \rm $remoteRef
      +        # if we have used the tmp directory we swap
      +        if [[ "$suffix" != "" ]]; then
                   swap
      -        fi
      -    fi
      +        fi
      +    fi
       }
       
      -# publication via rsync
      +# publication via rsync
       function rsyncPublish {
           result=1
           essai=1
      -    while (( $result > 0 )); do
      -        print -- rsync -arv $srcRep/ $destRep.tmp
      -        if ((!testmode)); then
      -            rsync -arv $srcRep/ $destRep.tmp
      -        fi
      -        result=$?
      -        if (( $result > 0 )); then
      -            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
      -        fi
      +    while (( $result > 0 )); do
      +        print -- rsync -arv $srcRep/ $destRep.tmp
      +        if ((!testmode)); then
      +            rsync -arv $srcRep/ $destRep.tmp
      +        fi
      +        result=$?
      +        if (( $result > 0 )); then
      +            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
      +        fi
               ((essai++))
      -    done
      +    done
       }
       
      -# swap
      +# swap
       function swap {
      -    print -P -- "%B[Directory Swap (tmp <=> target)]%b"
      -    [[ -e $destRep.old ]] && tryrm $destRep.old
      +    print -P -- "%B[Directory Swap (tmp <=> target)]%b"
      +    [[ -e $destRep.old ]] && tryrm $destRep.old
           
      -    print -- "  renommage du repertoire sandard vers le .old"
      -    tryrename $destRep $destRep.old 
      +    print -- "  renommage du repertoire sandard vers le .old"
      +    tryrename $destRep $destRep.old 
           
      -    print -- "  renommage du repertoire tmp (nouveau) vers le standard"
      -    print -P -- "%B[Site Indisponible]%b $(date)"
      -    tryrename $destRep.tmp $destRep
      -    print -P -- "%B[Site Disponible]%b $(date)"
      +    print -- "  renommage du repertoire tmp (nouveau) vers le standard"
      +    print -P -- "%B[Site Indisponible]%b $(date)"
      +    tryrename $destRep.tmp $destRep
      +    print -P -- "%B[Site Disponible]%b $(date)"
           
      -    print -- "  renommage du repertoire old vers le tmp"
      -    tryrename $destRep.old $destRep.tmp
      +    print -- "  renommage du repertoire old vers le tmp"
      +    tryrename $destRep.old $destRep.tmp
       
      -    print -P -- "  publication terminée"
      +    print -P -- "  publication terminée"
       }
       
      -print -- "Root = $webroot"
      -print -- "Dest = $destRep"
      +print -- "Root = $webroot"
      +print -- "Dest = $destRep"
       
      -if [[ "$1" = "-s" ]]; then
      +if [[ "$1" = "-s" ]]; then
           swap
      -else 
      -    if [[ "$1" = "-d" ]]; then
      -        suffix=""
      -    else
      -        suffix=".tmp"
      -    fi
      -    print -P -- "%BSync%b[${Root:t} => ${destRep:t}$suffix]"
      +else 
      +    if [[ "$1" = "-d" ]]; then
      +        suffix=""
      +    else
      +        suffix=".tmp"
      +    fi
      +    print -P -- "%BSync%b[${Root:t} => ${destRep:t}$suffix]"
           incrementalPublish
      -fi
      -
      -
      +fi + + +

      This is my way to replace rsync with filesystem not handling it. -Hope it is usefull. I’ll be happy to hear a way to handle the webdav rename folder problem. This is really annoying.

      +Hope it is usefull. I’ll be happy to hear a way to handle the webdav rename folder problem. This is really annoying.

      diff --git a/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js b/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js index 661daa2f2..6b047dbb2 100644 --- a/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js +++ b/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js @@ -1,4 +1,3 @@ - // Remove all CSS I don't want to use on IE $('link[rel=stylesheet]').each(function(i) { diff --git a/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/index.html b/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/index.html index f914c8435..263109b62 100644 --- a/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/index.html +++ b/output/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,38 +59,43 @@

      For developer IE is a nightmare. This is why, I use a method to disable my standard CSS and enable a IE only CSS. I use jQuery to accomplish that.

      -
      -$(document).ready( function() {
      -    if ($.browser["msie"]) {
      -        // include the ie.js file
      -        $('head').append('<script type="text/javascript" src="/js/ie.js"></scr' + 'ipt>');
      +
      + +
      $(document).ready( function() {
      +    if ($.browser["msie"]) {
      +        // include the ie.js file
      +        $('head').append('<script type="text/javascript" src="/js/ie.js"></scr' + 'ipt>');
           }
       });
      -
      +
      -
      -
      -// Remove all CSS I don't want to use on IE
      -$('link[rel=stylesheet]').each(function(i)
      +
      + +
      + + +
      // Remove all CSS I don't want to use on IE
      +$('link[rel=stylesheet]').each(function(i)
       {
      -    if (this.getAttribute('href') == '/css/layout.css') 
      -        this.disabled = true;
      -    if (this.getAttribute('href') == '/css/shadows.css') 
      -        this.disabled = true;
      -    if (this.getAttribute('href') == '/css/gen.css')    
      -        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/layout.css') 
      +        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/shadows.css') 
      +        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/gen.css')    
      +        this.disabled = true;
       }) ;
       
      -// Append the CSS for IE only
      -$('head').append('<link rel="stylesheet" type="text/css" href="/css/ie.css"/>');
      +// Append the CSS for IE only
      +$('head').append('<link rel="stylesheet" type="text/css" href="/css/ie.css"/>');
       
      -// I also add a message on top of the page
      -$('body').prepend('<div id="iemessage"><p><span class="fr"><em>Avec <a href="http://www.firefox.com"> Firefox </a> et <a href="http://www.apple.com/safari">Safari</a> cette page est bien plus jolie !</em></span><span class="en"><em>This page is far nicer with <a href="http://www.firefox.com"> Firefox </a> and <a href="http://www.apple.com/safari">Safari</a>!</em></span></p>.</div>');
      +// I also add a message on top of the page
      +$('body').prepend('<div id="iemessage"><p><span class="fr"><em>Avec <a href="http://www.firefox.com"> Firefox </a> et <a href="http://www.apple.com/safari">Safari</a> cette page est bien plus jolie !</em></span><span class="en"><em>This page is far nicer with <a href="http://www.firefox.com"> Firefox </a> and <a href="http://www.apple.com/safari">Safari</a>!</em></span></p>.</div>');
       
      -
      -
      + -

      That’s it.

      +
      + +

      That’s it.

      diff --git a/output/Scratch/en/blog/2009-10-Focus-vs-Minimalism/index.html b/output/Scratch/en/blog/2009-10-Focus-vs-Minimalism/index.html index 098ff1314..690db718e 100644 --- a/output/Scratch/en/blog/2009-10-Focus-vs-Minimalism/index.html +++ b/output/Scratch/en/blog/2009-10-Focus-vs-Minimalism/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,9 +57,9 @@
      -

      I believe the goal researched by minimalism is Focus. But I don’t believe minimalism should be the goal. Focus should be the goal, and I believe minimalism isn’t necessary to reach it.

      +

      I believe the goal researched by minimalism is Focus. But I don’t believe minimalism should be the goal. Focus should be the goal, and I believe minimalism isn’t necessary to reach it.

      -

      This is why my design is not minimalist, but I decided to remove most of the navigation stuff of all pages of my website. May be I’ll prefer to hide the menu only when you are on blog article. For now, I hide the menu everywhere on the website.

      +

      This is why my design is not minimalist, but I decided to remove most of the navigation stuff of all pages of my website. May be I’ll prefer to hide the menu only when you are on blog article. For now, I hide the menu everywhere on the website.

      @@ -71,57 +74,66 @@

      The HTML:

      -
      -<div id="menuButton"></div>
      -<div id="entete">#content of the menu</div>
      -
      + + +
      <div id="menuButton"></div>
      +<div id="entete">#content of the menu</div>
      +
      + +

      The CSS:

      -
      -#menuButton {
      -  font-size: 2em;
      -  height: 2em;
      -  line-height: 1.8em;
      -  width: 2em;
      -  position: fixed;
      -  left: 0;
      -  top: 0; 
      -  z-index: 9001 }
      +
      + +
      #menuButton {
      +  font-size: 2em;
      +  height: 2em;
      +  line-height: 1.8em;
      +  width: 2em;
      +  position: fixed;
      +  left: 0;
      +  top: 0; 
      +  z-index: 9001 }
      +
      +#menuButton:hover {
      +  cursor: pointer; }
      +
      +#entete {
      +  top: 5em;
      +  left: 0;
      +  position: fixed;
      +  width: 10em;
      +  z-index: 9000; }
      +
      -#menuButton:hover { - cursor: pointer; } -#entete { - top: 5em; - left: 0; - position: fixed; - width: 10em; - z-index: 9000; } -

      The javascript code (using jQuery)

      -
      -function hideMenu() {
      -    $('#entete').animate({left:"-10em"}, 500 );
      -    $('#menuButton').html('&rarr;');
      +
      +
      +
      function hideMenu() {
      +    $('#entete').animate({left:"-10em"}, 500 );
      +    $('#menuButton').html('→');
       }
      -function showMenu() {
      -    $('#entete').animate({left:"0em"}, 500 );
      -    $('#menuButton').html('&larr;');
      +function showMenu() {
      +    $('#entete').animate({left:"0em"}, 500 );
      +    $('#menuButton').html('←');
       }
      -function toggleMenu() {
      -    if ( $('#entete').css('left')=='-10em' ) {
      +function toggleMenu() {
      +    if ( $('#entete').css('left')=='-10em' ) {
               showMenu();
      -    } else {
      +    } else {
               hideMenu();
           }
       }
      -
      +
      + +

      And the result is shown in the top left corner of this website.

      diff --git a/output/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/index.html b/output/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/index.html index 8f6cf1011..550e54342 100644 --- a/output/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/index.html +++ b/output/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -79,7 +82,7 @@
      -

      I first tried to integrate queryLoader, but it didn’t fill my needs.

      +

      I first tried to integrate queryLoader, but it didn’t fill my needs.

      The plugin add a black div to hide all the content. But as the script had to be launched at the end of the source code my website show for a small time.

      @@ -90,21 +93,25 @@

      In a first time, I added at the top of the body the div hiding all the content.

      -
      -...
      -<body>
      -<div id="blackpage">
      +
      +
      +
      ...
      +<body>
      +<div id="blackpage">
           content to display during the loading.
      -</div>
      +</div>
       ...
      -
      +
      + +

      and here is the associated CSS to #blackpage:

      -
      -#blackpage
      +
      +
      +
      #blackpage
         top: 0 
         left: 0 
         width: 100%
      @@ -117,19 +124,24 @@
         text-align: center
         color: #666
         padding-top: 10em
      -  background-color: #eee
      +  background-color: #eee
         z-index: 9000
      -
      +
      + +

      and the associated jQuery code:

      -
      -$(document).ready(function(){
      -    $('#blackpage').fadeOut();
      +
      +
      +
      $(document).ready(function(){
      +    $('#blackpage').fadeOut();
       });
      -
      +
      + +

      Yes, it is as simple as that. And, putting the #blackpage div at the top of my page, I ensure to hide anything while loading.

      diff --git a/output/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html b/output/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html index 0faf9128d..62f0b57b5 100644 --- a/output/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html +++ b/output/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -61,81 +64,90 @@

      HTML:

      -
      -    <div id="menuButton"></div>
      -    <div id="entete">
      -        <ul>
      -            <li> menu item 1 </li>
      +
      +
      +
          <div id="menuButton"></div>
      +    <div id="entete">
      +        <ul>
      +            <li> menu item 1 </li>
                   ...
      -            <li> menu item n </li>
      -        </ul>
      -    </div>
      -
      + <li> menu item n </li> + </ul> + </div> +
      + +

      CSS:

      -
      -    #entete {
      -      top: 1em;
      -      left: 0;
      -      position: fixed;
      -      width: 10em;
      -      z-index: 2000; }
      +
      + +
          #entete {
      +      top: 1em;
      +      left: 0;
      +      position: fixed;
      +      width: 10em;
      +      z-index: 2000; }
           
      -    #entete {
      -      top: 1em;
      -      height: 22em;
      -      left: 0;
      -      position: fixed;
      -      width: 10em; }
      -
      + #entete { + top: 1em; + height: 22em; + left: 0; + position: fixed; + width: 10em; } +
      + +

      Javascript:

      -
      -var last=0;
      +
      -// will hide the menu in 5 seconds -// if the variable 'last' has not changed its value -function autoHideMenu(value) { - setTimeout(function(){ - if ( last == value ) { hideMenu(); } - },5000); +
      var last=0;
      +
      +// will hide the menu in 5 seconds
      +// if the variable 'last' has not changed its value
      +function autoHideMenu(value) {
      +    setTimeout(function(){
      +        if ( last == value ) { hideMenu(); }
      +    },5000);
       }
       
      -$(document).ready( function() {
      -    // show the menu when the mouse is on
      -    // the good area
      -    $('#menuButton').hover(showMenu);
      +$(document).ready( function() {
      +    // show the menu when the mouse is on
      +    // the good area
      +    $('#menuButton').hover(showMenu);
       
      -    // If the mouse is on the menu change the
      -    // value of 'last'
      -    // try to hide the menu when the mouse 
      -    // go out off the menu.
      -    $('#entete').hover(
      -        function(){last+=1;}, 
      -        function(){autoHideMenu(last);} );
      -    autoHideMenu(0);
      +    // If the mouse is on the menu change the
      +    // value of 'last'
      +    // try to hide the menu when the mouse 
      +    // go out off the menu.
      +    $('#entete').hover(
      +        function(){last+=1;}, 
      +        function(){autoHideMenu(last);} );
      +    autoHideMenu(0);
       });
       
      -// show / hide menu functions details
      +// show / hide menu functions details
       
      -// move to the left
      -function hideMenu() { 
      -    $('#entete').animate({left:"-10em"}, 500 ); 
      +// move to the left
      +function hideMenu() { 
      +    $('#entete').animate({left:"-10em"}, 500 ); 
       }
       
      -// move to right and will try to hide in 5 sec.
      -function showMenu() { 
      -    $('#entete').animate({left:"0em"}, 500 );
      -    last+=1;
      +// move to right and will try to hide in 5 sec.
      +function showMenu() { 
      +    $('#entete').animate({left:"0em"}, 500 );
      +    last+=1;
           autoHideMenu(last);
       }
       
      -
      +
      -

      Simple and lightweight. No timer (almost), no memory leak, no Date…

      +
      + +

      Simple and lightweight. No timer (almost), no memory leak, no Date…

      diff --git a/output/Scratch/en/blog/2009-10-launch-daemon-from-command-line/index.html b/output/Scratch/en/blog/2009-10-launch-daemon-from-command-line/index.html index b7171cf3a..738fc33bb 100644 --- a/output/Scratch/en/blog/2009-10-launch-daemon-from-command-line/index.html +++ b/output/Scratch/en/blog/2009-10-launch-daemon-from-command-line/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,13 +57,16 @@
      -

      Here is a tip, I don’t know why, but I almost always forgot how to do that.

      +

      Here is a tip, I don’t know why, but I almost always forgot how to do that.

      When you want to launch a command and this command should not be killed after you close your terminal. Here is how to accomplish that from command line:

      -
      -nohup cmd &
      -
      +
      + +
      nohup cmd &
      +
      + + where cmd is your command.
      diff --git a/output/Scratch/en/blog/2009-10-untaught-git-usage/index.html b/output/Scratch/en/blog/2009-10-untaught-git-usage/index.html index b4ae1474c..85bd90f7f 100644 --- a/output/Scratch/en/blog/2009-10-untaught-git-usage/index.html +++ b/output/Scratch/en/blog/2009-10-untaught-git-usage/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,9 +57,9 @@
      -

      I explain why I had so much difficulties to use Git. There is an “untaught rule” that make hard to work without. Until I read the good document.

      +

      I explain why I had so much difficulties to use Git. There is an “untaught rule” that make hard to work without. Until I read the good document.

      -

      “Cheap branches” aren’t designed to be totally isolated branches but rather should follow a “Master Branch”. There is a Standard Workflow to follow. If you don’t follow it, you prepare yourself with some hard time with Git.

      +

      “Cheap branches” aren’t designed to be totally isolated branches but rather should follow a “Master Branch”. There is a Standard Workflow to follow. If you don’t follow it, you prepare yourself with some hard time with Git.

      @@ -72,7 +75,7 @@

      I must say I was completely convinced. And the more you learn about DCVS the more you see good reason to use them.

      -

      I then needed a versioning system for my team. As they were not used to open source versioning system except those heavy, with a GUI and with and administrator

      +

      I then needed a versioning system for my team. As they were not used to open source versioning system except those heavy, with a GUI and with and administrator

      After some web searches, I founded three main choices:

      @@ -82,7 +85,7 @@
    • Mercurial
    -

    After trying each other I chosen Bazaar. It has the simplest User Interface*. My choice was done.

    +

    After trying each other I chosen Bazaar. It has the simplest User Interface*. My choice was done.

    From Bazaar to Git

    @@ -91,21 +94,21 @@

    After some times, reading on many blogs, I realize Git is far more popular and by influent people.

    I then decide to use Git in particular to version this current website. -But after trying it, I found it difficult and couter intuitive (I’ll speak a work about it later).

    +But after trying it, I found it difficult and couter intuitive (I’ll speak a work about it later).

    After calling for some help, when I say Bazaar is much simpler to learn, some people answer me that Git:

    -

    SO-MUCH-EASY my 12 year old daughter uses it to version its school documents. She has no difficulties at all, creating branches, blah, blah, blah…

    +

    SO-MUCH-EASY my 12 year old daughter uses it to version its school documents. She has no difficulties at all, creating branches, blah, blah, blah…

    If a 12 years old girl has no problem with Git and I (with my Computer Science Ph.D.) have difficulties to uses it like I want, it is frustrating and humiliating. But what makes Git natural for some people and confusing for me?

    -

    I finally understood why reading a document I didn’t read before. It was the untaught part of the conception. The part every developer found so natural it is not necessary to say a word about it. But it was not natural for me.

    +

    I finally understood why reading a document I didn’t read before. It was the untaught part of the conception. The part every developer found so natural it is not necessary to say a word about it. But it was not natural for me.

    -

    - I speak about ClearCase©. I know there exists command line tools. But it was not the way my team used it.

    +

    - I speak about ClearCase©. I know there exists command line tools. But it was not the way my team used it.

    -

    * - I never really given its chance to Mercurial. The terminology they chosen was too far from the svn one. And I was used to it.

    +

    * - I never really given its chance to Mercurial. The terminology they chosen was too far from the svn one. And I was used to it.

    @@ -113,7 +116,7 @@ But after trying it, I found it difficult and couter intuitive (I’ll speak
    -

    When you see explanation about branches and DCVS we imagine each branch is totally uncorrelated to each other, except when merging. Everything is magic. This is the “Parallel World” explanation. This way of seeing is explained more in depth in the real good article about branches on betterexplained.

    +

    When you see explanation about branches and DCVS we imagine each branch is totally uncorrelated to each other, except when merging. Everything is magic. This is the “Parallel World” explanation. This way of seeing is explained more in depth in the real good article about branches on betterexplained.

    Git was designed to manage the Linux Kernel. Git was designed using the concept of Patch instead of Parallel Worlds.

    @@ -124,11 +127,11 @@ But after trying it, I found it difficult and couter intuitive (I’ll speak
  • While Git seem base on the Patch model which will implie the creation of Parallel Worlds.
  • -

    I will not argument about which is the best. Just tell my vision of DCVS come from the Parallel World vision and Git was designed the other way.

    +

    I will not argument about which is the best. Just tell my vision of DCVS come from the Parallel World vision and Git was designed the other way.

    From Theory to Real Life Usage

    -

    I believe I understood conceptual mechanism under Git. But I had some difficulties with real usage. The worst point, the one I didn’t get before long was because I didn’t get really well the notion of Cheap Branching.

    +

    I believe I understood conceptual mechanism under Git. But I had some difficulties with real usage. The worst point, the one I didn’t get before long was because I didn’t get really well the notion of Cheap Branching.

    What is a Cheap Branch? If like me you come from Bazaar, it is a totally new notion. It is in fact the ability to create a branches all of them using the same directory.

    @@ -136,10 +139,10 @@ But after trying it, I found it difficult and couter intuitive (I’ll speak

    In theory, Cheap Branches are exactly like Bazaar branches. The word used is Branch and not Cheap Branch. But there is a slight difference between them. A slight difference between a Cloned Branch and a Cheap Branch.

    -

    A “Standard branch” is what is theoretically a kind of new Parallel World. +

    A “Standard branch” is what is theoretically a kind of new Parallel World. But Cheap branch was designed to be future Patch for the main branch of the directory/Cloned branch.

    -

    Of course, I know anybody can state you can totally use Cheap branches as Cloned branches. But they weren’t designed for that. On daily usage, it is a bit uneasy to use it like this.

    +

    Of course, I know anybody can state you can totally use Cheap branches as Cloned branches. But they weren’t designed for that. On daily usage, it is a bit uneasy to use it like this.

    Here how Git cheap branches should be used (for more details see Git for Designers):

    @@ -148,11 +151,11 @@ But Cheap branch was designed to be future Patch for the main
  • creation of a Cheap branch containing differences which have to be patched somewhere in the future into The Great Repository
  • -

    Here’s how you should not use Git:

    +

    Here’s how you should not use Git:

    • Get or creation of a repository
    • -
    • Create a cheap branch which will never push it’s modification to the main repository.
    • +
    • Create a cheap branch which will never push it’s modification to the main repository.

    This simple minor difference of point of view confused me a lot.

    @@ -170,23 +173,35 @@ And I return exactly in branches Bazaar provided me.

    For now, I prefer (from far) Bazaar terminology. They are cleaner and more understandable.

    -
    bzr revert
    -
    +
    + +
    bzr revert
    + +

    Is clearer than

    -
    git reset --hard HEAD
    -
    +
    + +
    git reset --hard HEAD
    + +

    We can tell the same thing about

    -
    bzr revert -r -3
    -
    +
    + +
    bzr revert -r -3
    + +

    which seems preferable to

    -
    git reset --hard HEAD~3
    -
    +
    + +
    git reset --hard HEAD~3
    + +

    Until now, it is not big business. But now, things will go worse. If we want to revert time on all the tree we use the keyword reset.

    @@ -195,34 +210,49 @@ If we want to revert time on all the tree we use the keyword reset.

    Now, if I want to revert time on on file. We should naturally imagine the command will be:

    -
    git reset --hard FILE
    -
    +
    + +
    git reset --hard FILE
    + +
    **OF COURSE NOT!**

    The solution is:

    -
    git checkout FILE
    -
    +
    + +
    git checkout FILE
    + +

    What? checkout !? Well, ok. I accept. why not? With Bazaar it is:

    -
    git revert FILE
    -
    +
    + +
    git revert FILE
    + +

    What I personally found far more natural.

    But the command to change the current cheap branch is really hard to be accepted (from the User Interface point of view). With Bazaar it is:

    -
    cd ../branch
    -
    +
    + +
    cd ../branch
    + +

    Well yes. With Bazaar you have to change your directory to change your branch. It needs more disk resources but it is really clear. Which is my current branch, is just a pwd away. For Git here is the command:

    -
    git checkout branch
    -
    +
    + +
    git checkout branch
    + +

    WTF? I believed checkout was the key to get a file in some state (not the entire tree).

    @@ -232,15 +262,15 @@ With Bazaar it is:

      -
    • — Try to find the good keyword for this operation
    • -
    • — Wrong! Try again!
    • -
    • — False, it is not yet right!
    • +
    • — Try to find the good keyword for this operation
    • +
    • — Wrong! Try again!
    • +
    • — False, it is not yet right!
    -

    That were the Git bad side. But It has many advantages. Once you’ve understood the cheap branching paradigm. All became clearer for me after. Even if there is also some difficulties with the edit of the .git/config files (not user friendly at all).

    +

    That were the Git bad side. But It has many advantages. Once you’ve understood the cheap branching paradigm. All became clearer for me after. Even if there is also some difficulties with the edit of the .git/config files (not user friendly at all).

    -

    I must precise that I worked a lot with multi-modal logic and particularly about “Temporal Logics” (linear or not). This is why I was more inclined to see things this way. “Ah ! Just to remember my firsts love with computer science !”

    +

    I must precise that I worked a lot with multi-modal logic and particularly about “Temporal Logics” (linear or not). This is why I was more inclined to see things this way. “Ah ! Just to remember my firsts love with computer science !”

    @@ -257,15 +287,15 @@ Such as working on a fix on a totally isolated branches.

    Is Git better than Bazaar?

    -

    Speaking about features I’ll tell Git is the best. -But Git was too much in my way. Is was exactly what I didn’t want for my first DCVS.

    +

    Speaking about features I’ll tell Git is the best. +But Git was too much in my way. Is was exactly what I didn’t want for my first DCVS.

    -

    I shouldn’t have had those difficulties about understanding cheap branching which must be a patch. In reality, Git make a difference between the Tree and the Branch. Which is obviously not the case for Bazaar. Conceptually, bazaar is simpler to understand.

    +

    I shouldn’t have had those difficulties about understanding cheap branching which must be a patch. In reality, Git make a difference between the Tree and the Branch. Which is obviously not the case for Bazaar. Conceptually, bazaar is simpler to understand.

    Finally

    In conclusion, I use Git more often than Bazaar and I must say, that I have some preferences for Git. However, Git lack hardly clear commands name like revert. -For now I don’t made alias to correct that. But may be one day I should do that.

    +For now I don’t made alias to correct that. But may be one day I should do that.

    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html index e9159933d..351efd320 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -70,34 +73,40 @@
    -

    Git is a DCVS, which means a Decentralized Concurrent Versions System. Let’s analyze each part of this long term:

    +

    Git is a DCVS, which means a Decentralized Concurrent Versions System. Let’s analyze each part of this long term:

    Versions System

    Firstly, versions system manage files. When somebody work with files without a versions system, the following happens frequently:

    -

    When you modify a somehow critical file you don’t want to loose. You copy naturally this file with another name. For example:

    - -
    $ cp fichier_important.c fichier_important.c.bak
    -
    - -

    In consequence of what, the new file, play the role of backup. If you break everything, you can always return in the last state by overwriting your modifications. -Of course, this method is not very professional and is a bit limited. If you make many modifications, you’ll end with many files with strange names like:

    +

    When you modify a somehow critical file you don’t want to loose. You copy naturally this file with another name. For example:

    -
    -fichier_important.c.bak
    +
    +
    $ cp fichier_important.c fichier_important.c.bak
    + +
    + +

    In consequence of what, the new file, play the role of backup. If you break everything, you can always return in the last state by overwriting your modifications. +Of course, this method is not very professional and is a bit limited. If you make many modifications, you’ll end with many files with strange names like:

    + +
    + + +
    fichier_important.c.bak
     fichier_important.c.old
     fichier_important.c.Bakcup
     fichier_important.c.BAK.2009-11-14
     fichier_important.c.2009.11.14
     fichier_important.c.12112009
     old.fichier_important.c
    -
    + + +
    -

    If you want to make it works correctly, you’ll have to use naming convention. Files take many place even if you modify most of time only some lines.

    +

    If you want to make it works correctly, you’ll have to use naming convention. Files take many place even if you modify most of time only some lines.

    Fortunately, versions system are here to help.

    @@ -105,13 +114,13 @@ old.fichier_important.c

    Once upon a time versions were managed for each file separately. I think about CVS. Then it naturally appears projects are a coherent set of files. Recover each file separately was a tedious work. This is why versions number passed from files to the entire project.

    -

    It is therefore possible to say, “I want to get back three days earlier”.

    +

    It is therefore possible to say, “I want to get back three days earlier”.

    -

    What gives versions system? (I didn’t mention everything at all)

    +

    What gives versions system? (I didn’t mention everything at all)

    • automatic backups: back in time,
    • @@ -128,14 +137,17 @@ old.fichier_important.c

      Version Systems are already useful to manage its own projects. They help to organize and resolve partially backup problems. I say partially because you have to backup your repository on a decent file system. But versions system are really interesting is on projects done by many people.

      -

      Let’s begin by an example, a two person project ; Alex and Beatrice. On a file containing a Lovecraft’s gods list:

      +

      Let’s begin by an example, a two person project ; Alex and Beatrice. On a file containing a Lovecraft’s gods list:

      -
      -Cthulhu
      +
      +
      +
      Cthulhu
       Shubniggurath
       Yogsototh
      -
      + + +

    Say Alex is home and modify the file: <div style="width: 10em; margin-left: auto; margin-right: auto"> @@ -187,7 +199,7 @@ Yogsototh

    @@ -199,7 +211,7 @@ Yogsototh

    This word became popular only recently about CVS. And it mainly means two things:

    -

    First, until really recently (SVN), you’ll have to be connected to the distant server to get informations about a project. Like get the history. New decentralized systems work with a local REPOSITORY (directory containing backups and many informations linked to the versions system functionalities). Hence, one can view the history of a project without the need of being connected.

    +

    First, until really recently (SVN), you’ll have to be connected to the distant server to get informations about a project. Like get the history. New decentralized systems work with a local REPOSITORY (directory containing backups and many informations linked to the versions system functionalities). Hence, one can view the history of a project without the need of being connected.

    All instances of a project can live independently.

    @@ -212,7 +224,7 @@ Yogsototh

    Typical example:

    -

    I develop my project. I’m ameliorating something. An urgent bug is reported.

    +

    I develop my project. I’m ameliorating something. An urgent bug is reported.

    With a DCVS I can easily, get back to the version with the bug. Fix it. Send the fix. Get back to my feature work. And even, use the fix for the new version with my new feature.

    @@ -237,7 +249,7 @@ Yogsototh

    To resume

    -

    Let’s resume what we can easily do with DCVS:

    +

    Let’s resume what we can easily do with DCVS:

    Versions Systems

    @@ -263,7 +275,7 @@ Yogsototh
  • Easily manipulate branches
  • -

    Now let’s see how to obtain all these things easily with Git.

    +

    Now let’s see how to obtain all these things easily with Git.

    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html index ded71871f..a363379a1 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -84,16 +87,19 @@

    Conflicts management

    -

    Conflicts can arise when you change the same line of code on the same file from another branch you’re merging. It can seems a bit intimidating, but with Git this kind of thing is really simple to handle.

    +

    Conflicts can arise when you change the same line of code on the same file from another branch you’re merging. It can seems a bit intimidating, but with Git this kind of thing is really simple to handle.

    example

    You start from the following file

    -
    -Zoot 
    -
    + + +
    Zoot 
    +
    + +

    and you modify one line

    @@ -115,17 +121,20 @@ Zoot, just Zoot

    Now when you do a:

    -
    -$ git pull
    -remote: Counting objects: 5, done.
    +
    +
    +
    $ git pull
    +remote: Counting objects: 5, done.
     remote: Total 3 (delta 0), reused 0 (delta 0)
    -Unpacking objects: 100% (3/3), done.
    +Unpacking objects: 100% (3/3), done.
     From /home/e640846/tmp/conflictTest
    -   d3ea395..2dc7ffb  master     -> origin/master
    +   d3ea395..2dc7ffb  master     -> origin/master
     Auto-merging foo
    -CONFLICT (content): Merge conflict in foo
    -Automatic merge failed; fix conflicts and then commit the result.
    -
    +CONFLICT (content): Merge conflict in foo +Automatic merge failed; fix conflicts and then commit the result. +
    + +

    Our file foo now contains:

    @@ -153,13 +162,16 @@ Zoot the not so pure

    and to commit

    -
    -git commit -a -m "conflict resolved"
    -
    + + +
    git commit -a -m "conflict resolved"
    +
    + +
    -

    Now you’re ready to use Git. -Git provide many other functionnalities. Now we’ll see some Git usages older CVS couldn’t handle.

    +

    Now you’re ready to use Git. +Git provide many other functionnalities. Now we’ll see some Git usages older CVS couldn’t handle.

    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html index 7d024927b..b3675c786 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -78,114 +81,165 @@

    get others modifications

    -
    -$ git pull
    -
    +
    + +
    $ git pull
    +
    + +

    send modifications to others

    -
    -$ git push
    -
    +
    + +
    $ git push
    +
    + +

    or more generally

    -
    -$ git pull
    +
    + +
    $ git pull
     $ git push
    -
    +
    + +

    get back in time

    For all tree

    -
    -$ git checkout
    -
    +
    -
    -$ git revert
    -
    +
    $ git checkout
    +
    + +
    + +
    + +
    $ git revert
    +
    + +

    revert three version before (see my .gitconfig file).

    -
    -$ git uncommit 3
    -
    +
    + +
    $ git uncommit 3
    +
    + +

    Undo the las merge (if something goes wrong)

    -
    -$ git revertbeforemerge
    -
    +
    + +
    $ git revertbeforemerge
    +
    + +

    For one file

    -
    -$ git checkout file
    +
    + +
    $ git checkout file
     $ git checkout VersionHash file
     $ git checkout HEAD~3 file
    -
    +
    + +

    list differences between each version

    list files being modified

    -
    -$ git status
    -
    +
    + +
    $ git status
    +
    + +

    differences between last version files and local files

    -
    -$ git diff
    -
    +
    + +
    $ git diff
    +
    + +

    differences between some version and local files

    -
    -$ git diff VersionHash fichier
    -
    +
    + +
    $ git diff VersionHash fichier
    +
    + +

    name some version to refer to them in the future

    -
    -$ git tag 'toto'
    -
    +
    + +
    $ git tag 'toto'
    +
    + +

    show historic of modifications

    -
    -$ git log
    +
    + +
    $ git log
     $ git lg
     $ git logfull
    -
    +
    + +

    know who did what and when

    -
    -$ git blame fichier
    -
    +
    + +
    $ git blame fichier
    +
    + +

    handle conflicts

    -
    -$ git conflict
    -
    +
    + +
    $ git conflict
    +
    + +

    manage branches

    To create a branch:

    -
    -$ git branch branch_name
    -
    +
    + +
    $ git branch branch_name
    +
    + +

    To change the current branch:

    -
    -$ git checkout branch_name
    -
    +
    + +
    $ git checkout branch_name
    +
    + +
    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/index.html index c969d5ccf..85415054d 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -70,9 +73,9 @@

    All was done to code and decide how to organize your versions after. In other VCS it is not as natural as in Git.

    -

    With Git you can depend of many different sources. Then, there is not necessarily a ‘master’ repository where everybody puts its modifications.

    +

    With Git you can depend of many different sources. Then, there is not necessarily a ‘master’ repository where everybody puts its modifications.

    -

    What changes the most with Git when you come from SVN, it’s the idea of a centralized project on one server. With Git many people could work on the same project but not necessarily on the same repository as main reference. One can easily fix a bug and send a patch to many different versions of a project.

    +

    What changes the most with Git when you come from SVN, it’s the idea of a centralized project on one server. With Git many people could work on the same project but not necessarily on the same repository as main reference. One can easily fix a bug and send a patch to many different versions of a project.

    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig index 25bcf85ce..828ac4d13 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig @@ -1,4 +1,3 @@ - [color] branch = auto diff = auto diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html index 9ff0d761d..2581c0037 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -64,8 +67,11 @@

    Under Linux Ubuntu or Debian:

    -
    $ sudo apt-get install git
    -
    +
    + +
    $ sudo apt-get install git
    + +

    Under Mac OS X:

    @@ -74,19 +80,23 @@
  • install Git
  • -
    -$ sudo port selfupdate
    +
    + +
    $ sudo port selfupdate
     
     $ sudo port install git-core
    -
    +
    + +

    Global configuration

    Save the following file as your ~/.gitconfig.

    -
    -
    -[color]
    +
    + + +
    [color]
         branch = auto
         diff   = auto
         status = auto
    @@ -97,24 +107,28 @@ $ sudo port install git-core
         lg        = log --pretty=oneline --graph
         logfull   = log --pretty=fuller --graph --stat -p
         unstage   = reset HEAD
    -    # there should be an article on what this command do
    -    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    +    # there should be an article on what this command do
    +    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
         undomerge = reset --hard ORIG_HEAD
    -	conflict  = !gitk --left-right HEAD...MERGE_HEAD
    -    # under Mac OS X, you should use gitx instead
    -	# conflict    = !gitx --left-right HEAD...MERGE_HEAD
    +	conflict  = !gitk --left-right HEAD...MERGE_HEAD
    +    # under Mac OS X, you should use gitx instead
    +	# conflict    = !gitx --left-right HEAD...MERGE_HEAD
     [branch]
     	autosetupmerge = true
    -
    -
    + + +

    You can achieve the same result using for each entry the command: git config --global. Next, configure your name and your email. For example, if your name is John Doe and your email is john.doe@email.com. Launch the following commands:

    -
    -$ git config --global user.name John Doe
    +
    + +
    $ git config --global user.name John Doe
     
     $ git config --global user.email john.doe@email.com
    -
    +
    + +

    Here it is. Base configuration is over. The file containing alias will help to type shorter commands.

    @@ -122,69 +136,93 @@ $ git config --global user.email john.doe@email.com

    If a project is already versionned with Git you should have an URL of the sources. Then use the following command:

    -
    -$ cd ~/Projets
    +
    + +
    $ cd ~/Projets
     $ git clone git://main.server/path/to/file
    -
    +
    -

    If there is no git server but you’ve got an ssh access. Just replace the git://host by ssh://user@host. In order not to type your password each time, use:

    +
    -
    -$ ssh-keygen -t rsa
    -
    +

    If there is no git server but you’ve got an ssh access. Just replace the git://host by ssh://user@host. In order not to type your password each time, use:

    + +
    + +
    $ ssh-keygen -t rsa
    +
    + +

    Reply to question and *do not enter a password. Then copy your keys to the distant server. This is not the safest way to do this. The safest being, using ssh-agent.

    The easiest way if you have ssh-copy-id:

    -
    -me@locahost$ ssh-copy-id ~/.ssh/id_rsa.pub me@main.server
    -
    +
    + +
    me@locahost$ ssh-copy-id ~/.ssh/id_rsa.pub me@main.server
    +
    + +

    or manually

    -
    -me@locahost$ scp ~/.ssh/id_rsa.pub me@main.server:
    +
    + +
    me@locahost$ scp ~/.ssh/id_rsa.pub me@main.server:
     me@locahost$ ssh me@main.server
     password:
    -me@main.server$ cat id_rsa.pub >> ~/.ssh/authorized_keys
    +me@main.server$ cat id_rsa.pub >> ~/.ssh/authorized_keys
     me@main.server$ rm id_rsa.pub
     me@main.server$ logout
    -
    +
    -

    Now you don’t need to write your password to access the main.server.

    +
    + +

    Now you don’t need to write your password to access the main.server.

    Creating a new project

    Suppose you already have a project with files. Then it is really easy to version it.

    -
    -$ cd /path/to/project
    +
    + +
    $ cd /path/to/project
     $ git init
     $ git add .
    -$ git commit -m "Initial commit"
    -
    +$ git commit -m "Initial commit" +
    -

    Let do a small remark. If you don’t want to version every file. Typically intermediate compilation file, swap files… Then you need to exclude them. Just before launching the git add . command. You need to create a .gitignore file in the root directory of your project. This file will contain all exclude pattern. For example:

    +
    -
    -*.o
    +

    Let do a small remark. If you don’t want to version every file. Typically intermediate compilation file, swap files… Then you need to exclude them. Just before launching the git add . command. You need to create a .gitignore file in the root directory of your project. This file will contain all exclude pattern. For example:

    + +
    + +
    *.o
     *.bak
     *.swp
     *~
    -
    +
    + +

    Now, if you want to create a repository on a distant server, it must not be in bare mode. The repository will contain only versionning informations, but not the files of the project. To achieve that:

    -
    -$ cd /path/to/local/project
    +
    + +
    $ cd /path/to/local/project
     $ git clone --bare . ssh://server/path/to/project
    -
    +
    + +

    Others will be able to get your modifications.

    -
    -git clone ssh://server/path/to/project
    -
    +
    + +
    git clone ssh://server/path/to/project
    +
    + +

    Abstract of the second step

    diff --git a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/index.html b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/index.html index 4c52fb267..da52c91b2 100644 --- a/output/Scratch/en/blog/2009-11-12-Git-for-n00b/index.html +++ b/output/Scratch/en/blog/2009-11-12-Git-for-n00b/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,7 +65,7 @@
    -

    A detailed tutorial of Git for people knowing very few about versions systems. You’ll understand utility of such program and how we use modern version control system. I try to stay as pragmatic as possible.

    +

    A detailed tutorial of Git for people knowing very few about versions systems. You’ll understand utility of such program and how we use modern version control system. I try to stay as pragmatic as possible.

    @@ -80,37 +83,43 @@

    Get a project from the web:

    -
    -git clone ssh://server/path/to/project
    -
    +
    + +
    git clone ssh://server/path/to/project
    +
    + +

    Everyday Git usage:

    -
    -# get modifications from other
    +
    + +
    # get modifications from other
     git pull
    -# read what was done
    +# read what was done
     git log
     
    -# Make local changes to files 
    +# Make local changes to files 
     hack, hack, hack...
    -# list the modified files
    +# list the modified files
     git status
    -# show what I've done
    +# show what I've done
     git diff
     
    -# tell git to version a new file
    +# tell git to version a new file
     git add new/file
     
    -# commit its own modifications 
    -# to its local branch
    -git commit -a -m "Fix bug #321"
    +# commit its own modifications 
    +# to its local branch
    +git commit -a -m "Fix bug #321"
     
    -# send local modifications to other
    +# send local modifications to other
     git push
    -
    +
    -

    This article is written for people knowing very few about versionning systems. It is also written for those who had didn’t followed progress since CVS or subversion (SVN). This is why, in a first time I’ll explain quickly which are the goal of such systems. Secondly, I’ll explain how to install and configure Git. Then, I give the command for each feature a DCVS must have.

    +
    + +

    This article is written for people knowing very few about versionning systems. It is also written for those who had didn’t followed progress since CVS or subversion (SVN). This is why, in a first time I’ll explain quickly which are the goal of such systems. Secondly, I’ll explain how to install and configure Git. Then, I give the command for each feature a DCVS must have.

    diff --git a/output/Scratch/en/blog/2009-12-06-iphone-call-filter/index.html b/output/Scratch/en/blog/2009-12-06-iphone-call-filter/index.html index f12b39b49..a47bc30e6 100644 --- a/output/Scratch/en/blog/2009-12-06-iphone-call-filter/index.html +++ b/output/Scratch/en/blog/2009-12-06-iphone-call-filter/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,7 +59,7 @@

    It is unbelievable you cannot filter your call with an iPhone! The only reason I see for that is a negotiation with phone operator to force users to get phone advertising. It is simple unacceptable.

    -

    I’m a λ iPhone’s user. The only way to filter your call and to manage blacklist is to jailbreak your iPhone. And I don’t want to do that. Then, if like me you find it unacceptable, just write a line to Apple: http://www.apple.com/feedback/iphone.html

    +

    I’m a λ iPhone’s user. The only way to filter your call and to manage blacklist is to jailbreak your iPhone. And I don’t want to do that. Then, if like me you find it unacceptable, just write a line to Apple: http://www.apple.com/feedback/iphone.html

    diff --git a/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/code/gitconfig b/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/code/gitconfig index 85a89236f..78a9edd90 100644 --- a/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/code/gitconfig +++ b/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/code/gitconfig @@ -1,3 +1,2 @@ - [alias] uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""' diff --git a/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/index.html b/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/index.html index 6dd3b096b..f10ebb01e 100644 --- a/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/index.html +++ b/output/Scratch/en/blog/2009-12-14-Git-vs--Bzr/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,7 +65,7 @@
    -

    Why even if I believe git has many bad point I believe it is the best DCVS around to work with. This is why I first tell why I prefer Bazaar over Git. Secondly I’ll talk about the only advantage of git against Bazaar which lead me to prefer it.

    +

    Why even if I believe git has many bad point I believe it is the best DCVS around to work with. This is why I first tell why I prefer Bazaar over Git. Secondly I’ll talk about the only advantage of git against Bazaar which lead me to prefer it.

    @@ -74,7 +77,7 @@

    There is two way of perceive version control system. Either you think in term of branches (see the really good article on betterexplained) or think in term of patches. Another way to say that, is weather you concentrate on vertices or on transitions of the graph of possible states of your project.

    -

    This is the second approach who was behind git and this is the first behind Bazaar. git was created by Linus Torvald in order to close some gap in the version system used to develop the Linux kernel. And patches is a term which is more present than ‘state’ in the development community.

    +

    This is the second approach who was behind git and this is the first behind Bazaar. git was created by Linus Torvald in order to close some gap in the version system used to develop the Linux kernel. And patches is a term which is more present than ‘state’ in the development community.

    I first was convinced by Bazaar. Why? Argument in favor of Bazaar were: user friendly, terminology close to the subversion one. And I tried a bit the two, and it was clearly more natural for me to use Bazaar. But after seeing so many people using git I decided to give it a serious try.

    @@ -84,45 +87,63 @@

    The first example, checkout is used to make only one thing from the technical point of vue. But from the user perspective, you make many different things with this word. Example:

    -
    -git checkout pipo
    -
    +
    + +
    git checkout pipo
    +
    + +

    undo the current modification of the file pipo

    -
    -git checkout pipo
    -
    +
    + +
    git checkout pipo
    +
    + +

    change the current branch to the branch pipo

    And, like me, you remark, it is exactly the same command to make two completely different things. What occur when you have a pipo branch and a pipo file? By default, it change the current branch. In order to leave the ambiguity you have to use the following syntax:

    -
    -git checkout ./pipo
    -
    +
    -

    Yes, hum…

    +
    git checkout ./pipo
    +
    + +
    + +

    Yes, hum…

    It works, but it is clearly not really user friendly. Furthermore, checkout had a complete different signification in older CSV like cvs et svn. checkout was used to get a distant project locally.

    -

    Bazaar terminology is far more natural, because there is no command to change the current branch as there is only one branch per directory. Changing a branch in Bazaar is changing the current directory. I also believe it is the biggest problem of Bazaar, I’ll tell you why. And to undo things in Bazaar:

    +

    Bazaar terminology is far more natural, because there is no command to change the current branch as there is only one branch per directory. Changing a branch in Bazaar is changing the current directory. I also believe it is the biggest problem of Bazaar, I’ll tell you why. And to undo things in Bazaar:

    -
    -bzr revert pipo
    -
    +
    + +
    bzr revert pipo
    +
    + +

    Furthermore, most Bazaar command take a revision number in parameter. For example, to get back 3 versions earlier, it is enough to write:

    -
    -bzr revert -r -3 pipo
    -
    +
    + +
    bzr revert -r -3 pipo
    +
    + +

    The git equivalent is far more cryptic:

    -
    -bzr checkout HEAD~3 pipo
    -
    +
    + +
    bzr checkout HEAD~3 pipo
    +
    + +

    One more time, Bazaar is far more readable.

    @@ -130,65 +151,82 @@ bzr checkout HEAD~3 pipo

    with Bazaar:

    -
    -bzr revert -r -3 pipo
    -
    +
    + +
    bzr revert -r -3 pipo
    +
    + +

    and with git? git checkout? Of course not! It would be too simple. What we find in the documentation (man) and everywhere on the net:

    -
    -git reset --hard HEAD~3
    -
    +
    + +
    git reset --hard HEAD~3
    +
    + +

    Except that this command is horrible. It forget revisions! Then you must use it with prudence. And you cannot tell other people working on the project you discard some changes. If someone had pulled the bad version, you are doomed. This is why you can also use:

    -
    -git checkout HEAD~3 -- . && git commit -m 'back in time'
    -
    +
    + +
    git checkout HEAD~3 -- . && git commit -m 'back in time'
    +
    + +

    Just to keep a backup branch. Without it we can definitively loose the current version HEAD. But some error may rest when there were some addition and deletion of files. The unique way to be really clean without any risk is to use the following command:

    -
    -for i in $(seq 0 2); do 
    -    git revert -n --no-edit head~$i; 
    -done
    -git commit -m "reverted 3 versions back"
    -
    +
    + +
    for i in $(seq 0 2); do 
    +    git revert -n --no-edit head~$i; 
    +done
    +git commit -m "reverted 3 versions back"
    +
    + +

    And with this command this is the only good way to undo things in a project and tell other contributor you reverted something. You simply revert version in backward order.

    The rule is simple: NEVER use the git reset command on a version somebody else could have fetched

    -

    It was said. Discover the best method took me some time. I’d made many different tries. The safer and best way of reverting back your tree is to use this method. If you want to make it automatic just had the following alias in your ~/.gitconfig. Of course this alias will work only on environment having zsh installed. Which is the cas for most UNIX (Ubuntu, Mac OS X…).

    +

    It was said. Discover the best method took me some time. I’d made many different tries. The safer and best way of reverting back your tree is to use this method. If you want to make it automatic just had the following alias in your ~/.gitconfig. Of course this alias will work only on environment having zsh installed. Which is the cas for most UNIX (Ubuntu, Mac OS X…).

    -
    -
    -[alias]
    -    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    -
    -
    +
    + + +
    [alias]
    +    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    +
    + +

    What make git by far the best DCVS today

    -

    After talking about the negatives points of git, now it’s time to speak about the very positive feature that make git the best DCVS in my humble opinion.

    +

    After talking about the negatives points of git, now it’s time to speak about the very positive feature that make git the best DCVS in my humble opinion.

    Cheap branching

    You always work into the same main directory. For example, you can work on two fix in the same time. Say fix1 require you to work on file1 and fix2 to work on file2. You can work in any order on file1 and file2 in the master branch. And then go to branch fix1, commit file1 into it. Then go to branch fix2 and commit file2 into it. And finally merge the two branches fix1 and fix2 into master.

    -
    -> vim file1
    -> vim file2
    -> git br fix1
    -> git add file1 
    -> git commit -m 'fix1'
    -> git br fix2
    -> git add file2
    -> git commit -m 'fix2'
    -> git commit master
    -> git merge fix1
    -> git merge fix2
    -
    +
    + +
    > vim file1
    +> vim file2
    +> git br fix1
    +> git add file1 
    +> git commit -m 'fix1'
    +> git br fix2
    +> git add file2
    +> git commit -m 'fix2'
    +> git commit master
    +> git merge fix1
    +> git merge fix2
    +
    + +

    And this is great not to worry about working in the good branch and coding in the same time. You just worry about your code and then about the versionning system.

    diff --git a/output/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html b/output/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html index 7bb644d15..4edf63c6d 100644 --- a/output/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html +++ b/output/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,9 +59,12 @@

    I just found a way to change the default shell on Mac OS X. This note is mostly for me, but somebody else should find it useful. Just launch the following command:

    -
    -> chsh
    -
    +
    + +
    > chsh
    +
    + +
    diff --git a/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf b/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf index cbbcb2c5e..f1a5dfc6d 100644 --- a/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf +++ b/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf @@ -1,5 +1,4 @@ - diff --git a/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html b/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html index de6afdc99..d4caca7ef 100644 --- a/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html +++ b/output/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,64 +62,66 @@

    Just modify the /etc/fonts/local.conf with the following code:

    -
    -
    +
     
    -<?xml version="1.0"?>
    -<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
    -<fontconfig>
     
    -<!-- Miscellaneous settings -->
    +
    
    +<?xml version="1.0"?>
    +<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
    +<fontconfig>
     
    -<include ignore_missing="yes">misc.conf</include>
    +<!-- Miscellaneous settings -->
     
    -<!-- Define alias -->
    +<include ignore_missing="yes">misc.conf</include>
     
    -<include ignore_missing="yes">alias.conf</include>
    +<!-- Define alias -->
     
    -<!-- Rules for Microsoft fonts -->
    +<include ignore_missing="yes">alias.conf</include>
     
    -<include ignore_missing="yes">msfonts-rules.conf</include>
    +<!-- Rules for Microsoft fonts -->
     
    -  <match target="pattern" name="family" >
    -      <test name="family" qual="any" >
    -          <string>Tahoma</string>
    -      </test>
    -      <edit mode="assign" name="family" >
    -          <string>Verdana</string>
    -      </edit>
    -  </match>
    -  <selectfont>
    -      <acceptfont>
    -          <pattern>
    -              <patelt name="family"> 
    -                <string>Lucida Grande</string> 
    -              </patelt>
    -          </pattern>
    -      </acceptfont>
    -  </selectfont>
    +<include ignore_missing="yes">msfonts-rules.conf</include>
    +
    +  <match target="pattern" name="family" >
    +      <test name="family" qual="any" >
    +          <string>Tahoma</string>
    +      </test>
    +      <edit mode="assign" name="family" >
    +          <string>Verdana</string>
    +      </edit>
    +  </match>
    +  <selectfont>
    +      <acceptfont>
    +          <pattern>
    +              <patelt name="family"> 
    +                <string>Lucida Grande</string> 
    +              </patelt>
    +          </pattern>
    +      </acceptfont>
    +  </selectfont>
    +
    +  <match target="pattern" name="family" >
    +      <test name="family" qual="any" >
    +          <string>Georgia</string>
    +      </test>
    +      <edit mode="assign" name="family" >
    +          <string>Georgia</string>
    +      </edit>
    +  </match>
    +  <selectfont>
    +      <acceptfont>
    +          <pattern>
    +              <patelt name="family"> 
    +                <string>Century Schoolbook L</string> 
    +              </patelt>
    +          </pattern>
    +      </acceptfont>
    +  </selectfont>
    +
    +</fontconfig>
    +
    - <match target="pattern" name="family" > - <test name="family" qual="any" > - <string>Georgia</string> - </test> - <edit mode="assign" name="family" > - <string>Georgia</string> - </edit> - </match> - <selectfont> - <acceptfont> - <pattern> - <patelt name="family"> - <string>Century Schoolbook L</string> - </patelt> - </pattern> - </acceptfont> - </selectfont> -</fontconfig> -
    -

    Hope it helped someone who like me had his eyes crying in face of such ugly fonts.

    diff --git a/output/Scratch/en/blog/2010-02-15-All-but-something-regexp/index.html b/output/Scratch/en/blog/2010-02-15-All-but-something-regexp/index.html index c999196b6..c26d117e4 100644 --- a/output/Scratch/en/blog/2010-02-15-All-but-something-regexp/index.html +++ b/output/Scratch/en/blog/2010-02-15-All-but-something-regexp/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,31 +59,37 @@

    Sometimes you cannot simply write:

    -
    -if str.match(regexp) and 
    -    not str.match(other_regexp)
    +
    + +
    if str.match(regexp) and 
    +    not str.match(other_regexp)
             do_something
    -
    +
    + +

    and you have to make this behaviour with only one regular expression. But, there exists a major problem: the complementary of a regular language might not be regular. Then, for some expression it is absolutely impossible to negate a regular expression.

    -

    But sometimes with some simple regular expression it should be possible. Say you want to match everything containing the some word say bull but don’t want to match bullshit. Here is a nice way to do that:

    +

    But sometimes with some simple regular expression it should be possible. Say you want to match everything containing the some word say bull but don’t want to match bullshit. Here is a nice way to do that:

    -
    -# match all string containing 'bull' (bullshit comprised)
    -/bull/
    +
    -# match all string containing 'bull' except 'bullshit' -/bull([^s]|$)| -bulls([^h]|$)| -bullsh([^i]|$)| -bullshi([^t]|$)/ +
    # match all string containing 'bull' (bullshit comprised)
    +/bull/
     
    -# another way to write it would be
    -/bull([^s]|$|s([^h]|$)|sh([^i]|$)|shi([^t]|$))/
    -
    +# match all string containing 'bull' except 'bullshit' +/bull([^s]|$)| +bulls([^h]|$)| +bullsh([^i]|$)| +bullshi([^t]|$)/ + +# another way to write it would be +/bull([^s]|$|s([^h]|$)|sh([^i]|$)|shi([^t]|$))/ +
    + +

    Let look closer. In the first line the expression is: bull([^s]|$), why does the $ is needed? @@ -95,34 +104,40 @@ contains bull followed by a letter different from s. <

    And this is it. I hope it could help you.

    Notice this method is not always the best. For example try to write a regular expression equivalent to the following conditional expression:

    -
    -# Begin with 'a': ^a
    -# End with 'a': c$
    -# Contain 'b': .*b.*
    -# But isn't 'axbxc'
    -if str.match(/^a.*b.*c$/) and 
    -        not str.match(/^axbxc$/)
    +
    + +
    # Begin with 'a': ^a
    +# End with 'a': c$
    +# Contain 'b': .*b.*
    +# But isn't 'axbxc'
    +if str.match(/^a.*b.*c$/) and 
    +        not str.match(/^axbxc$/)
         do_something
    -end
    -
    +end +
    + +

    A nice solution is:

    -
    -/abc|           # length 3
    -a.bc|           # length 4
    -ab.c|
    -a[^x]b[^x]c|    # length 5
    -a...*b.*c|      # length >5
    -a.*b...*c/
    -
    +
    + +
    /abc|           # length 3
    +a.bc|           # length 4
    +ab.c|
    +a[^x]b[^x]c|    # length 5
    +a...*b.*c|      # length >5
    +a.*b...*c/
    +
    + +

    This solution uses the maximal length of the string not to be matched. There certainly exists many other methods. But the important lesson is it is not straightforward to exclude something of a regular expression.


    -

    +

    It can be proved that any regular set minus a finite set is also regular.

    diff --git a/output/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/index.html b/output/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/index.html index 7d9280717..7d03625dd 100644 --- a/output/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/index.html +++ b/output/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,7 +57,7 @@
    -

    In my previous post I had given some trick to match all except something. On the same idea, the trick to match the smallest possible string. Say you want to match the string between ‘a’ and ‘b’, for example, you want to match:

    +

    In my previous post I had given some trick to match all except something. On the same idea, the trick to match the smallest possible string. Say you want to match the string between ‘a’ and ‘b’, for example, you want to match:

     a.....a......b..b..a....a....b...
    @@ -75,7 +78,7 @@ a.....a......b..b..a....
     
     

    The next natural way, is to change the greediness. But it is not enough as you will match from the first a to the first b. -Then a simple constatation is that our matching string shouldn’t contain any a nor b. Which lead to the last elegant solution.

    +Then a simple constatation is that our matching string shouldn’t contain any a nor b. Which lead to the last elegant solution.

     /a[^ab]*b/
    @@ -85,64 +88,82 @@ a.....a......b..b..a....Until now, that was, easy. 
     Now, just pass at the case you need to match not between a and b, but between strings.
     For example:

    -
    -<li>...<li>
    -
    +
    + +
    <li>...<li>
    +
    + +

    This is a bit difficult. You need to match

    -
    -<li>[anything not containing <li>]</li>
    -
    +
    + +
    <li>[anything not containing <li>]</li>
    +
    + +

    The first method would be to use the same reasoning as in my previous post. Here is a first try:

    -
    -<li>([^<]|<[^l]|<l[^i]|<li[^>])*</li>
    -
    +
    + +
    <li>([^<]|<[^l]|<l[^i]|<li[^>])*</li>
    +
    + +

    But what about the following string:

    -
    -<li>...<li</li>
    -
    +
    -

    That string should not match. This is why if we really want to match it correctly we need to add:

    -
    -<li>([^<]|<[^l]|<l[^i]|<li[^>])*(|<|<l|<li)</li>
    -
    +
    <li>...<li</li>
    +
    + +
    + +

    That string should not match. This is why if we really want to match it correctly we need to add:

    +
    + +
    <li>([^<]|<[^l]|<l[^i]|<li[^>])*(|<|<l|<li)</li>
    +
    + +

    Yes a bit complicated. But what if the string I wanted to match was even longer?

    Here is the algorithm way to handle this easily. You reduce the problem to the first one letter matching:

    -
    -# transform a simple randomly choosen character
    -# to an unique ID 
    -# (you should verify the identifier is REALLY unique)
    -# beware the unique ID must not contain the 
    -# choosen character
    -s/X/_was_x_/g
    -s/Y/_was_y_/g
    +
    -# transform the long string in this simple character -s/<li>/X/g -s/<\/li>/Y/g +
    # transform a simple randomly choosen character
    +# to an unique ID 
    +# (you should verify the identifier is REALLY unique)
    +# beware the unique ID must not contain the 
    +# choosen character
    +s/X/_was_x_/g
    +s/Y/_was_y_/g
     
    -# use the first method
    -s/X([^X]*)Y//g
    +# transform the long string in this simple character
    +s/<li>/X/g
    +s/<\/li>/Y/g
     
    -# retransform choosen letter by string
    -s/X/<li>/g
    -s/Y/<\/li>/g
    +# use the first method
    +s/X([^X]*)Y//g
     
    -# retransform the choosen character back
    -s/_was_x_/X/g
    -s/_was_y_/Y/g
    -
    +# retransform choosen letter by string +s/X/<li>/g +s/Y/<\/li>/g + +# retransform the choosen character back +s/_was_x_/X/g +s/_was_y_/Y/g +
    + +

    And it works in only 9 lines for any beginning and ending string. This solution should look less I AM THE GREAT REGEXP M45T3R, URAN00B, but is more convenient in my humble opinion. Further more, using this last solution prove you master regexp, because you know it is difficult to manage such problems with only a regexp.


    -

    I know I used an HTML syntax example, but in my real life usage, I needed to match between en: and ::. And sometimes the string could finish with e::.

    +

    I know I used an HTML syntax example, but in my real life usage, I needed to match between en: and ::. And sometimes the string could finish with e::.

    diff --git a/output/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/index.html b/output/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/index.html index 353a8f221..fdd11a78a 100644 --- a/output/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/index.html +++ b/output/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,18 +57,21 @@
    -

    Strangely enough, I didn’t find any built-in tool to split a file by keyword. I made one myself in awk. I put it here mostly for myself. But it could also helps someone else. +

    Strangely enough, I didn’t find any built-in tool to split a file by keyword. I made one myself in awk. I put it here mostly for myself. But it could also helps someone else. The following code split a file for each line containing the word UTC.

    -
    -#!/usr/bin/env awk
    -BEGIN{i=0;}
    -/UTC/ { 
    +
    + +
    #!/usr/bin/env awk
    +BEGIN{i=0;}
    +/UTC/ { 
         i+=1;
    -    FIC=sprintf("fic.%03d",i); 
    +    FIC=sprintf("fic.%03d",i); 
     } 
    -{print $0>>FIC}
    -
    +{print $0>>FIC} +
    + +

    In my real world example, I wanted one file per day, each line containing UTC being in the following format:

    @@ -75,19 +81,22 @@ Mon Dec 7 10:32:30 UTC 2009

    I then finished with the following code:

    -
    -#!/usr/bin/env awk
    -BEGIN{i=0;}
    -/UTC/ {
    -    date=$1$2$3; 
    -    if ( date != olddate ) {
    +
    + +
    #!/usr/bin/env awk
    +BEGIN{i=0;}
    +/UTC/ {
    +    date=$1$2$3; 
    +    if ( date != olddate ) {
             olddate=date;
             i+=1;
    -        FIC=sprintf("fic.%03d",i); 
    +        FIC=sprintf("fic.%03d",i); 
         }
     } 
    -{print $0>>FIC}
    -
    +{print $0>>FIC} +
    + +
    diff --git a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb index 33e9e5580..1770c6aa5 100644 --- a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb +++ b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby require 'benchmark' n=80000 diff --git a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb index f99b07a97..e41a21361 100644 --- a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb +++ b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby require 'benchmark' n=80000 diff --git a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html index a83914ad1..946ba6673 100644 --- a/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html +++ b/output/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,50 +62,55 @@ Particularly when transformations you want to make are easy.

    I wanted to know how to get file extension from filename the fastest way possible. There is 3 natural way of doing this:

    -
    -# regexp
    -str.match(/[^.]*$/); 
    -ext=$&
    +
    -# split -ext=str.split('.')[-1] +
    # regexp
    +str.match(/[^.]*$/); 
    +ext=$&
     
    -# File module
    -ext=File.extname(str)
    -
    +# split +ext=str.split('.')[-1] + +# File module +ext=File.extname(str) +
    + +

    At first sight I believed that the regexp should be faster than the split because it could be many . in a filename. But in reality, most of time there is only one dot and I realized the split will be faster. But not the fastest way. There is a function dedicated to this work in the File module.

    Here is the Benchmark ruby code:

    -
    -
    -#!/usr/bin/env ruby
    -require 'benchmark'
    -n=80000
    -tab=[ '/accounts/user.json',
    -      '/accounts/user.xml',
    -      '/user/titi/blog/toto.json',
    -      '/user/titi/blog/toto.xml' ]
    +
    -puts "Get extname" -Benchmark.bm do |x| - x.report("regexp:") { n.times do - str=tab[rand(4)]; - str.match(/[^.]*$/); - ext=$&; - end } - x.report(" split:") { n.times do - str=tab[rand(4)]; - ext=str.split('.')[-1] ; - end } - x.report(" File:") { n.times do - str=tab[rand(4)]; - ext=File.extname(str); - end } -end -
    -
    + +
    #!/usr/bin/env ruby
    +require 'benchmark'
    +n=80000
    +tab=[ '/accounts/user.json',
    +      '/accounts/user.xml',
    +      '/user/titi/blog/toto.json',
    +      '/user/titi/blog/toto.xml' ]
    +
    +puts "Get extname"
    +Benchmark.bm do |x|
    +    x.report("regexp:") { n.times do 
    +        str=tab[rand(4)]; 
    +        str.match(/[^.]*$/); 
    +        ext=$&; 
    +    end  }
    +    x.report(" split:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=str.split('.')[-1] ; 
    +    end }
    +    x.report("  File:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=File.extname(str); 
    +    end  }
    +end
    +
    + +

    And here is the result

    @@ -118,30 +126,32 @@ regexp: 2.550000 0.020000 2.570000 ( 2.693407)

    file path without the extension.

    -
    -
    -#!/usr/bin/env ruby
    -require 'benchmark'
    -n=80000
    -tab=[ '/accounts/user.json',
    -      '/accounts/user.xml',
    -      '/user/titi/blog/toto.json',
    -      '/user/titi/blog/toto.xml' ]
    +
    -puts "remove extension" -Benchmark.bm do |x| - x.report(" File:") { n.times do - str=tab[rand(4)]; - path=File.expand_path(str,File.basename(str,File.extname(str))); - end } - x.report("chomp:") { n.times do - str=tab[rand(4)]; - ext=File.extname(str); - path=str.chomp(ext); - end } -end -
    -
    + +
    #!/usr/bin/env ruby
    +require 'benchmark'
    +n=80000
    +tab=[ '/accounts/user.json',
    +      '/accounts/user.xml',
    +      '/user/titi/blog/toto.json',
    +      '/user/titi/blog/toto.xml' ]
    +
    +puts "remove extension"
    +Benchmark.bm do |x|
    +    x.report(" File:") { n.times do 
    +        str=tab[rand(4)]; 
    +        path=File.expand_path(str,File.basename(str,File.extname(str))); 
    +    end }
    +    x.report("chomp:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=File.extname(str); 
    +        path=str.chomp(ext); 
    +    end }
    +end
    +
    + +

    and here is the result:

    diff --git a/output/Scratch/en/blog/2010-03-22-Git-Tips/index.html b/output/Scratch/en/blog/2010-03-22-Git-Tips/index.html index 2a3f1b8e2..7b787ef22 100644 --- a/output/Scratch/en/blog/2010-03-22-Git-Tips/index.html +++ b/output/Scratch/en/blog/2010-03-22-Git-Tips/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,60 +61,75 @@

    Standard:

    -
    -git clone git@github.com:yogsototh/project.git
    -
    +
    + +
    git clone git@github.com:yogsototh/project.git
    +
    + +

    Using HTTPS port:

    -
    -git clone git+ssh://git@github.com:443/yogsototh/project.git
    -
    +
    + +
    git clone git+ssh://git@github.com:443/yogsototh/project.git
    +
    + +

    clone all branches

    git clone can only fetch the master branch.

    -

    If you don’t have much branches, you can simply use clone your project and then use the following command:

    +

    If you don’t have much branches, you can simply use clone your project and then use the following command:

    -
    -git branch --track local_branch remote_branch
    -
    +
    + +
    git branch --track local_branch remote_branch
    +
    + +

    for example:

    -
    -$ git clone git@github:yogsototh/example.git
    +
    + +
    $ git clone git@github:yogsototh/example.git
     $ git branch
     master *
     $ git branch -a
     master *
    -remotes/origin/HEAD -> origin/master
    +remotes/origin/HEAD -> origin/master
     remotes/origin/experimental
     $ git branch --track experimental remotes/origin/experimental
     $ git branch
     master *
     experimental
    -
    +
    + +

    If you have many branches it can be useful to use the following script/long command line.

    -
    -# first clone your project
    +
    + +
    # first clone your project
     $ git clone git@github.com:yogsototh/project.git
     
    -# copy all branches
    +# copy all branches
     $ zsh
     $ cd project
    -$ for br in $( git br -a ); do
    -    case $br in
    -    remotes/*) print $br ; 
    -        case ${br:t} in
    -            master|HEAD) continue ;;
    -            *) git branch --track ${br:t} $br ;;
    -        esac ;;
    -    esac
    -done
    -
    +$ for br in $( git br -a ); do + case $br in + remotes/*) print $br ; + case ${br:t} in + master|HEAD) continue ;; + *) git branch --track ${br:t} $br ;; + esac ;; + esac +done +
    + +
    diff --git a/output/Scratch/en/blog/2010-03-23-Encapsulate-git/code/eng b/output/Scratch/en/blog/2010-03-23-Encapsulate-git/code/eng index e46a24787..8dc739649 100644 --- a/output/Scratch/en/blog/2010-03-23-Encapsulate-git/code/eng +++ b/output/Scratch/en/blog/2010-03-23-Encapsulate-git/code/eng @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby # encoding: utf-8 diff --git a/output/Scratch/en/blog/2010-03-23-Encapsulate-git/index.html b/output/Scratch/en/blog/2010-03-23-Encapsulate-git/index.html index 581ab96b1..2abf46f92 100644 --- a/output/Scratch/en/blog/2010-03-23-Encapsulate-git/index.html +++ b/output/Scratch/en/blog/2010-03-23-Encapsulate-git/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -82,12 +85,15 @@ clientB: project adapted for client B

    An arrow from A to B means, you can merge A in B. If there is no arrow from A to B that means it is forbidden to merge A in B. Here is the corresponding rubycode:

    -
    -$architecture={ 
    -    :master => [ :dev, :client ],
    -    :dev => [ :master ],
    -    :client => [ :clientA, :clientB ] }
    -
    +
    + +
    $architecture={ 
    +    :master => [ :dev, :client ],
    +    :dev => [ :master ],
    +    :client => [ :clientA, :clientB ] }
    +
    + +

    Having a :master => [ :dev, :client ] means you can merge master branch into dev and client.

    @@ -95,123 +101,128 @@ clientB: project adapted for client B

    But this script do far more than that. It also merge from top to down. The action allmerges will do:

    -
    -git co dev && git merge master
    -git co client && git merge master
    -git co clientA && git merge client
    -git co clientB && git merge client
    -
    +
    + +
    git co dev && git merge master
    +git co client && git merge master
    +git co clientA && git merge client
    +git co clientB && git merge client
    +
    + +

    That means, I can update all branches. The algorithm will not make loop even if there is a cycle in the branch hierarchy.

    Here it is:

    -
    -
    -#!/usr/bin/env ruby
    -# encoding: utf-8
    +
    -# architecture -# -# master <-> dev -# master -> client -# clien -> clientA | clientB -# -# merge using two of these branches should be -# restricted to these rules -# merge to one of these branch and an unknown one should -# raise a warning, and may the option to add this new branch -# to the hierarchy -$architecture={ - :master => [ :dev, :client ], - :dev => [ :master ], - :client => [ :clientA, :clientB ] } +
    #!/usr/bin/env ruby
    +# encoding: utf-8
     
    -def get_current_branch()
    -    (`git branch --no-color | awk '$1 == "*" {print $2}'`).chop.intern
    -end
    +# architecture
    +#
    +# master <-> dev
    +# master -> client
    +# clien -> clientA | clientB
    +#
    +# merge using two of these branches should be 
    +#   restricted to these rules
    +# merge to one of these branch and an unknown one should
    +#   raise a warning, and may the option to add this new branch
    +#   to the hierarchy
     
    -if ARGV.length == 0
    -    puts %{usage: $0:t [git_command or local_command]
    -    
    -local commands:
    -    allmerges: merge from top to down}
    -    exit 0
    -end
    +$architecture={ 
    +    :master => [ :dev, :client ],
    +    :dev => [ :master ],
    +    :client => [ :clientA, :clientB ] }
     
    -require 'set'
    -$known_branches=Set.new
    -$architecture.each do |k,v| 
    -    $known_branches.add(k)
    -    v.each { |b| $known_branches.add(b) }
    -end
    +def get_current_branch()
    +    (`git branch --no-color | awk '$1 == "*" {print $2}'`).chop.intern
    +end
     
    -def rec_merge(branch)
    -    if $architecture[branch].nil?
    -        return
    -    end
    -    $architecture[branch].each do |b|
    -        if $flag.has_key?(b.to_s + branch.to_s)
    -            next
    -        end
    -        flagname=branch.to_s + b.to_s
    -        if $flag.has_key?(flagname)
    -            next
    -        end
    -        if system %{eng checkout #{b}}
    -            if get_current_branch != b
    -                puts "Can't checkout to #{b}"
    -                exit 2
    -            end
    -            if system %{eng merge #{branch}}
    -                $flag[flagname]=true
    -                rec_merge(b)
    -            else
    -                exit 1
    -            end
    -        else
    -            exit 1
    -        end
    -    end
    -end
    +if ARGV.length == 0
    +    puts %{usage: $0:t [git_command or local_command]
    +    
    +local commands:
    +    allmerges: merge from top to down}
    +    exit 0
    +end
     
    -def do_all_merges
    -    puts 'Will merge from father to sons'
    -    current_branch=get_current_branch
    -    $flag={}
    -    rec_merge(:master)
    -    system %{git co #{current_branch}}
    -end
    +require 'set'
    +$known_branches=Set.new
    +$architecture.each do |k,v| 
    +    $known_branches.add(k)
    +    v.each { |b| $known_branches.add(b) }
    +end
     
    -def do_merge
    -    current_branch=get_current_branch
    -    src_branch=ARGV[1].intern
    -    puts %{do_merge: #{src_branch} => #{current_branch}}
    -    if $known_branches.include?(current_branch)
    -        if $known_branches.include?(src_branch)
    -            if $architecture.has_key?(src_branch) and 
    -                $architecture[src_branch].include?(current_branch)
    -                system %{git merge #{src_branch}}
    -            else
    -                puts %{Forbidden merge: #{src_branch} => #{current_branch}}
    -            end
    -        else
    -            puts %{Warning! #{src_branch} not mentionned in rb configuration}
    -            sleep 2
    -            system %{git merge #{src_branch}}
    -            puts %{Warning! #{src_branch} not mentionned in rb configuration}
    -        end
    -    end
    -end
    +def rec_merge(branch)
    +    if $architecture[branch].nil?
    +        return
    +    end
    +    $architecture[branch].each do |b|
    +        if $flag.has_key?(b.to_s + branch.to_s)
    +            next
    +        end
    +        flagname=branch.to_s + b.to_s
    +        if $flag.has_key?(flagname)
    +            next
    +        end
    +        if system %{eng checkout #{b}}
    +            if get_current_branch != b
    +                puts "Can't checkout to #{b}"
    +                exit 2
    +            end
    +            if system %{eng merge #{branch}}
    +                $flag[flagname]=true
    +                rec_merge(b)
    +            else
    +                exit 1
    +            end
    +        else
    +            exit 1
    +        end
    +    end
    +end
     
    -case ARGV[0] 
    -    when 'allmerges' then do_all_merges
    -    when 'merge' then do_merge
    -    else system %{git #{ARGV.join(' ')}}
    -end
    -
    -
    +def do_all_merges + puts 'Will merge from father to sons' + current_branch=get_current_branch + $flag={} + rec_merge(:master) + system %{git co #{current_branch}} +end + +def do_merge + current_branch=get_current_branch + src_branch=ARGV[1].intern + puts %{do_merge: #{src_branch} => #{current_branch}} + if $known_branches.include?(current_branch) + if $known_branches.include?(src_branch) + if $architecture.has_key?(src_branch) and + $architecture[src_branch].include?(current_branch) + system %{git merge #{src_branch}} + else + puts %{Forbidden merge: #{src_branch} => #{current_branch}} + end + else + puts %{Warning! #{src_branch} not mentionned in rb configuration} + sleep 2 + system %{git merge #{src_branch}} + puts %{Warning! #{src_branch} not mentionned in rb configuration} + end + end +end + +case ARGV[0] + when 'allmerges' then do_all_merges + when 'merge' then do_merge + else system %{git #{ARGV.join(' ')}} +end + + +

    All you need to do to make it work is simply to copy eng in a directory contained in your PATH.

    @@ -332,7 +343,7 @@ git co clientB && git merge client
    Created: 03/23/2010 - Modified: 05/09/2010 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/index.html b/output/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/index.html index 61ba0695b..bbf80c9cb 100644 --- a/output/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/index.html +++ b/output/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,14 +63,14 @@

    The more you wait to do something, the more difficult it is to start doing it.

    -

    I had to write another post for this blog. I had added many article idea in my todolist. But, I made many other things, and I’ve always said (until now), I’ll do this later. What changed my mind is the haunt of this simple remark about how to be productive in programming. +

    I had to write another post for this blog. I had added many article idea in my todolist. But, I made many other things, and I’ve always said (until now), I’ll do this later. What changed my mind is the haunt of this simple remark about how to be productive in programming. > Stop write TODO in your code and make it now!
    -> You’ll be surprised by the results.

    +> You’ll be surprised by the results.

    In short: > Just do it! ou Juste fait le comme auraient dit les nuls.

    -

    Finally I’ll certainly write blog post more often for a short period of time.

    +

    Finally I’ll certainly write blog post more often for a short period of time.

    What did I do?

    @@ -77,7 +80,7 @@

    I also have a real life. I enjoyed some vacancies with my family.

    -

    I work with Luc on a simple ruby REST/JSON/API oriented framework. It works fairly well, with really few bug until now. We planify to make a simple todolist tutorial. May be in two to three blog posts. This framework is not public for now. It will certainly be after we’ll create some simple web service with it and made a nice website for it.

    +

    I work with Luc on a simple ruby REST/JSON/API oriented framework. It works fairly well, with really few bug until now. We planify to make a simple todolist tutorial. May be in two to three blog posts. This framework is not public for now. It will certainly be after we’ll create some simple web service with it and made a nice website for it.

    Then what I plan to do from now:

    @@ -89,7 +92,7 @@
  • provide the sources of this website on github
  • -

    There is some random in some of these achivement mostly because they don’t depend totally on me.

    +

    There is some random in some of these achivement mostly because they don’t depend totally on me.

    diff --git a/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb b/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb index 1121d4ef1..bc18f6ccd 100644 --- a/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb +++ b/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb @@ -1,4 +1,3 @@ - # repair cutted XML code by closing the tags # work even if the XML is cut into a tag. # example: diff --git a/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html b/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html index 7b211f2bb..4eafea2f7 100644 --- a/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html +++ b/output/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,104 +65,100 @@

    Here is an example:

    -
    -<div class="corps">
    -    <div class="intro">
    -        <p>Introduction</p>
    -    </div>
    -    <p>The first paragraph</p>
    -    <img src="/img/img.png" alt="an image"/>
    -    <p>Another long paragraph</p>
    -</div>
    -
    +
    <div class="corps">
    +    <div class="intro">
    +        <p>Introduction</p>
    +    </div>
    +    <p>The first paragraph</p>
    +    <img src="/img/img.png" alt="an image"/>
    +    <p>Another long paragraph</p>
    +</div>
    +

    After the cut, I obtain:

    -
    -<div class="corps">
    -    <div class="intro">
    -        <p>Introduction</p>
    -    </div>
    -    <p>The first paragraph</p>
    -    <img src="/img/im
    -
    +
    <div class="corps">
    +    <div class="intro">
    +        <p>Introduction</p>
    +    </div>
    +    <p>The first paragraph</p>
    +    <img src="/img/im
    +

    Argh! In the middle of an <img> tag.

    -

    In fact, it is not as difficult as it should sound first. The secret is, you don’t need to keep the complete tree structure to repair it, but only the list of not closed parents.

    +

    In fact, it is not as difficult as it should sound first. The secret is, you don’t need to keep the complete tree structure to repair it, but only the list of not closed parents.

    Given with our example, when we are after the first paragraph. we only have to close the div for class corps and the XML is repaired. Of course, when you cut inside a tag, you sould go back, as if you where just before it. Delete this tag and all is ok.

    Then, all you have to do, is not remember all the XML tree, but only the heap containing your parents. Suppose we treat the complete first example, the stack will pass through the following state, in order:

    -
    -[]           
    -[div]           <div class="corps">
    -[div, div]          <div class="intro">
    -[div, div, p]           <p>
    +
    []           
    +[div]           <div class="corps">
    +[div, div]          <div class="intro">
    +[div, div, p]           <p>
                                 Introduction
    -[div, div]              </p>
    -[div]               </div>
    -[div, p]            <p>
    +[div, div]              </p>
    +[div]               </div>
    +[div, p]            <p>
                             The first paragraph
    -[div]               </p>
    -[div]               <img src="/img/img.png" alt="an image"/>
    -[div, p]            <p>
    +[div]               </p>
    +[div]               <img src="/img/img.png" alt="an image"/>
    +[div, p]            <p>
                             Another long paragraph
    -[div]               </p>
    -[]              </div>
    -
    +[div] </p> +[] </div> +
    -

    The algorihm, is then really simple: -<pre class="twilight"> -let res be the XML as a string ; +

    The algorihm, is then really simple:

    + +
    let res be the XML as a string ; 
     read res and each time you encouter a tag: 
         if it is an opening one: 
             push it to the stack
         else if it is a closing one: 
    -        pop the stack.

    + pop the stack. -

    remove any malformed/cutted tag in the end of res +remove any malformed/cutted tag in the end of res for each tag in the stack, pop it, and write: - res = res + closed tag

    + res = res + closed tag -

    return res -</pre>

    +return res +

    And res contain the repaired XML.

    Finally, this is the code in ruby I use. The xml variable contain the cutted XML.

    -
    -
    -# repair cutted XML code by closing the tags
    -# work even if the XML is cut into a tag.
    -# example:
    -#    transform '<div> <span> toto </span> <p> hello <a href="http://tur'
    -#    into      '<div> <span> toto </span> <p> hello </p></div>'
    -def repair_xml( xml )
    -    parents=[]
    -    depth=0
    -    xml.scan( %r{<(/?)(\w*)[^>]*(/?)>} ).each do |m|
    -        if m[2] == "/"
    -            next
    -        end
    -        if m[0] == "" 
    -            parents[depth]=m[1]
    -            depth+=1
    -        else
    -            depth-=1
    -        end
    -    end
    -    res=xml.sub(/<[^>]*$/m,'')
    -    depth-=1
    -    depth.downto(0).each { |x| res<<= %{</#{parents[x]}>} }
    -    res
    -end
    -
    -
    + -

    I don’t know if the code can help you, but the raisonning should definitively be known.

    +
    # repair cutted XML code by closing the tags
    +# work even if the XML is cut into a tag.
    +# example:
    +#    transform '<div> <span> toto </span> <p> hello <a href="http://tur'
    +#    into      '<div> <span> toto </span> <p> hello </p></div>'
    +def repair_xml( xml )
    +    parents=[]
    +    depth=0
    +    xml.scan( %r{<(/?)(\w*)[^>]*(/?)>} ).each do |m|
    +        if m[2] == "/"
    +            next
    +        end
    +        if m[0] == "" 
    +            parents[depth]=m[1]
    +            depth+=1
    +        else
    +            depth-=1
    +        end
    +    end
    +    res=xml.sub(/<[^>]*$/m,'')
    +    depth-=1
    +    depth.downto(0).each { |x| res<<= %{</#{parents[x]}>} }
    +    res
    +end
    +
    + +

    I don’t know if the code can help you, but the raisonning should definitively be known.

    diff --git a/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_destination_tree.png b/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_destination_tree.png index a39289809ee6cbf29f3e2d6df992dfd039caec3b..9bcef924eb488af7685c2de50c9112ff1d23f4b4 100644 GIT binary patch literal 24490 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 X?^|CYk4pl+PwX_^Ws+`u`27C>t0#us literal 24274 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

  • WyRXY97GpBae?U2&| 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 DPqkQr diff --git a/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_source_tree.png b/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_source_tree.png index ae8c96b84f9c82adf383ab49126dc54476ad5874..73a4a5dbb546d5fd7493ec725c6a9a4b9c41e12f 100644 GIT binary patch literal 19344 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 EFRlA3^#A|> literal 17763 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 MrlCeD*7nZ-2XKb9S^xk5 diff --git a/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html b/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html index c695b71e9..84679b77a 100644 --- a/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html +++ b/output/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -71,7 +74,7 @@
  • Used a pen and a sheet of paper
  • Made some math.
  • Crushed the problem in 10 minutes
  • -
  • Conclusion: The pragmatism shouldn’t mean “never use theory”.
  • +
  • Conclusion: The pragmatism shouldn’t mean “never use theory”.
  • @@ -86,10 +89,10 @@ I entered in the infernal: try & repair loop. Each step was like:

    -

    – Just this thing to repair and that should be done.
    - – OK, now that should just work.
    - – Yeah!!!
    - – Oops! I forgotten that…
    +

    – Just this thing to repair and that should be done.
    + – OK, now that should just work.
    + – Yeah!!!
    + – Oops! I forgotten that…
    repeat until death

    @@ -117,52 +120,50 @@ I had to face a problem of the same kind at my job. The problem was simple to th

    The source xml was in the following general format:

    -
    -<rubrique>
    -    <contenu>
    -        <tag1>value1</tag1>
    -        <tag2>value2</tag2>
    +
    <rubrique>
    +    <contenu>
    +        <tag1>value1</tag1>
    +        <tag2>value2</tag2>
             ...
    -    </contenu>
    -    <enfant>
    -        <rubrique>
    +    </contenu>
    +    <enfant>
    +        <rubrique>
                 ...
    -        </rubrique>
    +        </rubrique>
             ...
    -        <rubrique>
    +        <rubrique>
                 ...
    -        </rubrique>
    -    </enfant>
    -</menu>
    -
    + </rubrique> + </enfant> +</menu> +

    and the destination format was in the following general format:

    -
    -<item name="Menu0">
    -    <value>
    -        <item name="menu">
    -            <value>
    -                <item name="tag1">
    -                    <value>value1</value>
    -                </item>
    -                <item name="tag2">
    -                    <value>value2</value>
    -                </item>
    +
    <item name="Menu0">
    +    <value>
    +        <item name="menu">
    +            <value>
    +                <item name="tag1">
    +                    <value>value1</value>
    +                </item>
    +                <item name="tag2">
    +                    <value>value2</value>
    +                </item>
                     ...
    -                <item name="menu">
    -                    <value>
    +                <item name="menu">
    +                    <value>
                             ...
    -                    </value>
    -                    <value>
    +                    </value>
    +                    <value>
                             ...
    -                    </value>
    -                </item>
    -            </value>
    -        </item>
    -    </value>
    -</item>
    -
    + </value> + </item> + </value> + </item> + </value> +</item> +

    At first sight I believed it will be easy. I was so certain it will be easy that I fixed to myself the following rules:

    @@ -173,7 +174,7 @@ I had to face a problem of the same kind at my job. The problem was simple to th

    You can try if you want. If you attack the problem directly opening an editor, I assure you, it will certainly be not so simple. -I can tell that, because it’s what I’ve done. And I must say I lost almost a complete day at work trying to resolve this. There was also, many small problems around that make me lose more than two days for this problem.

    +I can tell that, because it’s what I’ve done. And I must say I lost almost a complete day at work trying to resolve this. There was also, many small problems around that make me lose more than two days for this problem.

    Why after two days did I was unable to resolve this problem which seems so simple?

    @@ -194,14 +195,14 @@ I thought about how to resolve the problem but with the eyes of a pragmatic

    That should be a simple perl search and replace program.
    -Let’s begin to write code

    +Let’s begin to write code

    This is the second sentence that was plainly wrong. I started in the wrong direction. And the workflow did not work from this entry point.

    Think

    -

    After some times, I just stopped to work. Tell myself “it is enough, now, I must finish it!”. +

    After some times, I just stopped to work. Tell myself “it is enough, now, I must finish it!”. I took a sheet of paper, a pen and began to draw some trees.

    I began by make by removing most of the verbosity. @@ -248,23 +249,21 @@ r - b

    And look at what it implies when you write it in xml:

    -
    -<r>
    -  <x>
    -    <a>value for a</a>
    -    <b>value for b</b>
    -  </x>
    -  <y>
    -    <c>value for c</c>
    -  </y>
    -</r>
    -
    +
    <r>
    +  <x>
    +    <a>value for a</a>
    +    <b>value for b</b>
    +  </x>
    +  <y>
    +    <c>value for c</c>
    +  </y>
    +</r>
    +

    Then deleting all x nodes is equivalent to pass the xml via the following search and replace script:

    -
    -s/<\/?x>//g
    -
    +
    s/<\/?x>//g
    +

    Therefore, if there exists a one state deterministic transducer which transform my trees ; I can transform the xml from one format to another with just a simple list of search and replace directives.

    @@ -307,28 +306,26 @@ M - V - M - V - tag2 tag1

    can be done using the following one state deterministic tree transducer:

    -

    C → ε
    +

    C → ε
    E → M
    R → V

    Wich can be traduced by the following simple search and replace directives:

    -
    -s/C//g
    -s/E/M/g
    -s/R/V/g
    -
    +
    s/C//g
    +s/E/M/g
    +s/R/V/g
    +

    Once adapted to xml it becomes:

    -
    -s%</?contenu>%%g
    -s%<enfant>%<item name="menu">%g
    -s%</enfant>%</item>%g
    -s%<rubrique>%<value>%g
    -s%</rubrique>%</value>%g
    -
    +
    s%</?contenu>%%g
    +s%<enfant>%<item name="menu">%g
    +s%</enfant>%</item>%g
    +s%<rubrique>%<value>%g
    +s%</rubrique>%</value>%g
    +

    That is all.

    diff --git a/output/Scratch/en/blog/2010-06-14-multi-language-choices/index.html b/output/Scratch/en/blog/2010-06-14-multi-language-choices/index.html index 32929da94..cdd5b46d8 100644 --- a/output/Scratch/en/blog/2010-06-14-multi-language-choices/index.html +++ b/output/Scratch/en/blog/2010-06-14-multi-language-choices/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -73,7 +76,7 @@ here is an example of english text.

    This way of handling translations force you to write completely an article in one language, copy it, and translate it.

    -

    However, most of time, there are common parts like images, source code, etc… +

    However, most of time, there are common parts like images, source code, etc… When I want to correct some mistake on these parts, I have to make twice the work. With sometimes adding another mistake in only one language.

    This is why I preferred to handle it differently. @@ -89,7 +92,7 @@ Finally my files looks like:

    [image](url) -

    As I edit my files with vim, it is really easy to add fr: or en: at some line’s beginning using the useful C-v. +

    As I edit my files with vim, it is really easy to add fr: or en: at some line’s beginning using the useful C-v. However nanoc was conceived to be used for one language only. Or to be used with the first method. I tried to adapt nanoc to my usage. But after a while, I found it easier to pre-filter the nanoc work by a simple script. My script transform my file into two new files. And all work like a charm.

    You can get my blog code source (without most of articles) at github.com/yogsototh/Scratch.

    diff --git a/output/Scratch/en/blog/2010-06-15-Get-my-blog-engine/index.html b/output/Scratch/en/blog/2010-06-15-Get-my-blog-engine/index.html index 0c259a059..f80c901fd 100644 --- a/output/Scratch/en/blog/2010-06-15-Get-my-blog-engine/index.html +++ b/output/Scratch/en/blog/2010-06-15-Get-my-blog-engine/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,7 +69,7 @@ You can get it on github.comintenseDebate comments integration (asynchronous) ;
  • Portable with and without javascript, XHTML Strict 1.0 / CSS3,
  • Write in markdown format (no HTML editing needed),
  • -
  • Typographic ameliorations (no ‘:’ starting a line in French for example),
  • +
  • Typographic ameliorations (no ‘:’ starting a line in French for example),
  • Graphviz graph generation integration.
  • @@ -82,12 +85,11 @@ You can get it on github.comOnce installed (follow the README.md instructions).

    -
    -$ cd /root/of/nanoc3_blog
    +
    $ cd /root/of/nanoc3_blog
     $ ./task/new_blog_entry Title of the blog
     $ vi latest.md
     $ ./task/recompile
    -
    +

    Now your website reside into the output directory.

    @@ -104,9 +106,9 @@ $ ./task/recompile

    Multi-language

    All files in multi are processed and copied in the content directory. -For each file in multi, each line starting by ‘fr: ’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.

    +For each file in multi, each line starting by ‘fr: ’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.

    -

    If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.

    +

    If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.

    Edition & Rendering

    @@ -120,7 +122,7 @@ For each file in multi, each line starting by ‘fr: ’ ar

    Typography

    -

    In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by &nbsp. This enable not to have a line starting by a single special character.

    +

    In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by &nbsp. This enable not to have a line starting by a single special character.

    You can use small caps using <sc> tags.

    @@ -135,11 +137,10 @@ For each file in multi, each line starting by ‘fr: ’ ar

    To write source code you should use the following format:

    -
    -<code class="ruby" file="filename.rb">
    +
    <code class="ruby" file="filename.rb">
     The code
    -</cOde>
    -
    +</cOde> +

    The file attribute is not required.

    diff --git a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html index e3f00f255..946d8732a 100644 --- a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html +++ b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html index 3593c9bfc..0f79658ee 100644 --- a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html +++ b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/index.html b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/index.html index d025fc0f6..f25c0ea98 100644 --- a/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/index.html +++ b/output/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -61,68 +64,65 @@ First you should look on how
    - var admin = $.cookie('admin'); - if (! admin) { - // put your analytics code here - } else { - console.log("[WARNING] you're HIDDEN to analytics"); +
        var admin = $.cookie('admin');
    +    if (! admin) {
    +        // put your analytics code here
    +    } else {
    +        console.log("[WARNING] you're HIDDEN to analytics");
         }
    -
    +

    then create two html files. One to hide:

    -
    -
    -<?xml version="1.0" encoding="utf-8"?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    -        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    -    <head>
    -        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -        <script type="text/javascript" src="jquery.js"></script>
    -        <script type="text/javascript" src="jquery.cookie.js"></script>
    -        <script>
    -            $(document).ready(function(){
    -                $.cookie('admin',1);
    -                $('#info').html('Analytics can no more see you.')
    -            });
    -        </script>
    -        <title>Hide to analytics</title>
    -    </head>
    -    <body>
    -        <div id="info"></div> 
    -    </body>
    -</html>
    -
    -
    + + +
    <?xml version="1.0" encoding="utf-8"?>
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    +        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    +    <head>
    +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    +        <script type="text/javascript" src="jquery.js"></script>
    +        <script type="text/javascript" src="jquery.cookie.js"></script>
    +        <script>
    +            $(document).ready(function(){
    +                $.cookie('admin',1);
    +                $('#info').html('Analytics can no more see you.')
    +            });
    +        </script>
    +        <title>Hide to analytics</title>
    +    </head>
    +    <body>
    +        <div id="info"></div> 
    +    </body>
    +</html>
    +

    the other to be visible again (it can be useful):

    -
    -
    -<?xml version="1.0" encoding="utf-8"?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    -        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    -    <head>
    -        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -        <script type="text/javascript" src="jquery.js"></script>
    -        <script type="text/javascript" src="jquery.cookie.js"></script>
    -        <script>
    -            $(document).ready(function(){
    -                $.cookie('admin',null);
    -                $('#info').html('Analytics can see you.')
    -            });
    -        </script>
    -        <title>Hide to analytics</title>
    -    </head>
    -    <body>
    -        <div id="info"></div> 
    -    </body>
    -</html>
    -
    -
    + + +
    <?xml version="1.0" encoding="utf-8"?>
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    +        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    +    <head>
    +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    +        <script type="text/javascript" src="jquery.js"></script>
    +        <script type="text/javascript" src="jquery.cookie.js"></script>
    +        <script>
    +            $(document).ready(function(){
    +                $.cookie('admin',null);
    +                $('#info').html('Analytics can see you.')
    +            });
    +        </script>
    +        <title>Hide to analytics</title>
    +    </head>
    +    <body>
    +        <div id="info"></div> 
    +    </body>
    +</html>
    +

    Now accessing these files with you browser you can hide or appear in your statistics. You just have to think to access these file from all you browser.

    diff --git a/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/code/yga.js b/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/code/yga.js index 31369ad5c..99f4a4661 100644 --- a/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/code/yga.js +++ b/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/code/yga.js @@ -1,4 +1,3 @@ - $(document).ready( function() { // add an event to all link for google analytics $('a').click(function () { diff --git a/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/index.html b/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/index.html index 5de44c571..0aad118af 100644 --- a/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/index.html +++ b/output/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,64 +65,62 @@

    First in your html you need to use jQuery and a javscript file I named yga.js:

    -
    -    <script type="text/javascript" src="jquery.js"></script>
    -    <script type="text/javascript" src="yga.js"></script>
    -
    +
        <script type="text/javascript" src="jquery.js"></script>
    +    <script type="text/javascript" src="yga.js"></script>
    +

    And here is the yga.js file:

    -
    -
    -$(document).ready( function() {
    -    // add an event to all link for google analytics
    -    $('a').click(function () {
    -        // tell analytics to save event
    -        try {
    -            var identifier=$(this).attr('id') ;
    -            var href=$(this).attr('href')
    -            var label="";
    -            if ( typeof( identifier ) != 'undefined' ) {
    -                label=label+'[id]:'+identifier
    -                category='JSLink'
    +
    +
    +
    $(document).ready( function() {
    +    // add an event to all link for google analytics
    +    $('a').click(function () {
    +        // tell analytics to save event
    +        try {
    +            var identifier=$(this).attr('id') ;
    +            var href=$(this).attr('href')
    +            var label="";
    +            if ( typeof( identifier ) != 'undefined' ) {
    +                label=label+'[id]:'+identifier
    +                category='JSLink'
                 }
    -            if ( typeof( href ) != 'undefined' ) {
    -                label=label+' [href]:'+href
    -                if ( href[0] == '#' ) {
    -                    category='Anchor';
    -                } else {
    -                    category='Link';
    +            if ( typeof( href ) != 'undefined' ) {
    +                label=label+' [href]:'+href
    +                if ( href[0] == '#' ) {
    +                    category='Anchor';
    +                } else {
    +                    category='Link';
                     }
                 }
    -            _gaq.push(['_trackEvent', category, 'clicked', label]);
    -            // console.log('[tracked]: ' + category + ' ; clicked ; ' + label );
    +            _gaq.push(['_trackEvent', category, 'clicked', label]);
    +            // console.log('[tracked]: ' + category + ' ; clicked ; ' + label );
             }
    -        catch (err) {
    -            console.log(err);
    +        catch (err) {
    +            console.log(err);
             }
     
    -        // pause to allow google script to run
    -        var date = new Date();
    -        var curDate = null;
    -        do {
    -            curDate = new Date();
    -        } while(curDate-date < 300);
    +        // pause to allow google script to run
    +        var date = new Date();
    +        var curDate = null;
    +        do {
    +            curDate = new Date();
    +        } while(curDate-date < 300);
         });
     });
     
    -var _gaq = _gaq || [];
    -_gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
    -_gaq.push(['_trackPageview']);
    +var _gaq = _gaq || [];
    +_gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
    +_gaq.push(['_trackPageview']);
     
    -(function() {
    - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    +(function() {
    + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
      })();
    -
    -
    + -

    Replace the: UA-XXXXXXXX-1 by your google analytics code and you’re done.

    +

    Replace the: UA-XXXXXXXX-1 by your google analytics code and you’re done.

    To see what occurs, simply go in Content and Event Tracking as shown in the following screenshot:

    @@ -242,7 +243,7 @@ _gaq.push([
    Created: 06/17/2010 - Modified: 06/17/2010 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js b/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js index 751a56c5c..d0c83610a 100644 --- a/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js +++ b/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js @@ -1,4 +1,3 @@ - // --- code popup --- function openPopup() { $(this).clone(false).appendTo($("#_code")); diff --git a/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/index.html b/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/index.html index e17bee2dc..9786e337f 100644 --- a/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/index.html +++ b/output/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,32 +59,31 @@

    Here is a fast and easy way to create jQuery popup.

    -
    -
    -// --- code popup ---
    -function openPopup() {
    -    $(this).clone(false).appendTo($("#_code"));
    -    $("#_code").show();
    +
    +
    +
    // --- code popup ---
    +function openPopup() {
    +    $(this).clone(false).appendTo($("#_code"));
    +    $("#_code").show();
     }
     
    -function closePopup() {
    -    $("#_code").html("");
    -    $("#_code").hide();
    +function closePopup() {
    +    $("#_code").html("");
    +    $("#_code").hide();
     }
     
    -function initCode() {
    -    $(".code").click(openPopup);
    -    $(".code").css({cursor: "pointer"});
    -    $('body').append('<div id="_code"></div>');
    -    $('#_code').css( { 'text-align': "justify", position: "fixed", 
    -                        left:0, top:0, width: "100%", height: "100%", 
    -                        "background-color": "rgba(0, 0, 0, 0.8)", 'z-index':2000, 'padding':'3px'} );
    -    $('#_code').hide();
    -    $('#_code').click(closePopup);
    +function initCode() {
    +    $(".code").click(openPopup);
    +    $(".code").css({cursor: "pointer"});
    +    $('body').append('<div id="_code"></div>');
    +    $('#_code').css( { 'text-align': "justify", position: "fixed", 
    +                        left:0, top:0, width: "100%", height: "100%", 
    +                        "background-color": "rgba(0, 0, 0, 0.8)", 'z-index':2000, 'padding':'3px'} );
    +    $('#_code').hide();
    +    $('#_code').click(closePopup);
     }
    -// --- end of code popup section ---
    -
    -
    +// --- end of code popup section --- +

    What does this code do?

    diff --git a/output/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/index.html b/output/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/index.html index a0d16bf3b..29d6bab03 100644 --- a/output/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/index.html +++ b/output/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -64,7 +67,7 @@
  • Tried to make YPassword in jQuery and with Cappuccino.
  • Cappuccino nice in desktop browser but 1.4MB, not compatible with iPhone.
  • jQuery not as nice as the Cappuccino version but 106KB. iPhone compatible.
  • -
  • I’ll give a try to Dashcode 3.
  • +
  • I’ll give a try to Dashcode 3.
  • @@ -97,7 +100,7 @@ I then made a second version with the Cappuccino
    -

    If you don’t mind about what does my widget and just want to know how the two frameworkcompare, you should go +

    If you don’t mind about what does my widget and just want to know how the two frameworkcompare, you should go directly to the next part.

    @@ -105,9 +108,9 @@ directly to the next part.

    I manage my password on many site with a simple method. -I remember a strong master password. And my password is mainly -<pre class="twilight">hash(masterPassword+domainName) -</pre>

    +I remember a strong master password. And my password is mainly

    + +
    hash(masterPassword+domainName)

    In reality I need a bit more informations to create a password:

    @@ -121,14 +124,13 @@ I remember a strong master password. And my password is mainly

    The result password is this:

    -
    -domainName=domaine_Name_Of_URL(url)
    -hash=sha1( masterPassword + leakedTimes + domainName )
    -if ( kind == 'base64' )
    -    hash=base64(hash)
    -end
    -return hash[0..maxlength]
    -
    +
    domainName=domaine_Name_Of_URL(url)
    +hash=sha1( masterPassword + leakedTimes + domainName )
    +if ( kind == 'base64' )
    +    hash=base64(hash)
    +end
    +return hash[0..maxlength]
    +

    In fact depending of websites, some give some strange constraint to your password:

    @@ -137,7 +139,7 @@ hash=sha1( masterPasswo
  • maximal length,
  • must not contain a special character,
  • must contain a special character,
  • -
  • etc…
  • +
  • etc…
  • And if you want to change your password the leak number is here for that. @@ -153,14 +155,14 @@ All informations such as user name, maximal length can be stored in a public fil

    Cappuccino

    -

    First, I’d like to say Cappuccino applications look simply awesome. +

    First, I’d like to say Cappuccino applications look simply awesome. It is like having a Cocoa application in your web browser. And this is great.

    I also must admit I enjoyed making my application with Cappuccino. It is like programming for an iPhone application. If you are a bit familiar with Cocoa, you feel at home. -If you don’t know anything about Cocoa, I suggest you to look at it. +If you don’t know anything about Cocoa, I suggest you to look at it. This is a really great framework to make User Interface. I am not a specialist, but I have done some MFC, java Swing1 and WXWindows User Interfaces (some years ago). And I must say, Cocoa is far better than those.

    @@ -177,37 +179,37 @@ But there was also some drawbacks

  • I could have done the User Interface using Interface Builder.
  • -

    Some things I didn’t like:

    +

    Some things I didn’t like:

    • I made some time to understand how to handle the onChange on the text fields.
    • Documentation lacked a bit of organisation.
    • -
    • It doesn’t work on iPhone.
    • +
    • It doesn’t work on iPhone.
    • It weighted 11MB to deploy.
    • It weight 1.3MB to load.
    -

    I didn’t use bindings because I believe they are not ready by now.

    +

    I didn’t use bindings because I believe they are not ready by now.

    jQuery

    -

    The jQuery version of YPassword is not as finished as the Cappuccino one. Because, there is no slider directly with jQuery. I’d have to use jQueryUI. And I believe, using it will make the application weight far more than the today 106KB.

    +

    The jQuery version of YPassword is not as finished as the Cappuccino one. Because, there is no slider directly with jQuery. I’d have to use jQueryUI. And I believe, using it will make the application weight far more than the today 106KB.

    -

    To make this version I simply copied my widget source code and adapted it. It was straightforward. But jQuery is not an application oriented framework. It is more a “dark side javascript animation framework”2.

    +

    To make this version I simply copied my widget source code and adapted it. It was straightforward. But jQuery is not an application oriented framework. It is more a “dark side javascript animation framework”2.

    -

    I don’t have too much to say about the jQuery version. But this was way more low level programming than Cappuccino.

    +

    I don’t have too much to say about the jQuery version. But this was way more low level programming than Cappuccino.

    My conclusion

    -

    If you want to make an iPhone compatible web application just don’t use Cappuccino yet. +

    If you want to make an iPhone compatible web application just don’t use Cappuccino yet. If you want to make simple application like mine, I also believe, Cappuccino is a bit too much.

    If you want to make a complex web oriented application, Cappuccino is a great choice. But you may have some difficulties to begin programming with it.

    -

    Finally, to terminate my web version of my widget, I’ll give a try to Dashcode 3. +

    Finally, to terminate my web version of my widget, I’ll give a try to Dashcode 3. It seems to be a good alternative to create web widgets. -I don’t know if Dashcode 3 is portable on non webkit browser. +I don’t know if Dashcode 3 is portable on non webkit browser. But if it is, it could be the end of projects like Cappuccino and Sproutcore.


      @@ -215,7 +217,7 @@ But if it is, it could be the end of projects like Cappuccino and Sproutcore.

      If you are interested you can take a look at SEDiL. I am proud of the tree drawing view made from scratch.

    1. -

      I don’t want to feel like a troll I use jQuery to make some dark side animation on this blog. But the javascript on my blog is not needed except for commenting.

      +

      I don’t want to feel like a troll I use jQuery to make some dark side animation on this blog. But the javascript on my blog is not needed except for commenting.

    diff --git a/output/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html b/output/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html index 24168d1d3..039791a47 100644 --- a/output/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html +++ b/output/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -55,7 +58,7 @@

    Some Reddit users reported my website was really long to load and to scroll. -They thinks it was because of the ‘1px shadow’ I apply on all the text. +They thinks it was because of the ‘1px shadow’ I apply on all the text. I was a bit surprised, because I make some test into a really slow virtual machine. And all have always worked fine. In fact, what slow down so much are by order of importance:

      @@ -69,7 +72,7 @@ I was a bit surprised, because I make some test into a really slow virt

      Safari and Chrome use webkit, when you access my website with javascript enabled, an additionnal browser specific CSS is loaded. Until now I switched only between: IE, Mozilla and Webkit. Now I added one more special case for Chrome. Now I continue to use gradient for Safari but no more on Chrome.

      -

      I didn’t tried to verify the efficiency of all new CSS 3 features. But I advise you not to use -webkit-gradient on Chrome. At least when the host is a Linux.

      +

      I didn’t tried to verify the efficiency of all new CSS 3 features. But I advise you not to use -webkit-gradient on Chrome. At least when the host is a Linux.

      Box Shadows

      @@ -77,7 +80,7 @@ I was a bit surprised, because I make some test into a really slow virt

      Text Shadows

      -

      Many tell me to use text-shadows sparingly. But I believe it was not the real reason of the slow down. This is why I’ll get them back.

      +

      Many tell me to use text-shadows sparingly. But I believe it was not the real reason of the slow down. This is why I’ll get them back.

      Conclusion

      diff --git a/output/Scratch/en/blog/2010-07-09-Indecidabilities/index.html b/output/Scratch/en/blog/2010-07-09-Indecidabilities/index.html index 19c4ab58f..04e5a119e 100644 --- a/output/Scratch/en/blog/2010-07-09-Indecidabilities/index.html +++ b/output/Scratch/en/blog/2010-07-09-Indecidabilities/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -81,10 +84,10 @@

      If a demiurge made our world, he certainly had a great sense of humor. After this read, you should be convinced. -I’ll pretend to be him. -I’ll create a simplified world. +I’ll pretend to be him. +I’ll create a simplified world. A world that obey to simple mathematical rules. -And I’ll tell you about one of the curse on this world: the undecidability. +And I’ll tell you about one of the curse on this world: the undecidability. The inability to know if we had find the truth. The inability to predict many things that should be natural. Here begin the story.

      @@ -93,14 +96,14 @@ Here begin the story.

    -

    no name

    +

    leftblogimage(“genesis.png”)

    In the beginning there was only void. Then a blog post beginning to be written. I breath profoundly to feel the weight of the act I will accomplish. -A last tense moment and… I create the Universe. +A last tense moment and… I create the Universe. An incredible Universe which will exists only the time of this read. -I’m the demiurge of this universe and you are its observer.

    +I’m the demiurge of this universe and you are its observer.

    I construct this world using only simples rules. I decide that real rules of this world will be the one we believe are true for our world. @@ -109,7 +112,7 @@ For their world, everything we believe today is true for them. Their world is then probably simpler than our. Particularly, we can describe this world with axioms and mathematic rules. It is not so sure for our Universe. -But we’ll talk about that later.

    +But we’ll talk about that later.

    Lets the work begin. I create an Earth. @@ -119,14 +122,14 @@ In particular they try to understand their world. They believe that if they know the rules of their world they will be able to predict the consequences of most of their acts. They are so naive. If only they knew. -But I’m here to help them.

    +But I’m here to help them.

    I am a God who likes jokes. The first joke I make to Ys is to make their sense imperfect. Furthermore it is not possible to make perfect precise measure in my world. I let Ys ameliorate their technology but there is a theoretical limit to the best precision they can reach.

    -

    I’d like to precise that these people believe their world is flat. +

    I’d like to precise that these people believe their world is flat. Some believe it is possible to find the rules of their Universe. Now, let the game begins.

    @@ -143,7 +146,7 @@ It is certainly a rule of my Universe. But how to be certain all triangle in my Universe share this property?

    -

    no name

    +

    three triangles

    Some began to formalize the problem. They end by writing a mathematical proof. @@ -153,14 +156,14 @@ The proof is based on rules and axioms. How to be certain these rules and axioms are right in their world? They will try to measure again and again the sum of the angles of triangles. The measure will never fail. -But they’ll never be certain the rules and axioms are right. +But they’ll never be certain the rules and axioms are right. Because then only way to verify all axioms depends of observation. And as a facetious god, I forbid perfect measure in observation.

    Of course, they prey, they call me to help. -And as any respectful god, I don’t answer. -Ah ah ah! I’ve always loved to make these kind of thing. -Let’s act as if I don’t exists. +And as any respectful god, I don’t answer. +Ah ah ah! I’ve always loved to make these kind of thing. +Let’s act as if I don’t exists. What a good joke!

    They feel sad. But they have some hope:

    @@ -173,10 +176,10 @@ What a good joke!

    Growing errors Undecidability

    -

    no name

    +

    Three bodies

    Unfortunately, the three bodies problem will crush this hope. -Using Newton’s Universal Law of gravitation with two bodies, we can predict with precision what will be their position and speed in the future. +Using Newton’s Universal Law of gravitation with two bodies, we can predict with precision what will be their position and speed in the future. Until there all seems OK. But now, add another body. All errors will grow. @@ -195,25 +198,25 @@ And we should at least determine what we can predict and what we cannot.

    Consider the following question:

    -

    no name

    +

    Mandelbrot set

    -

    Consider some GPS coordinates on a point around the cost of the “Bretagne” in France. +

    Consider some GPS coordinates on a point around the cost of the “Bretagne” in France. The coordinates are 3 feet precise. Is the point in the water or on Earth?

    For some coordinates it is not possible to know. Even if we are authorize to move a bit to dodge the borders. -Because there are some zone in which all point could be a “border” for any size of the zone.

    +Because there are some zone in which all point could be a “border” for any size of the zone.

    We can even imagine some mathematical structure where all points are at the border1.

    Logical Undecidability

    -

    no name

    +

    recursive stack overflow

    Until there all problem were undecidable because of measure errors. May be in a controlled world without any error we should be able to predict anything.
    -I’m sorry to say no. +I’m sorry to say no. Even in a self-contained mathematical world it can be possible to create object with an unpredictable behaviour.

    It is the halting problem.

    @@ -229,8 +232,8 @@ More precisely:

    Hypothesis: there exists a program P such that:

      -
    • P(x,y) return “stop” in a finite amount of time if x(y)2 will stop running.
    • -
    • P(x,y) return “loop” in a finite amount of time if x(y) will never stop running.
    • +
    • P(x,y) return “stop” in a finite amount of time if x(y)2 will stop running.
    • +
    • P(x,y) return “loop” in a finite amount of time if x(y) will never stop running.

    Remark: Any program can be represented as a string. Therefore, a program can be used as the input of another program. @@ -247,8 +250,8 @@ Q(x) :

    Now, what is the value of P(Q,Q)?

      -
    • if P(Q,Q) returns “stop” that imply by construction of Q that P(Q,Q) returns “loop”.
    • -
    • if P(Q,Q) returns “loop” that means by construction of Q that P(Q,Q) return “stop”.
    • +
    • if P(Q,Q) returns “stop” that imply by construction of Q that P(Q,Q) returns “loop”.
    • +
    • if P(Q,Q) returns “loop” that means by construction of Q that P(Q,Q) return “stop”.

    Therefore there is a contradiction the only way to handle is by the non existence of the program P.

    @@ -256,7 +259,7 @@ Q(x) :

    I am the demiurge of this imaginary world. And I cannot know the future of this world. -Therefore, creative power isn’t equivalent to omnipotence.

    +Therefore, creative power isn’t equivalent to omnipotence.

    @@ -266,7 +269,7 @@ Therefore, creative power isn’t equivalent to omnipotence.

    After all this, it becomes difficult to know what we can believe. But it would be another error to throw away all our knowledge. -In a future next part, I’ll explain what we can hope and what attitude we should have once we’ve realized most of truth are unaccessible.

    +In a future next part, I’ll explain what we can hope and what attitude we should have once we’ve realized most of truth are unaccessible.


    1. @@ -393,7 +396,7 @@ In a future next part, I’ll explain what we can hope and what attitude we
    Created: 08/11/2010 - Modified: 04/11/2012 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/2010-07-31-New-style-after-holidays/index.html b/output/Scratch/en/blog/2010-07-31-New-style-after-holidays/index.html index 9e3625ea2..f615d4943 100644 --- a/output/Scratch/en/blog/2010-07-31-New-style-after-holidays/index.html +++ b/output/Scratch/en/blog/2010-07-31-New-style-after-holidays/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + diff --git a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/.gems b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/.gems index 7c156f5cb..5e3736f7c 100644 --- a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/.gems +++ b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/.gems @@ -1,4 +1,3 @@ - rack rack-rewrite rack-contrib diff --git a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru index 32b16fc1f..48ae35298 100644 --- a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru +++ b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru @@ -1,4 +1,3 @@ - require 'rubygems' require 'rack' require 'rack/contrib' diff --git a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/index.html b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/index.html index 0e23ba24a..b8f4ac952 100644 --- a/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/index.html +++ b/output/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -69,73 +72,70 @@ But here is the conf to make it work on heroku.

    The root of my files is /output. You only need to create a config.ru1 file:

    -
    -
    -require 'rubygems'
    -require 'rack'
    -require 'rack/contrib'
    -require 'rack-rewrite'
    -require 'mime/types'
    +
     
    -use Rack::ETag
    -module ::Rack
    -    class TryStatic < Static
    +
    require 'rubygems'
    +require 'rack'
    +require 'rack/contrib'
    +require 'rack-rewrite'
    +require 'mime/types'
     
    -        def initialize(app, options)
    -            super
    -            @try = ([''] + Array(options.delete(:try)) + [''])
    -        end
    +use Rack::ETag
    +module ::Rack
    +    class TryStatic < Static
     
    -        def call(env)
    -            @next = 0
    -            while @next < @try.size && 404 == (resp = super(try_next(env)))[0] 
    -                @next += 1
    -            end
    -            404 == resp[0] ? @app.call : resp
    -        end
    +        def initialize(app, options)
    +            super
    +            @try = ([''] + Array(options.delete(:try)) + [''])
    +        end
     
    -        private
    -        def try_next(env)
    -            env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
    -        end
    +        def call(env)
    +            @next = 0
    +            while @next < @try.size && 404 == (resp = super(try_next(env)))[0] 
    +                @next += 1
    +            end
    +            404 == resp[0] ? @app.call : resp
    +        end
     
    -    end
    -end
    +        private
    +        def try_next(env)
    +            env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
    +        end
     
    -use Rack::TryStatic, 
    -    :root => "output",                              # static files root dir
    -    :urls => %w[/],                                 # match all requests 
    -    :try => ['.html', 'index.html', '/index.html']  # try these postfixes sequentially
    +    end
    +end
     
    -errorFile='output/Scratch/en/error/404-not_found/index.html'
    -run lambda { [404, {
    -                "Last-Modified"  => File.mtime(errorFile).httpdate,
    -                "Content-Type"   => "text/html",
    -                "Content-Length" => File.size(errorFile).to_s
    -            }, File.read(errorFile)] }
    -
    -
    +use Rack::TryStatic, + :root => "output", # static files root dir + :urls => %w[/], # match all requests + :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially + +errorFile='output/Scratch/en/error/404-not_found/index.html' +run lambda { [404, { + "Last-Modified" => File.mtime(errorFile).httpdate, + "Content-Type" => "text/html", + "Content-Length" => File.size(errorFile).to_s + }, File.read(errorFile)] } +

    and the .gems file needed to install rack middlewares.

    -
    -
    -rack
    -rack-rewrite
    -rack-contrib
    -
    -
    + + +
    rack
    +rack-rewrite
    +rack-contrib
    +

    Now, just follow the heroku tutorial to create an application :

    -
    -git init
    +
    git init
     git add .
     heroku create
     git push heroku master
    -
    +
    -

    Now I’ll should be able to redirect properly to my own 404 page for example. +

    Now I’ll should be able to redirect properly to my own 404 page for example. I hope it is helpful.


      diff --git a/output/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html b/output/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html index 5e99ba880..b5aa04d32 100644 --- a/output/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html +++ b/output/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -55,19 +58,18 @@

      I had to send a mail using only command line. -I was surprised it isn’t straightforward at all. -I didn’t had pine nor mutt or anything like that. +I was surprised it isn’t straightforward at all. +I didn’t had pine nor mutt or anything like that. Just mail and mailx.

      What Internet say (via google) is

      -
      -uuencode fic.jpg fic.jpg | mail -s 'Subject'
      -
      +
      uuencode fic.jpg fic.jpg | mail -s 'Subject'
      +

      I tried it. And it works almost each times. -But for my file, it didn’t worked. +But for my file, it didn’t worked. I compressed it to .gz, .bz2 and .zip. Using .bz2 format it worked nicely, but not with other formats. Instead of having an attached file I saw this in my email.

      @@ -87,12 +89,11 @@ After some research I found the solution. Use MIME instead of uuencode.

      Finally I made it manually using sendmail. -I didn’t dare to use telnet. +I didn’t dare to use telnet. The command to use is:

      -
      -sendmail -t -oi < mailcontent.txt
      -
      +
      sendmail -t -oi < mailcontent.txt
      +

      Of course you need to create the mailcontent.txt file. It should contains:

      @@ -118,7 +119,7 @@ H4sICB6Ke0wAA2Rjcl93aXRob3V0X2tleXdvcmQuY3N2ANSdW5ubOJPH7/e7 ... -

      And to obtain the “encoded” file in base64 I used:

      +

      And to obtain the “encoded” file in base64 I used:

      uuencode -m fic.jpg fic.jpg diff --git a/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb b/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb index 0617e0637..7aeec2be6 100644 --- a/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb +++ b/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb @@ -1,4 +1,3 @@ - def gitmtime filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'') ext=%{.#{@item[:extension]}} diff --git a/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html b/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html index 4c948371e..a960093c7 100644 --- a/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html +++ b/output/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -57,30 +60,29 @@

      You can remark at the bottom of each page I provide a last modification date. This label was first calculated using the mtime of the file on the file system. But many times I modify this date just to force some recompilation. -Therefore the date wasn’t a date of real modification.

      +Therefore the date wasn’t a date of real modification.

      I use git to version my website. And fortunately I can know the last date of real change of a file. This is how I do this with nanoc:

      -
      -
      -def gitmtime
      -    filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'')
      -    ext=%{.#{@item[:extension]}}
      -    filepath<<=ext
      -    if not FileTest.exists?(filepath)
      -        filepath.sub!(ext,%{#{@item.raw_filename}#{ext}})
      -    end
      -    str=`git log -1 --format='%ci' -- #{filepath}`
      -    if str.nil? or str.empty?
      -        return Time.now
      -    else
      -        return DateTime.parse( str )
      -    end
      -end
      -
      -
      + + +
      def gitmtime
      +    filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'')
      +    ext=%{.#{@item[:extension]}}
      +    filepath<<=ext
      +    if not FileTest.exists?(filepath)
      +        filepath.sub!(ext,%{#{@item.raw_filename}#{ext}})
      +    end
      +    str=`git log -1 --format='%ci' -- #{filepath}`
      +    if str.nil? or str.empty?
      +        return Time.now
      +    else
      +        return DateTime.parse( str )
      +    end
      +end
      +

      Of course I know it is really slow and absolutely not optimized. But it works as expected. diff --git a/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c b/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c index 26c5a31f0..cd9879240 100644 --- a/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c +++ b/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c @@ -1,5 +1,4 @@ - - (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result { char *c_baseString=(char *)[baseString UTF8String]; CC_SHA1(c_baseString, strlen(c_baseString), result); diff --git a/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html b/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html index d3a8db3bf..79188889e 100644 --- a/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html +++ b/output/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,62 +62,61 @@ here are two functions to add to your code to have base64 and To use it, simply copy the code in your class and use as this:

      -
      -#import <CommonCrypto/CommonDigest.h>
      +
      #import <CommonCrypto/CommonDigest.h>
       ...
      -NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"];
      +NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"];
       ...
      -NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"];
      -
      +NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"]; +

      The base64 algorithm must be programmed by hand on iPhone!

      -
      -
      +
       
      -- (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result {
      -    char *c_baseString=(char *)[baseString UTF8String];
      -    CC_SHA1(c_baseString, strlen(c_baseString), result);
      -    return result;
      +
      
      +- (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result {
      +    char *c_baseString=(char *)[baseString UTF8String];
      +    CC_SHA1(c_baseString, strlen(c_baseString), result);
      +    return result;
       }
       
      -- (NSString *)base64:(unsigned char *)result {
      +- (NSString *)base64:(unsigned char *)result {
           NSString *password=[[NSString alloc] init];
      -    static const unsigned char cb64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      -    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i+=3) {
      -        password=[password stringByAppendingFormat:@"%c%c%c%c",
      -            cb64[(result[i] &0xFC)>>2],
      -            cb64[((result[i] & 0x03) << 4)
      -                | ((result[i + 1] & 0xF0) >> 4)],
      -            cb64[((result[i + 1] & 0x0F) << 2)
      -                | ((result[i + 2] & 0xC0) >> 6)],
      -            cb64[result[i+2]&0x3F]
      +    static const unsigned char cb64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      +    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i+=3) {
      +        password=[password stringByAppendingFormat:@"%c%c%c%c",
      +            cb64[(result[i] &0xFC)>>2],
      +            cb64[((result[i] & 0x03) << 4)
      +                | ((result[i + 1] & 0xF0) >> 4)],
      +            cb64[((result[i + 1] & 0x0F) << 2)
      +                | ((result[i + 2] & 0xC0) >> 6)],
      +            cb64[result[i+2]&0x3F]
                       ];            
           }
      -    return password;
      +    return password;
       }
       
      -- (NSString *)hexadecimalRepresentation:(unsigned char *)result {
      +- (NSString *)hexadecimalRepresentation:(unsigned char *)result {
           NSString *password=[[NSString alloc] init];
      -    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
      -        password=[password stringByAppendingFormat:@"%02x", result[i]];
      +    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
      +        password=[password stringByAppendingFormat:@"%02x", result[i]];
           }
      -    return password;
      +    return password;
       }
       
       - (NSString *)b64_sha1:(NSString *)inputString {
      -    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
      +    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
           [self sha1:inputString result:result];
      -    return [self base64:result];
      +    return [self base64:result];
       }
       
       - (NSString *)hex_sha1:(NSString *)inputString {
      -    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
      +    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
           [self sha1:inputString result:result];
      -    return [self hexadecimalRepresentation:result];
      +    return [self hexadecimalRepresentation:result];
       }
      -
      -
      + +
      diff --git a/output/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/index.html b/output/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/index.html index b34cd40f7..e1030c3df 100644 --- a/output/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/index.html +++ b/output/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -64,7 +67,7 @@ But the major problem came from, font-shadow and gradients. Then my new design obey to the following rules:

        -
      • no CSS element begining by ‘-moz’ or ‘-webkit’, etc…,
      • +
      • no CSS element begining by ‘-moz’ or ‘-webkit’, etc…,
      • no text shadow,
      • clean (I mean delete) most javascript.
      diff --git a/output/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html b/output/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html index 94e1a7e90..6a0183878 100644 --- a/output/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html +++ b/output/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,7 +57,7 @@
      -

      Title image

      +

      Title image

      @@ -75,10 +78,10 @@
      -

      I’ve (re)discovered how to become S/MIME compliant. +

      I’ve (re)discovered how to become S/MIME compliant. I am now suprised how easy it was. Some years ago it was far more difficult. -Now I’m able to sign and encrypt my emails.

      +Now I’m able to sign and encrypt my emails.

      Why is it important?

      @@ -214,7 +217,7 @@ Now you should see these icons:
      Created: 10/10/2010 - Modified: 10/10/2010 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.c b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.c index 214ab6ee5..aa3982825 100644 --- a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.c +++ b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.c @@ -1,4 +1,3 @@ - #include #include #include diff --git a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.py b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.py index ddeedd473..c0d400125 100644 --- a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.py +++ b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python from struct import calcsize, unpack from sys import argv, exit diff --git a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.rb b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.rb index fc63899b5..aa3c90678 100644 --- a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.rb +++ b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.rb @@ -1,4 +1,3 @@ - data = ARGF.read keys = %w[id totallength wavefmt format pcm channels frequency bytes_per_second diff --git a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum2.c b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum2.c index 0f9850943..48b713868 100644 --- a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum2.c +++ b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum2.c @@ -1,4 +1,3 @@ - #include #include #include // for memcmp diff --git a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/index.html b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/index.html index 1ad154d46..3a5fab6f4 100644 --- a/output/Scratch/en/blog/2010-10-14-Fun-with-wav/index.html +++ b/output/Scratch/en/blog/2010-10-14-Fun-with-wav/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,7 +63,7 @@

      tl;dr: Played to process a wav file. C was easier and cleaner than Ruby.

      -

      edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didn’t had any portability consideration. This is only a hack.

      +

      edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didn’t had any portability consideration. This is only a hack.

      @@ -69,7 +72,7 @@

      I had to compute the sum of the absolute values of data of a .wav file. For efficiency (and fun) reasons, I had chosen C language.

      -

      I didn’t programmed in C for a long time. +

      I didn’t programmed in C for a long time. From my memory it was a pain to read and write to files. But in the end I was really impressed by the code I get. It was really clean. @@ -81,113 +84,109 @@ The header is then a block of packed bytes.

      • The 4th first bytes must contains RIFF in ASCII,
      • -
      • the following 4th Bytes is an 32 bits integer giving the size of the file minus 8, etc…
      • +
      • the following 4th Bytes is an 32 bits integer giving the size of the file minus 8, etc…

      Surprisingly, I believe that reading this kind of file is easier in C than in most higher level language. Proof: I only have to search on the web the complete header format and write it in a struct.

      -
      -struct wavfile
      +
      struct wavfile
       {
      -    char        id[4];          // should always contain "RIFF"
      -    int     totallength;    // total file length minus 8
      -    char        wavefmt[8];     // should be "WAVEfmt "
      -    int     format;         // 16 for PCM format
      -    short     pcm;            // 1 for PCM format
      -    short     channels;       // channels
      -    int     frequency;      // sampling frequency
      -    int     bytes_per_second;
      -    short     bytes_by_capture;
      -    short     bits_per_sample;
      -    char        data[4];        // should always contain "data"
      -    int     bytes_in_data;
      +    char        id[4];          // should always contain "RIFF"
      +    int     totallength;    // total file length minus 8
      +    char        wavefmt[8];     // should be "WAVEfmt "
      +    int     format;         // 16 for PCM format
      +    short     pcm;            // 1 for PCM format
      +    short     channels;       // channels
      +    int     frequency;      // sampling frequency
      +    int     bytes_per_second;
      +    short     bytes_by_capture;
      +    short     bits_per_sample;
      +    char        data[4];        // should always contain "data"
      +    int     bytes_in_data;
       };
      -
      +

      To read this kind of data in Ruby, I certainly had to write a block of code for each element in the struct. But in C I simply written:

      -
      -fread(&header,sizeof(header),1,wav)
      -
      +
      fread(&header,sizeof(header),1,wav)
      +

      Only one step to fill my data structure. Magic!

      Then, get an int value coded on two Bytes is also not a natural operation for high level language. In C, to read a sequence of 2 Bytes numbers I only had to write:

      -
      -short value=0;
      -while( fread(&value,sizeof(value),1,wav) ) {
      -    // do something with value
      +
      short value=0;
      +while( fread(&value,sizeof(value),1,wav) ) {
      +    // do something with value
       }
      -
      +

      Finally I ended with the following code. Remark I know the wav format (16 bit / 48000Hz):

      -
      -
      -#include <stdio.h>
      -#include <stdlib.h>
      -#include <stdint.h>
      +
       
      -struct wavfile
      +
      #include <stdio.h>
      +#include <stdlib.h>
      +#include <stdint.h>
      +
      +struct wavfile
       {
      -    char        id[4];          // should always contain "RIFF"
      -    int     totallength;    // total file length minus 8
      -    char        wavefmt[8];     // should be "WAVEfmt "
      -    int     format;         // 16 for PCM format
      -    short     pcm;            // 1 for PCM format
      -    short     channels;       // channels
      -    int     frequency;      // sampling frequency
      -    int     bytes_per_second;
      -    short     bytes_by_capture;
      -    short     bits_per_sample;
      -    char        data[4];        // should always contain "data"
      -    int     bytes_in_data;
      +    char        id[4];          // should always contain "RIFF"
      +    int     totallength;    // total file length minus 8
      +    char        wavefmt[8];     // should be "WAVEfmt "
      +    int     format;         // 16 for PCM format
      +    short     pcm;            // 1 for PCM format
      +    short     channels;       // channels
      +    int     frequency;      // sampling frequency
      +    int     bytes_per_second;
      +    short     bytes_by_capture;
      +    short     bits_per_sample;
      +    char        data[4];        // should always contain "data"
      +    int     bytes_in_data;
       };
       
      -int main(int argc, char *argv[]) {
      -    char *filename=argv[1];
      -    FILE *wav = fopen(filename,"rb");
      -    struct wavfile header;
      +int main(int argc, char *argv[]) {
      +    char *filename=argv[1];
      +    FILE *wav = fopen(filename,"rb");
      +    struct wavfile header;
       
      -    if ( wav == NULL ) {
      -        fprintf(stderr,"Can't open input file %s", filename);
      -        exit(1);
      +    if ( wav == NULL ) {
      +        fprintf(stderr,"Can't open input file %s", filename);
      +        exit(1);
           }
       
       
      -    // read header
      -    if ( fread(&header,sizeof(header),1,wav) < 1 )
      +    // read header
      +    if ( fread(&header,sizeof(header),1,wav) < 1 )
           {
      -        fprintf(stderr,"Can't read file header\n");
      -        exit(1);
      +        fprintf(stderr,"Can't read file header\n");
      +        exit(1);
           }
      -    if (    header.id[0] != 'R'
      -         || header.id[1] != 'I' 
      -         || header.id[2] != 'F' 
      -         || header.id[3] != 'F' ) { 
      -        fprintf(stderr,"ERROR: Not wav format\n"); 
      -        exit(1); 
      +    if (    header.id[0] != 'R'
      +         || header.id[1] != 'I' 
      +         || header.id[2] != 'F' 
      +         || header.id[3] != 'F' ) { 
      +        fprintf(stderr,"ERROR: Not wav format\n"); 
      +        exit(1); 
           }
       
      -    fprintf(stderr,"wav format\n");
      +    fprintf(stderr,"wav format\n");
       
      -    // read data
      -    long sum=0;
      -    short value=0;
      -    while( fread(&value,sizeof(value),1,wav) ) {
      -        // fprintf(stderr,"%d\n", value);
      -        if (value<0) { value=-value; }
      +    // read data
      +    long sum=0;
      +    short value=0;
      +    while( fread(&value,sizeof(value),1,wav) ) {
      +        // fprintf(stderr,"%d\n", value);
      +        if (value<0) { value=-value; }
               sum += value;
           }
      -    printf("%ld\n",sum);
      -    exit(0);
      +    printf("%ld\n",sum);
      +    exit(0);
       }
      -
      -
      +

      Of course it is only a hack. But we can see how easy and clean it should be to improve. @@ -207,185 +206,182 @@ But I must confess this task was a bit tedious. The code remain as readable as before. But I had to use some compiler specific declaration to force the structure to be packed:

      -
      -__attribute__((__packed__))
      -
      +
      __attribute__((__packed__))
      +

      Therefore this implementation should for big and little endian architecture. However, it must be compiled with gcc. -The new code make more tests but still don’t use mmap. +The new code make more tests but still don’t use mmap. Here it is:

      -
      -
      -#include <stdio.h>
      -#include <stdlib.h>
      -#include <string.h> // for memcmp
      -#include <stdint.h> // for int16_t and int32_t
      +
       
      -struct wavfile
      +
      #include <stdio.h>
      +#include <stdlib.h>
      +#include <string.h> // for memcmp
      +#include <stdint.h> // for int16_t and int32_t
      +
      +struct wavfile
       {
      -    char    id[4];          // should always contain "RIFF"
      -    int32_t totallength;    // total file length minus 8
      -    char    wavefmt[8];     // should be "WAVEfmt "
      -    int32_t format;         // 16 for PCM format
      -    int16_t pcm;            // 1 for PCM format
      -    int16_t channels;       // channels
      -    int32_t frequency;      // sampling frequency
      -    int32_t bytes_per_second;
      -    int16_t bytes_by_capture;
      -    int16_t bits_per_sample;
      -    char    data[4];        // should always contain "data"
      -    int32_t bytes_in_data;
      +    char    id[4];          // should always contain "RIFF"
      +    int32_t totallength;    // total file length minus 8
      +    char    wavefmt[8];     // should be "WAVEfmt "
      +    int32_t format;         // 16 for PCM format
      +    int16_t pcm;            // 1 for PCM format
      +    int16_t channels;       // channels
      +    int32_t frequency;      // sampling frequency
      +    int32_t bytes_per_second;
      +    int16_t bytes_by_capture;
      +    int16_t bits_per_sample;
      +    char    data[4];        // should always contain "data"
      +    int32_t bytes_in_data;
       } __attribute__((__packed__));
       
      -int is_big_endian(void) {
      -    union {
      -        uint32_t i;
      -        char c[4];
      -    } bint = {0x01000000};
      -    return bint.c[0]==1;
      +int is_big_endian(void) {
      +    union {
      +        uint32_t i;
      +        char c[4];
      +    } bint = {0x01000000};
      +    return bint.c[0]==1;
       }
       
      -int main(int argc, char *argv[]) {
      -    char *filename=argv[1];
      -    FILE *wav = fopen(filename,"rb");
      -    struct wavfile header;
      +int main(int argc, char *argv[]) {
      +    char *filename=argv[1];
      +    FILE *wav = fopen(filename,"rb");
      +    struct wavfile header;
       
      -    if ( wav == NULL ) {
      -        fprintf(stderr,"Can't open input file %s\n", filename);
      -        exit(1);
      +    if ( wav == NULL ) {
      +        fprintf(stderr,"Can't open input file %s\n", filename);
      +        exit(1);
           }
       
       
      -    // read header
      -    if ( fread(&header,sizeof(header),1,wav) < 1 ) {
      -        fprintf(stderr,"Can't read input file header %s\n", filename);
      -        exit(1);
      +    // read header
      +    if ( fread(&header,sizeof(header),1,wav) < 1 ) {
      +        fprintf(stderr,"Can't read input file header %s\n", filename);
      +        exit(1);
           }
       
      -    // if wav file isn't the same endianness than the current environment
      -    // we quit
      -    if ( is_big_endian() ) {
      -        if (   memcmp( header.id,"RIFX", 4) != 0 ) {
      -            fprintf(stderr,"ERROR: %s is not a big endian wav file\n", filename); 
      -            exit(1);
      +    // if wav file isn't the same endianness than the current environment
      +    // we quit
      +    if ( is_big_endian() ) {
      +        if (   memcmp( header.id,"RIFX", 4) != 0 ) {
      +            fprintf(stderr,"ERROR: %s is not a big endian wav file\n", filename); 
      +            exit(1);
               }
      -    } else {
      -        if (   memcmp( header.id,"RIFF", 4) != 0 ) {
      -            fprintf(stderr,"ERROR: %s is not a little endian wav file\n", filename); 
      -            exit(1);
      +    } else {
      +        if (   memcmp( header.id,"RIFF", 4) != 0 ) {
      +            fprintf(stderr,"ERROR: %s is not a little endian wav file\n", filename); 
      +            exit(1);
               }
           }
       
      -    if (   memcmp( header.wavefmt, "WAVEfmt ", 8) != 0 
      -        || memcmp( header.data, "data", 4) != 0 
      +    if (   memcmp( header.wavefmt, "WAVEfmt ", 8) != 0 
      +        || memcmp( header.data, "data", 4) != 0 
                   ) {
      -        fprintf(stderr,"ERROR: Not wav format\n"); 
      -        exit(1); 
      +        fprintf(stderr,"ERROR: Not wav format\n"); 
      +        exit(1); 
           }
      -    if (header.format != 16) {
      -        fprintf(stderr,"\nERROR: not 16 bit wav format.");
      -        exit(1);
      +    if (header.format != 16) {
      +        fprintf(stderr,"\nERROR: not 16 bit wav format.");
      +        exit(1);
           }
      -    fprintf(stderr,"format: %d bits", header.format);
      -    if (header.format == 16) {
      -        fprintf(stderr,", PCM");
      -    } else {
      -        fprintf(stderr,", not PCM (%d)", header.format);
      +    fprintf(stderr,"format: %d bits", header.format);
      +    if (header.format == 16) {
      +        fprintf(stderr,", PCM");
      +    } else {
      +        fprintf(stderr,", not PCM (%d)", header.format);
           }
      -    if (header.pcm == 1) {
      -        fprintf(stderr, " uncompressed" );
      -    } else {
      -        fprintf(stderr, " compressed" );
      +    if (header.pcm == 1) {
      +        fprintf(stderr, " uncompressed" );
      +    } else {
      +        fprintf(stderr, " compressed" );
           }
      -    fprintf(stderr,", channel %d", header.pcm);
      -    fprintf(stderr,", freq %d", header.frequency );
      -    fprintf(stderr,", %d bytes per sec", header.bytes_per_second );
      -    fprintf(stderr,", %d bytes by capture", header.bytes_by_capture );
      -    fprintf(stderr,", %d bits per sample", header.bytes_by_capture );
      -    fprintf(stderr,"\n" );
      +    fprintf(stderr,", channel %d", header.pcm);
      +    fprintf(stderr,", freq %d", header.frequency );
      +    fprintf(stderr,", %d bytes per sec", header.bytes_per_second );
      +    fprintf(stderr,", %d bytes by capture", header.bytes_by_capture );
      +    fprintf(stderr,", %d bits per sample", header.bytes_by_capture );
      +    fprintf(stderr,"\n" );
       
      -    if ( memcmp( header.data, "data", 4) != 0 ) { 
      -        fprintf(stderr,"ERROR: Prrroblem?\n"); 
      -        exit(1); 
      +    if ( memcmp( header.data, "data", 4) != 0 ) { 
      +        fprintf(stderr,"ERROR: Prrroblem?\n"); 
      +        exit(1); 
           }
      -    fprintf(stderr,"wav format\n");
      +    fprintf(stderr,"wav format\n");
       
      -    // read data
      -    long long sum=0;
      -    int16_t value;
      -    int i=0;
      -    fprintf(stderr,"---\n", value);
      -    while( fread(&value,sizeof(value),1,wav) ) {
      -        if (value<0) { value=-value; }
      +    // read data
      +    long long sum=0;
      +    int16_t value;
      +    int i=0;
      +    fprintf(stderr,"---\n", value);
      +    while( fread(&value,sizeof(value),1,wav) ) {
      +        if (value<0) { value=-value; }
               sum += value;
           }
      -    printf("%lld\n",sum);
      -    exit(0);
      +    printf("%lld\n",sum);
      +    exit(0);
       }
      -
      -
      +

      Edit(3): On reddit Bogdanp proposed a Python version:

      -
      -
      -#!/usr/bin/env python
      -from struct import calcsize, unpack
      -from sys import argv, exit
      +
       
      -def word_iter(f):
      -    while True:
      -        _bytes = f.read(2)
      +
      #!/usr/bin/env python
      +from struct import calcsize, unpack
      +from sys import argv, exit
       
      -    if len(_bytes) != 2:
      -        raise StopIteration
      +def word_iter(f):
      +    while True:
      +        _bytes = f.read(2)
       
      -    yield unpack("=h", _bytes)[0]
      +    if len(_bytes) != 2:
      +        raise StopIteration
       
      -try:
      -    with open(argv[1], "rb") as f:
      -        wav = "=4ci8cihhiihh4ci"
      -        wav_size = calcsize(wav)
      -        metadata = unpack(wav, f.read(wav_size))
      +    yield unpack("=h", _bytes)[0]
       
      -        if "".join(metadata[:4]) != "RIFF":
      -            print "error: not wav file."
      -            exit(1)
      +try:
      +    with open(argv[1], "rb") as f:
      +        wav = "=4ci8cihhiihh4ci"
      +        wav_size = calcsize(wav)
      +        metadata = unpack(wav, f.read(wav_size))
       
      -        print sum(abs(word) for word in word_iter(f))
      -except IOError:
      -    print "error: can't open input file '%s'." % argv[1]
      -    exit(1)
      -
      -
      + if "".join(metadata[:4]) != "RIFF": + print "error: not wav file." + exit(1) + + print sum(abs(word) for word in word_iter(f)) +except IOError: + print "error: can't open input file '%s'." % argv[1] + exit(1) +

      and luikore proposed an impressive Ruby version:

      -
      -
      -data = ARGF.read
      - keys = %w[id totallength wavefmt format
      -       pcm channels frequency bytes_per_second
      -         bytes_by_capture bits_per_sample
      -           data bytes_in_data sum
      - ]
      - values = data.unpack 'Z4 i Z8 i s s i i s s Z4 i s*'
      - sum = values.drop(12).map(&:abs).inject(:+)
      - keys.zip(values.take(12) << sum) {|k, v|
      -       puts "#{k.ljust 17}: #{v}"
      +
      +
      +
      data = ARGF.read
      + keys = %w[id totallength wavefmt format
      +       pcm channels frequency bytes_per_second
      +         bytes_by_capture bits_per_sample
      +           data bytes_in_data sum
      + ]
      + values = data.unpack 'Z4 i Z8 i s s i i s s Z4 i s*'
      + sum = values.drop(12).map(&:abs).inject(:+)
      + keys.zip(values.take(12) << sum) {|k, v|
      +       puts "#{k.ljust 17}: #{v}"
        }
      -
      -
      + +
      diff --git a/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb b/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb index 16c0e6d86..17ebf5524 100644 --- a/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb +++ b/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb @@ -1,4 +1,3 @@ - # usage: # --- # ... diff --git a/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html b/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html index 9412b18de..791b5656e 100644 --- a/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html +++ b/output/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -69,11 +72,10 @@ When we are used to L -macros: - test: "This is a macro test" - latex: '<span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>' - +
      macros:
      +  test: "This is a macro test"
      +  latex: '<span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>'
      +

      In the body it will replace every occurrence of:

      @@ -85,53 +87,52 @@ In the header of my files I simply write:

      The source code is really simple. For nanoc user, simply put this file in your lib directory.

      -
      -
      -# usage:
      -# ---
      -# ...
      -# macros:
      -#   test: "passed test"
      -# ---
      -# ...
      -# Here is a This is a macro test.
      -#
      -class Macros < Nanoc3::Filter
      -    identifier :falacy
      -    attr_accessor :macro
      -    def initialize(arg)
      -        super
      -        @macro={}
      -        @macro[:tlal] = %{<span class="sc"><abbr title="Trop long à lire">tlàl</abbr> : </span>}
      -        @macro[:tldr] = %{<span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span>}
      -        if @item.nil?
      -            if not arg.nil?
      -                @macro.merge!( arg )
      -            end
      -        else
      -            if not @item[:macros].nil?
      -                @macro.merge!( @item[:macros] )
      -            end
      -        end
      -    end
      -    def macro_value_for(macro_name)
      -        if macro_name.nil? or macro_name=="" or @macro[macro_name.intern].nil?
      -            return %{%#{macro_name}} 
      -        end
      -        return @macro[macro_name.intern]
      -    end
      -    def run(content, params={})
      -        content.gsub(/%(\w*)/) do |m| 
      -            if m != '%'
      -                macro_value_for($1)
      -            else
      +
      +
      +
      # usage:
      +# ---
      +# ...
      +# macros:
      +#   test: "passed test"
      +# ---
      +# ...
      +# Here is a This is a macro test.
      +#
      +class Macros < Nanoc3::Filter
      +    identifier :falacy
      +    attr_accessor :macro
      +    def initialize(arg)
      +        super
      +        @macro={}
      +        @macro[:tlal] = %{<span class="sc"><abbr title="Trop long à lire">tlàl</abbr> : </span>}
      +        @macro[:tldr] = %{<span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span>}
      +        if @item.nil?
      +            if not arg.nil?
      +                @macro.merge!( arg )
      +            end
      +        else
      +            if not @item[:macros].nil?
      +                @macro.merge!( @item[:macros] )
      +            end
      +        end
      +    end
      +    def macro_value_for(macro_name)
      +        if macro_name.nil? or macro_name=="" or @macro[macro_name.intern].nil?
      +            return %{%#{macro_name}} 
      +        end
      +        return @macro[macro_name.intern]
      +    end
      +    def run(content, params={})
      +        content.gsub(/%(\w*)/) do |m| 
      +            if m != '%'
      +                macro_value_for($1)
      +            else
                       m
      -            end
      -        end
      -    end
      -end
      -
      -
      + end + end + end +end +

      Macros could be very useful, read this article for example.

      diff --git a/output/Scratch/en/blog/2011-01-03-Happy-New-Year/index.html b/output/Scratch/en/blog/2011-01-03-Happy-New-Year/index.html index 1137618e7..eaea9542c 100644 --- a/output/Scratch/en/blog/2011-01-03-Happy-New-Year/index.html +++ b/output/Scratch/en/blog/2011-01-03-Happy-New-Year/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + diff --git a/output/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html b/output/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html index 95269d836..eefc126ab 100644 --- a/output/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html +++ b/output/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,7 +65,7 @@

      Update: I might change my mind now. Why? I just discovered a js2coffee converter. Furthermore Denis Knauf told me about a CoffeeScript.eval function. -And as Denis said: “it is time to use Coffeescript as a javascript with Ruby-like syntax not a Ruby-like programming language”.

      +And as Denis said: “it is time to use Coffeescript as a javascript with Ruby-like syntax not a Ruby-like programming language”.

    @@ -85,30 +88,30 @@ Recently I used a lot of javascript. After trying Cappuccino, looking at backbone.js & javascriptMVC, -I’ve finally decided to make my own minimal javascript MVC framework.1

    +I’ve finally decided to make my own minimal javascript MVC framework.1

    I had to fight the horrible syntax of javascript. It was like experiencing a back-in-time travel:

    • Verbose Java-like syntax,
    • Strange and insanely Verbose Object Oriented Programming,
    • -
    • No easy way to refer to current instance of a class (this doesn’t work really well),
    • -
    • etc…
    • +
    • No easy way to refer to current instance of a class (this doesn’t work really well),
    • +
    • etc…

    It was so annoying at a point, I had thinked about creating my own CoffeeScript.

    -

    I’d finished a first draft of my MVC javascript framework. +

    I’d finished a first draft of my MVC javascript framework. Just after I learned about the existence of CoffeeScript, I immediately created a new git branch to try it.

    Here is my experience:

      -
    1. I had to install node.js and use npm just to use CoffeeScript. It wasn’t a big deal but it wasn’t as straightfoward as I expected either.
    2. +
    3. I had to install node.js and use npm just to use CoffeeScript. It wasn’t a big deal but it wasn’t as straightfoward as I expected either.
    4. Existing javascript file are not coffee compatible. I had to translate them by hand. There were no script to help me in this process. - Thanks to vim, it wasn’t too hard to translate 90% of the javascript using some regexp. + Thanks to vim, it wasn’t too hard to translate 90% of the javascript using some regexp. The --watch option of coffee was also really helpful to help in the translation. But I had to write my own shell script in order to follow an entire directory tree.
    5. An unexpected event. I made some meta-programming in javascript using eval. But in order to work, the string in the eval must be written in pure javascript not in coffee. It was like writing in two different languages. Really not so good.
    6. @@ -141,7 +144,7 @@ Just after I learned about the existence of CoffeeScript, I immediately created

      The last two point were definitively really problematic for me.

      -

      But even if I’ll have to work alone, I certainly won’t use CoffeeScript either. +

      But even if I’ll have to work alone, I certainly won’t use CoffeeScript either. CoffeeScript is a third party and any of their update can break my code. I experienced this kind of situation many times, and it is very annoying. Far more than coding with a bad syntax.

      @@ -157,38 +160,36 @@ But I believe it would be a really hard task just to simulate the access of curr

      Typically @x translate into this.x. But the following code will not do what I should expect. Call the foo function of the current class.

      -
      --> 
      -class MyClass
      -  foo: ->
      -    alert('ok')
      +
      -> 
      +class MyClass
      +  foo: ->
      +    alert('ok')
       
      -  bar: ->
      -    $('#content').load( '/content.html', ( -> @foo(x) ) )
      -    # That won't call MyClass.foo
      -
      + bar: -> + $('#content').load( '/content.html', ( -> @foo(x) ) ) + # That won't call MyClass.foo +

      The only way to handle this is to make the following code:

      -
      --> 
      -class MyClass
      -  foo: ->
      -    alert('ok')
      +
      -> 
      +class MyClass
      +  foo: ->
      +    alert('ok')
       
      -  bar: ->
      -    self=this
      -    $('#content').load( '/content.html', ( -> self.foo(x) ) )
      -
      + bar: -> + self=this + $('#content').load( '/content.html', ( -> self.foo(x) ) ) +

      Knowing this, @ notation lose most of its interrest for me.


      1. -

        I know it may not be the best nor productive decision, but I’d like to start from scratch and understand how things works under the hood.

        +

        I know it may not be the best nor productive decision, but I’d like to start from scratch and understand how things works under the hood.

      2. -

        I know there is rb2js, but it doesn’t handle the problem I talk about.

        +

        I know there is rb2js, but it doesn’t handle the problem I talk about.

      @@ -308,7 +309,7 @@ But I believe it would be a really hard task just to simulate the access of curr
    Created: 01/03/2011 - Modified: 10/26/2011 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/2011-04-20-Now-hosted-on-github/index.html b/output/Scratch/en/blog/2011-04-20-Now-hosted-on-github/index.html index 118f77d0f..d65fedf3d 100644 --- a/output/Scratch/en/blog/2011-04-20-Now-hosted-on-github/index.html +++ b/output/Scratch/en/blog/2011-04-20-Now-hosted-on-github/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -171,7 +174,7 @@
    Created: 04/20/2011 - Modified: 04/20/2011 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/A-more-convenient-diff/code/ydiff b/output/Scratch/en/blog/A-more-convenient-diff/code/ydiff index ebd336f1a..90d6c8589 100644 --- a/output/Scratch/en/blog/A-more-convenient-diff/code/ydiff +++ b/output/Scratch/en/blog/A-more-convenient-diff/code/ydiff @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Load colors helpers diff --git a/output/Scratch/en/blog/A-more-convenient-diff/index.html b/output/Scratch/en/blog/A-more-convenient-diff/index.html index 430439e89..52992b8a9 100644 --- a/output/Scratch/en/blog/A-more-convenient-diff/index.html +++ b/output/Scratch/en/blog/A-more-convenient-diff/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,32 +63,32 @@

    Here is the script I use when I want to use human readable diff à la git.

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Load colors helpers
    -autoload -U colors && colors
    +
    #!/usr/bin/env zsh
    +
    +# Load colors helpers
    +autoload -U colors && colors
     
     function colorize_diff {
    -    while read line; do
    -    case ${line[0]} in
    -    +) print -n $fg[green];;
    -    -) print -n $fg[red];;
    -    @) # Display in cyan the @@ positions @@
    -       if [[ ${line[1]} = '@' ]]; then
    -           line=$(print $line | perl -pe 's#(\@\@[^\@]*\@\@)(.*)$#'$fg[cyan]'$1'$reset_color'$2#')
    -       fi;;
    +    while read line; do
    +    case ${line[0]} in
    +    +) print -n $fg[green];;
    +    -) print -n $fg[red];;
    +    @) # Display in cyan the @@ positions @@
    +       if [[ ${line[1]} = '@' ]]; then
    +           line=$(print $line | perl -pe 's#(\@\@[^\@]*\@\@)(.*)$#'$fg[cyan]'$1'$reset_color'$2#')
    +       fi;;
     
    -    esac
    -        print -- $line
    -        print -n $reset_color
    -        done
    +    esac
    +        print -- $line
    +        print -n $reset_color
    +        done
     }
     
    -diff -u $* | colorize_diff
    -
    -
    +diff -u $* | colorize_diff + +
    diff --git a/output/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs b/output/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs index 6483d21b2..6a817486a 100644 --- a/output/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs +++ b/output/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs @@ -1,4 +1,3 @@ - a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003) newtype C = C (Double,Double) deriving (Show,Eq) instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0) diff --git a/output/Scratch/en/blog/Haskell-Mandelbrot/index.html b/output/Scratch/en/blog/Haskell-Mandelbrot/index.html index 432338dd8..7c6003e1d 100644 --- a/output/Scratch/en/blog/Haskell-Mandelbrot/index.html +++ b/output/Scratch/en/blog/Haskell-Mandelbrot/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,23 +59,21 @@

    Here is the obfuscated code:

    -
    -
    -a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003)
    -newtype C = C (Double,Double) deriving (Show,Eq)
    -instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0)
    -r(C(x,y))=x;i(C(x,y))=y
    -f c z 0=0;f c z n=if(r(abs(z))>2)then n else f c ((z*z)+c) (n-1)
    -h j k = map (\z->(f (C z) (C(0,0)) 32,(fst z>l - q/2))) [(x,y)|y<-[p,(p+((o-p)/a))..o],x<-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b
    -u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&$@#"!!i):rst p;rst True="\n";rst False=""
    -main = putStrLn $ im 0 where cl n (C (x,y))=let cs=(1.1**n-1) in C ((x+cs*(r e))/cs+1,(y+cs*(i e))/cs+1);bl n=cl n c;tr n=cl n d;im n=u (bl n) (tr n)++"\x1b[H\x1b[25A"++im (n+1)
    -
    -
    + -

    To launch it, you’ll need to have haskell installed and to run:

    +
    a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003)
    +newtype C = C (Double,Double) deriving (Show,Eq)
    +instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0)
    +r(C(x,y))=x;i(C(x,y))=y
    +f c z 0=0;f c z n=if(r(abs(z))>2)then n else f c ((z*z)+c) (n-1)
    +h j k = map (\z->(f (C z) (C(0,0)) 32,(fst z>l - q/2))) [(x,y)|y<-[p,(p+((o-p)/a))..o],x<-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b
    +u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&$@#"!!i):rst p;rst True="\n";rst False=""
    +main = putStrLn $ im 0 where cl n (C (x,y))=let cs=(1.1**n-1) in C ((x+cs*(r e))/cs+1,(y+cs*(i e))/cs+1);bl n=cl n c;tr n=cl n d;im n=u (bl n) (tr n)++"\x1b[H\x1b[25A"++im (n+1)
    +
    -
    ghc --make animandel.hs && animandel
    -
    +

    To launch it, you’ll need to have haskell installed and to run:

    + +
    ghc --make animandel.hs && animandel

    Here is some image after 50 iterations:

    @@ -107,37 +108,36 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$&&&&&&&&&&WWWW

    Here is the more readable version. I believe with this far more readable version, no more explanation is needed.

    -
    -nbvert = 30
    +
    nbvert = 30
     nbhor = 79
     zoomfactor = 1.01
    -init_bottom_left = C (-2.0,-2.0)
    -init_top_right   = C (3.0,2.0)
    -interrest        = C (-1.713,-0.000)
    +init_bottom_left = C (-2.0,-2.0)
    +init_top_right   = C (3.0,2.0)
    +interrest        = C (-1.713,-0.000)
     
    -newtype Complex = C (Float,Float) deriving (Show,Eq)
    -instance Num Complex where
    -    fromInteger n     = C (fromIntegral n,0.0)
    -    C (x,y) * C (z,t) = C (z*x - y*t, y*z + x*t)
    -    C (x,y) + C (z,t) = C (x+z, y+t)
    -    abs (C (x,y))     = C (sqrt (x*x + y*y),0.0)
    -    signum (C (x,y))  = C (signum x , 0.0)
    +newtype Complex = C (Float,Float) deriving (Show,Eq)
    +instance Num Complex where
    +    fromInteger n     = C (fromIntegral n,0.0)
    +    C (x,y) * C (z,t) = C (z*x - y*t, y*z + x*t)
    +    C (x,y) + C (z,t) = C (x+z, y+t)
    +    abs (C (x,y))     = C (sqrt (x*x + y*y),0.0)
    +    signum (C (x,y))  = C (signum x , 0.0)
     
    -real :: Complex -> Float
    -real (C (x,y))    = x
    -im :: Complex -> Float
    -im   (C (x,y))    = y
    +real :: Complex -> Float
    +real (C (x,y))    = x
    +im :: Complex -> Float
    +im   (C (x,y))    = y
     
    -cabs :: Complex -> Float
    -cabs = real.abs
    +cabs :: Complex -> Float
    +cabs = real.abs
     
    -f :: Complex -> Complex -> Int -> Int
    +f :: Complex -> Complex -> Int -> Int
     f c z 0 = 0
    -f c z n = if (cabs z > 2) then n else f c ((z*z)+c) (n-1) 
    +f c z n = if (cabs z > 2) then n else f c ((z*z)+c) (n-1) 
     
     
    -bmandel bottomleft topright = map (\z -> (f (C z) (C(0,0)) 32, (fst z > right - hstep/2 ))) [(x,y) | y <- [bottom,(bottom + vstep)..top], x<-[left,(left + hstep)..right]]
    -    where
    +bmandel bottomleft topright = map (\z -> (f (C z) (C(0,0)) 32, (fst z > right - hstep/2 ))) [(x,y) | y <- [bottom,(bottom + vstep)..top], x<-[left,(left + hstep)..right]]
    +    where
             top = im topright
             bottom = im bottomleft
             left = real bottomleft
    @@ -145,31 +145,32 @@ bmandel bottomleft topright = map (\z -> (f (mandel :: (Complex,Complex) -> String
    -mandel (bottomleft,topright) = concat $ map treat $ bmandel bottomleft topright
    -    where
    -        treat (i,jump) = " .,:;rcuowijlbCUOW&$@#" !! (div (i*22) 32):rst jump
    -        rst True = "\n"
    -        rst False = ""
    +mandel :: (Complex,Complex) -> String
    +mandel (bottomleft,topright) = concat $ map treat $ bmandel bottomleft topright
    +    where
    +        treat (i,jump) = " .,:;rcuowijlbCUOW&$@#" !! (div (i*22) 32):rst jump
    +        rst True = "\n"
    +        rst False = ""
     
    -cdiv :: Complex -> Float -> Complex
    -cdiv (C(x,y)) r = C(x/r, y/r) 
    -cmul :: Complex -> Float -> Complex
    -cmul (C(x,y)) r = C(x*r, y*r) 
    +cdiv :: Complex -> Float -> Complex
    +cdiv (C(x,y)) r = C(x/r, y/r) 
    +cmul :: Complex -> Float -> Complex
    +cmul (C(x,y)) r = C(x*r, y*r) 
     
    -zoom :: Complex -> Complex -> Complex -> Float -> (Complex,Complex)
    +zoom :: Complex -> Complex -> Complex -> Float -> (Complex,Complex)
     zoom bl tr center magn = (f bl, f tr)
    -    where
    -        f point = ((center `cmul` magn) + point ) `cdiv` (magn + 1)
    +    where
    +        f point = ((center `cmul` magn) + point ) `cdiv` (magn + 1)
         
     
    -main = do
    -    x <- getContents
    -    putStrLn $ infinitemandel 0
    -    where
    +main = do
    +    x <- getContents
    +    putStrLn $ infinitemandel 0
    +    where
             window n = zoom init_bottom_left init_top_right interrest (zoomfactor**n) 
    -        infinitemandel n = mandel (window n) ++ "\x1b[H\x1b[25A" ++ infinitemandel (n+1)
    -
    + infinitemandel n = mandel (window n) ++ "\x1b[H\x1b[25A" ++ infinitemandel (n+1) +
    +
    diff --git a/output/Scratch/en/blog/Haskell-the-Hard-Way/index.html b/output/Scratch/en/blog/Haskell-the-Hard-Way/index.html index 604e54d8d..041c45808 100644 --- a/output/Scratch/en/blog/Haskell-the-Hard-Way/index.html +++ b/output/Scratch/en/blog/Haskell-the-Hard-Way/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -77,7 +80,7 @@
  • Introduction
    • Install
    • -
    • Don’t be afraid
    • +
    • Don’t be afraid
    • Very basic Haskell
      • Function declaration
      • @@ -152,7 +155,7 @@

        I really believe all developer should learn Haskell. -I don’t think all should be super Haskell ninjas, +I don’t think all should be super Haskell ninjas, but at least, they should discover what Haskell has to offer. Learning Haskell open your mind.

        @@ -181,9 +184,9 @@ But I believe this is a good thing. It is because it is hard that Haskell is interesting.

        The conventional method to learning Haskell is to read two books. -First “Learn You a Haskell” and just after “Real World Haskell”. +First “Learn You a Haskell” and just after “Real World Haskell”. I also believe this is the right way to go. -But, to learn what Haskell is all about, you’ll have to read them in detail.

        +But, to learn what Haskell is all about, you’ll have to read them in detail.

        On the other hand, this article is a very brief and dense overview of all major aspects of Haskell. I also added some informations I lacked while I learned Haskell.

        @@ -215,7 +218,7 @@ I also added some informations I lacked while I learned Haskell.

      -

      Note: Each time you’ll see a separator with a filename ending in .lhs, +

      Note: Each time you’ll see a separator with a filename ending in .lhs, you could click the filename to get this file. If you save the file as filename.lhs, you can run it with

      @@ -255,76 +258,77 @@ You should see a link just below.

      The Scream

      -

      Many book/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc…). +

      Many book/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc…). I will do the exact opposite. -At first I won’t show you any Haskell super power. +At first I won’t show you any Haskell super power. I will start with similarities between Haskell and other programming languages. -Let’s jump to the mandatory “Hello World”.

      +Let’s jump to the mandatory “Hello World”.

      -
      -main = putStrLn "Hello World!"
      -
      + + +
      main = putStrLn "Hello World!"
      +
      + +

      To run it, you can save this code in a hello.hs and:

      -
      -~ runhaskell ./hello.hs
      -Hello World!
      -
      +
      ~ runhaskell ./hello.hs
      +Hello World!
      +

      You could also download the literate Haskell source. You should see a link just above the introduction title. Download this file as 00_hello_world.lhs and:

      -
      -~ runhaskell 00_hello_world.lhs
      -Hello World!
      -
      +
      ~ runhaskell 00_hello_world.lhs
      +Hello World!
      +

      01_basic/10_Introduction/00_hello_world.lhs


      01_basic/10_Introduction/10_hello_you.lhs

      -

      Now, a program asking your name and replying “Hello” using the name you entered:

      +

      Now, a program asking your name and replying “Hello” using the name you entered:

      -
      -main = do
      -    print "What is your name?"
      -    name <- getLine
      -    print ("Hello " ++ name ++ "!")
      -
      + + +
      main = do
      +    print "What is your name?"
      +    name <- getLine
      +    print ("Hello " ++ name ++ "!")
      +
      + +

      First, let us compare with a similar program in some imperative languages:

      -
      - # Python
      -print "What is your name?"
      -name = raw_input()
      -print "Hello %s!" % name
      -
      +
       # Python
      +print "What is your name?"
      +name = raw_input()
      +print "Hello %s!" % name
      +
      -
      - # Ruby
      -puts "What is your name?"
      -name = gets.chomp
      -puts "Hello #{name}!"
      -
      +
       # Ruby
      +puts "What is your name?"
      +name = gets.chomp
      +puts "Hello #{name}!"
      +
      -
      -// In C
      - #include <stdio.h>
      -int main (int argc, char **argv) {
      -    char name[666]; // <- An Evil Number!
      -    // What if my name is more than 665 character long?
      -    printf("What is your name?\n"); 
      -    scanf("%s", name);
      -    printf("Hello %s!\n", name);
      -    return 0;
      +
      // In C
      + #include <stdio.h>
      +int main (int argc, char **argv) {
      +    char name[666]; // <- An Evil Number!
      +    // What if my name is more than 665 character long?
      +    printf("What is your name?\n"); 
      +    scanf("%s", name);
      +    printf("Hello %s!\n", name);
      +    return 0;
       }
      -
      +

      The structure is the same, but there are some syntax differences. A major part of this tutorial will be dedicated to explaining why.

      @@ -349,7 +353,7 @@ This means, main will cause side effects.

      Functional

      Haskell is a functional language. -If you have an imperative language background, you’ll have to learn a lot of new things. +If you have an imperative language background, you’ll have to learn a lot of new things. Hopefully many of these new concepts will help you to program even in imperative languages.

      Smart Static Typing

      @@ -358,8 +362,8 @@ Hopefully many of these new concepts will help you to program even in imperative

      Purity

      -

      Generally your functions won’t modify anything in the outside world. -This means, it can’t modify the value of a variable, can’t get user input, can’t write on the screen, can’t launch a missile. +

      Generally your functions won’t modify anything in the outside world. +This means, it can’t modify the value of a variable, can’t get user input, can’t write on the screen, can’t launch a missile. On the other hand, parallelism will be very easy to achieve. Haskell makes it clear where effects occur and where you are pure. Also, it will be far easier to reason about your program. @@ -380,7 +384,7 @@ In consequence, it provides a very elegant way to manipulate infinite structures

      A last warning on how you should read Haskell code. For me, it is like reading scientific papers. Some parts are very clear, but when you see a formula, just focus and read slower. -Also, while learning Haskell, it really doesn’t matter much if you don’t understand syntax details. +Also, while learning Haskell, it really doesn’t matter much if you don’t understand syntax details. If you meet a >>=, <$>, <- or any other weird symbol, just ignore them and follows the flow of the code.

      Function declaration

      @@ -389,51 +393,45 @@ If you meet a >>=, <$>, <-

      In C:

      -
      -int f(int x, int y) {
      -    return x*x + y*y;
      +
      int f(int x, int y) {
      +    return x*x + y*y;
       }
      -
      +

      In Javascript:

      -
      -function f(x,y) {
      -    return x*x + y*y;
      +
      function f(x,y) {
      +    return x*x + y*y;
       }
      -
      +

      in Python:

      -
      -def f(x,y):
      -    return x*x + y*y
      -
      +
      def f(x,y):
      +    return x*x + y*y
      +

      in Ruby:

      -
      -def f(x,y)
      -    x*x + y*y
      -end
      -
      +
      def f(x,y)
      +    x*x + y*y
      +end
      +

      In Scheme:

      -
      -(define (f x y)
      -    (+ (* x x) (* y y)))
      -
      +
      (define (f x y)
      +    (+ (* x x) (* y y)))
      +

      Finally, the Haskell way is:

      -
      -f x y = x*x + y*y
      -
      +
      f x y = x*x + y*y
      +

      Very clean. No parenthesis, no def.

      -

      Don’t forget, Haskell uses functions and types a lot. +

      Don’t forget, Haskell uses functions and types a lot. It is thus very easy to define them. The syntax was particularly well thought for these objects.

      @@ -443,16 +441,19 @@ The syntax was particularly well thought for these objects.

      This is not mandatory. The compiler is smart enough to discover it for you.

      -

      Let’s play a little.

      +

      Let’s play a little.

      -
      --- We declare the type using ::
      -f :: Int -> Int -> Int
      +
      +
      +
      -- We declare the type using ::
      +f :: Int -> Int -> Int
       f x y = x*x + y*y
       
      -main = print (f 2 3)
      -
      +main = print (f 2 3) +
      + +
      ~ runhaskell 20_very_basic.lhs
       13
      @@ -466,12 +467,15 @@ main = print (f 2 3)
       

      Now try

      -
      -f :: Int -> Int -> Int
      +
      +
      +
      f :: Int -> Int -> Int
       f x y = x*x + y*y
       
      -main = print (f 2.3 4.2)
      -
      +main = print (f 2.3 4.2) +
      + +

      You get this error:

      @@ -484,7 +488,7 @@ main = print (f 2.3 4.2) In the expression: print (f 2.3 4.2)
      -

      The problem: 4.2 isn’t an Int.

      +

      The problem: 4.2 isn’t an Int.

      01_basic/10_Introduction/21_very_basic.lhs

      @@ -492,19 +496,22 @@ main = print (f 2.3 4.2)

      01_basic/10_Introduction/22_very_basic.lhs

      The solution, -don’t declare the type for f. +don’t declare the type for f. Haskell will infer the most general type for us:

      -
      -f x y = x*x + y*y
       
      -main = print (f 2.3 4.2)
      -
      + +
      f x y = x*x + y*y
      +
      +main = print (f 2.3 4.2)
      +
      + +

      It works! -Great, we don’t have to declare a new function for every single type. -For example, in C, you’ll have to declare a function for int, for float, for long, for double, etc…

      +Great, we don’t have to declare a new function for every single type. +For example, in C, you’ll have to declare a function for int, for float, for long, for double, etc…

      But, what type should we declare? To discover the type Haskell has found for us, just launch ghci:

      @@ -526,13 +533,13 @@ Prelude>
      let f x y = x*x + y*y
      Num a => a -> a -> a
       
      -

      First, let’s focus on the right part a -> a -> a. +

      First, let’s focus on the right part a -> a -> a. To understand it, just look at a list of progressive examples:

      - + @@ -565,13 +572,13 @@ To understand it, just look at a list of progressive examples:

      In the type a -> a -> a, the letter a is a type variable. It means f is a function with two arguments and both arguments and the result have the same type. The type variable a could take many different type value. -For example Int, Integer, Float

      +For example Int, Integer, Float

      -

      So instead of having a forced type like in C with declaring the function for int, long, float, double, etc… +

      So instead of having a forced type like in C with declaring the function for int, long, float, double, etc… We declare only one function like in a dynamically typed language.

      Generally a can be any type. -For example a String, an Int, but also more complex types, like Trees, other functions, etc… +For example a String, an Int, but also more complex types, like Trees, other functions, etc… But here our type is prefixed with Num a => .

      Num is a type class. @@ -625,12 +632,15 @@ It is time to make a real application.

      But just before that, we should verify the type system works as expected:

      -
      -f :: Num a => a -> a -> a
      +
      +
      +
      f :: Num a => a -> a -> a
       f x y = x*x + y*y
       
      -main = print (f 3 2.4)
      -
      +main = print (f 3 2.4) +
      + +

      It works, because, 3 is a valid representation both for Fractional numbers like Float and for Integer. As 2.4 is a Fractional number, 3 is then interpreted as being also a Fractional number.

      @@ -643,16 +653,19 @@ As 2.4 is a Fractional number, 3 is then interpreted a

      If we force our function to work with different types, it will fail:

      -
      -f :: Num a => a -> a -> a
      +
      +
      +
      f :: Num a => a -> a -> a
       f x y = x*x + y*y
       
      -x :: Int
      +x :: Int
       x = 3
      -y :: Float
      +y :: Float
       y = 2.4
      -main = print (f x y) -- won't work because type x ≠ type y
      -
      +main = print (f x y) -- won't work because type x ≠ type y +
      + +

      The compiler complains. The two parameters must have the same type.

      @@ -743,7 +756,7 @@ Data.Ratio> (11 % 15) * (5 % 3)

      Remark: -In real code you shouldn’t use list of char to represent text. +In real code you shouldn’t use list of char to represent text. You should mostly use Data.Text instead. If you want to represent stream of ASCII char, you should use Data.ByteString.

      @@ -799,36 +812,45 @@ f :: a -> b -> c ⇔ f is a function from a to (b→c) f :: (a -> b) -> c ⇔ f is a function from (a→b) to c -

      Defining the type of a function before its declaration isn’t mandatory. +

      Defining the type of a function before its declaration isn’t mandatory. Haskell infers the most general type for you. But it is considered a good practice to do so.

      Infix notation

      -
      -square :: Num a => a -> a  
      +
      +
      +
      square :: Num a => a -> a  
       square x = x^2
      -
      +
      + +

      Note ^ use infix notation. For each infix operator there its associated prefix notation. You just have to put it inside parenthesis.

      -
      -square' x = (^) x 2
      +
      +
      +
      square' x = (^) x 2
       
       square'' x = (^2) x
      -
      +
      + +

      We can remove x in the left and right side! -It’s called η-reduction.

      +It’s called η-reduction.

      -
      -square''' = (^2)
      -
      + + +
      square''' = (^2)
      +
      + +

      Note we can declare function with ' in their name. Here:

      @@ -842,10 +864,13 @@ Here:

      An implementation of the absolute function.

      -
      -absolute :: (Ord a, Num a) => a -> a
      -absolute x = if x >= 0 then x else -x
      -
      + + +
      absolute :: (Ord a, Num a) => a -> a
      +absolute x = if x >= 0 then x else -x
      +
      + +

      Note: the if .. then .. else Haskell notation is more like the ¤?¤:¤ C operator. You cannot forget the else.

      @@ -853,11 +878,14 @@ absolute x = if x >= 0 the

      Another equivalent version:

      -
      -absolute' x
      +
      +
      +
      absolute' x
           | x >= 0 = x
      -    | otherwise = -x
      -
      + | otherwise = -x +
      + +

      Notation warning: indentation is important in Haskell. @@ -867,17 +895,20 @@ Like in Python, a bad indentation could break your code!

      -
      -main = do
      -      print $ square 10
      -      print $ square' 10
      -      print $ square'' 10
      -      print $ square''' 10
      -      print $ absolute 10
      -      print $ absolute (-10)
      -      print $ absolute' 10
      -      print $ absolute' (-10)
      -
      + + +
      main = do
      +      print $ square 10
      +      print $ square' 10
      +      print $ square'' 10
      +      print $ square''' 10
      +      print $ absolute 10
      +      print $ absolute (-10)
      +      print $ absolute' 10
      +      print $ absolute' (-10)
      +
      + +
      @@ -896,7 +927,7 @@ We will select a problem and solve it using a standard imperative way. Then I will make the code evolve. The end result will be both more elegant and easier to adapt.

      -

      Let’s solve the following problem:

      +

      Let’s solve the following problem:

      Given a list of integers, return the sum of the even numbers in the list.

      @@ -906,21 +937,20 @@ The end result will be both more elegant and easier to adapt.

      To show differences between the functional and imperative approach, -I’ll start by providing an imperative solution (in Javascript):

      +I’ll start by providing an imperative solution (in Javascript):

      -
      -function evenSum(list) {
      -    var result = 0;
      -    for (var i=0; i< list.length ; i++) {
      -        if (list[i] % 2 ==0) {
      -            result += list[i];
      +
      function evenSum(list) {
      +    var result = 0;
      +    for (var i=0; i< list.length ; i++) {
      +        if (list[i] % 2 ==0) {
      +            result += list[i];
               }
           }
      -    return result;
      +    return result;
       }
      -
      +
      -

      But, in Haskell we don’t have variables, nor for loop. +

      But, in Haskell we don’t have variables, nor for loop. One solution to achieve the same result without loops is to use recursion.

      @@ -933,61 +963,56 @@ Most of the time Haskell will handle recursive functions efficiently.

      Here is a C version of the recursive function. Note that for simplicity, I assume the int list ends with the first 0 value.

      -
      -int evenSum(int *list) {
      -    return accumSum(0,list);
      +
      int evenSum(int *list) {
      +    return accumSum(0,list);
       }
       
      -int accumSum(int n, int *list) {
      -    int x;
      -    int *xs;
      -    if (*list == 0) { // if the list is empty
      -        return n;
      -    } else {
      -        x = list[0]; // let x be the first element of the list
      -        xs = list+1; // let xs be the list without x
      -        if ( 0 == (x%2) ) { // if x is even
      -            return accumSum(n+x, xs);
      -        } else {
      -            return accumSum(n, xs);
      +int accumSum(int n, int *list) {
      +    int x;
      +    int *xs;
      +    if (*list == 0) { // if the list is empty
      +        return n;
      +    } else {
      +        x = list[0]; // let x be the first element of the list
      +        xs = list+1; // let xs be the list without x
      +        if ( 0 == (x%2) ) { // if x is even
      +            return accumSum(n+x, xs);
      +        } else {
      +            return accumSum(n, xs);
               }
           }
       }
      -
      +

      Keep this code in mind. We will translate it into Haskell. But before, I need to introduce three simple but useful functions we will use:

      -
      -even :: Integral a => a -> Bool
      -head :: [a] -> a
      -tail :: [a] -> [a]
      -
      +
      even :: Integral a => a -> Bool
      +head :: [a] -> a
      +tail :: [a] -> [a]
      +

      even verifies if a number is even.

      -
      -even :: Integral a => a -> Bool
      -even 3  ⇒ False
      -even 2  ⇒ True
      -
      +
      even :: Integral a => a -> Bool
      +even 3  ⇒ False
      +even 2  ⇒ True
      +

      head returns the first element of a list:

      -
      -head :: [a] -> a
      -head [1,2,3] ⇒ 1
      -head []      ⇒ ERROR
      -
      +
      head :: [a] -> a
      +head [1,2,3] ⇒ 1
      +head []      ⇒ ERROR
      +

      tail returns all elements of a list, except the first:

      -
      -tail :: [a] -> [a]
      -tail [1,2,3] ⇒ [2,3]
      -tail [3]     ⇒ []
      -tail []      ⇒ ERROR
      -
      +
      tail :: [a] -> [a]
      +tail [1,2,3] ⇒ [2,3]
      +tail [3]     ⇒ []
      +tail []      ⇒ ERROR
      +

      Note that for any non empty list l, l ⇔ (head l):(tail l)

      @@ -999,20 +1024,23 @@ But before, I need to introduce three simple but useful functions we will use:evenSum returns the sum of all even numbers in a list:

      -
      --- Version 1
      -evenSum :: [Integer] -> Integer
      +
      +
      +
      -- Version 1
      +evenSum :: [Integer] -> Integer
       
       evenSum l = accumSum 0 l
       
      -accumSum n l = if l == []
      -                  then n
      -                  else let x = head l 
      -                           xs = tail l 
      -                       in if even x
      -                              then accumSum (n+x) xs
      -                              else accumSum n xs
      -
      +accumSum n l = if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +

      To test a function you can use ghci:

      @@ -1054,16 +1082,18 @@ accumSum (0+2+4) [] In reality many things can be improved. First, we can generalize the type.

      -
      -evenSum :: Integral a => [a] -> a
      -
      +
      evenSum :: Integral a => [a] -> a
      +
      -
      -main = do print $ evenSum [1..10]
      -
      + + +
      main = do print $ evenSum [1..10]
      +
      + +
      @@ -1073,30 +1103,36 @@ main = do print $ evenS

      02_Hard_Part/12_Functions.lhs

      Next, we can use sub functions using where or let. -This way our accumSum function won’t pollute the global namespace.

      +This way our accumSum function won’t pollute the global namespace.

      -
      --- Version 2
      -evenSum :: Integral a => [a] -> a
      +
      +
      +
      -- Version 2
      +evenSum :: Integral a => [a] -> a
       
       evenSum l = accumSum 0 l
      -    where accumSum n l = 
      -            if l == []
      -                then n
      -                else let x = head l 
      -                         xs = tail l 
      -                     in if even x
      -                            then accumSum (n+x) xs
      -                            else accumSum n xs
      -
      + where accumSum n l = + if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1108,16 +1144,19 @@ main = print $ evenSum [1..10]

      Next, we can use pattern matching.

      -
      --- Version 3
      +
      +
      +
      -- Version 3
       evenSum l = accumSum 0 l
      -    where 
      +    where 
               accumSum n [] = n
               accumSum n (x:xs) = 
      -             if even x
      -                then accumSum (n+x) xs
      -                else accumSum n xs
      -
      + if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +

      What is pattern matching? Use values instead of general parameter names3.

      @@ -1125,30 +1164,27 @@ Use values instead of general parameter names -foo [] = <x> +
      foo [] =  <x>
       foo l  =  <y>
      -
      +

      But pattern matching goes even further. It is also able to inspect the inner data of a complex value. We can replace

      -
      -foo l =  let x  = head l 
      -             xs = tail l
      -         in if even x 
      -             then foo (n+x) xs
      -             else foo n xs
      -
      +
      foo l =  let x  = head l 
      +             xs = tail l
      +         in if even x 
      +             then foo (n+x) xs
      +             else foo n xs
      +

      with

      -
      -foo (x:xs) = if even x 
      -                 then foo (n+x) xs
      -                 else foo n xs
      -
      +
      foo (x:xs) = if even x 
      +                 then foo (n+x) xs
      +                 else foo n xs
      +

      This is a very useful feature. It makes our code both terser and easier to read.

      @@ -1156,9 +1192,12 @@ It makes our code both terser and easier to read.

      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1170,38 +1209,42 @@ main = print $ evenSum [1..10]

      In Haskell you can simplify function definition by η-reducing them. For example, instead of writing:

      -
      -f x = (some expresion) x
      -
      +
      f x = (some expresion) x
      +

      you can simply write

      -
      -f = some expression
      -
      +
      f = some expression
      +

      We use this method to remove the l:

      -
      --- Version 4
      -evenSum :: Integral a => [a] -> a
      +
      +
      +
      -- Version 4
      +evenSum :: Integral a => [a] -> a
       
       evenSum = accumSum 0
      -    where 
      +    where 
               accumSum n [] = n
               accumSum n (x:xs) = 
      -             if even x
      -                then accumSum (n+x) xs
      -                else accumSum n xs
      -
      + if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1220,27 +1263,24 @@ Higher order functions are functions taking functions as parameter.

      Here are some examples:

      -
      -filter :: (a -> Bool) -> [a] -> [a]
      -map :: (a -> b) -> [a] -> [b]
      -foldl :: (a -> b -> a) -> a -> [b] -> a
      -
      +
      filter :: (a -> Bool) -> [a] -> [a]
      +map :: (a -> b) -> [a] -> [b]
      +foldl :: (a -> b -> a) -> a -> [b] -> a
      +
      -

      Let’s proceed by small steps.

      +

      Let’s proceed by small steps.

      -
      --- Version 5
      -evenSum l = mysum 0 (filter even l)
      -    where 
      +
      -- Version 5
      +evenSum l = mysum 0 (filter even l)
      +    where 
             mysum n [] = n
             mysum n (x:xs) = mysum (n+x) xs 
      -
      +

      where

      -
      -filter even [1..10] ⇔  [2,4,6,8,10]
      -
      +
      filter even [1..10] ⇔  [2,4,6,8,10]
      +

      The function filter takes a function of type (a -> Bool) and a list of type [a]. It returns a list containing only elements for which the function returned true.

      @@ -1263,57 +1303,59 @@ myfunc list = foldl bar initialVa

      If you really want to know how the magic works. Here is the definition of foldl.

      -
      -foldl f z [] = z
      -foldl f z (x:xs) = foldl f (f z x) xs
      -
      +
      foldl f z [] = z
      +foldl f z (x:xs) = foldl f (f z x) xs
      +
      -
      -foldl f z [x1,...xn]
      +
      foldl f z [x1,...xn]
       ⇔  f (... (f (f z x1) x2) ...) xn
      -
      +
      -

      But as Haskell is lazy, it doesn’t evaluate (f z x) and pushes it to the stack. +

      But as Haskell is lazy, it doesn’t evaluate (f z x) and pushes it to the stack. This is why we generally use foldl' instead of foldl; foldl' is a strict version of foldl. -If you don’t understand what lazy and strict means, -don’t worry, just follow the code as if foldl and foldl' where identical.

      +If you don’t understand what lazy and strict means, +don’t worry, just follow the code as if foldl and foldl' where identical.

      Now our new version of evenSum becomes:

      -
      --- Version 6
      --- foldl' isn't accessible by default
      --- we need to import it from the module Data.List
      -import Data.List
      -evenSum l = foldl' mysum 0 (filter even l)
      -  where mysum acc value = acc + value
      -
      +
      -- Version 6
      +-- foldl' isn't accessible by default
      +-- we need to import it from the module Data.List
      +import Data.List
      +evenSum l = foldl' mysum 0 (filter even l)
      +  where mysum acc value = acc + value
      +

      Version we can simplify by using directly a lambda notation. -This way we don’t have to create the temporary name mysum.

      +This way we don’t have to create the temporary name mysum.

      -
      --- Version 7
      --- Generally it is considered a good practice
      --- to import only the necessary function(s)
      -import Data.List (foldl')
      -evenSum l = foldl' (\x y -> x+y) 0 (filter even l)
      -
      + + +
      -- Version 7
      +-- Generally it is considered a good practice
      +-- to import only the necessary function(s)
      +import Data.List (foldl')
      +evenSum l = foldl' (\x y -> x+y) 0 (filter even l)
      +
      + +

      And of course, we note that

      -
      -(\x y -> x+y) ⇔ (+)
      -
      +
      (\x y -> x+y) ⇔ (+)
      +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1324,17 +1366,16 @@ main = print $ evenSum [1..10]

      Finally

      -
      --- Version 8
      -import Data.List (foldl')
      -evenSum :: Integral a => [a] -> a
      -evenSum l = foldl' (+) 0 (filter even l)
      -
      +
      -- Version 8
      +import Data.List (foldl')
      +evenSum :: Integral a => [a] -> a
      +evenSum l = foldl' (+) 0 (filter even l)
      +
      -

      foldl' isn’t the easiest function to intuit. +

      foldl' isn’t the easiest function to intuit. If you are not used to it, you should study it a bit.

      -

      To help you understand what’s going on here, a step by step evaluation:

      +

      To help you understand what’s going on here, a step by step evaluation:

         evenSum [1,2,3,4]
      @@ -1350,31 +1391,32 @@ If you are not used to it, you should study it a bit.

      Another useful higher order function is (.). The (.) function corresponds to the mathematical composition.

      -
      -(f . g . h) x ⇔  f ( g (h x))
      -
      +
      (f . g . h) x ⇔  f ( g (h x))
      +

      We can take advantage of this operator to η-reduce our function:

      -
      --- Version 9
      -import Data.List (foldl')
      -evenSum :: Integral a => [a] -> a
      -evenSum = (foldl' (+) 0) . (filter even)
      -
      +
      -- Version 9
      +import Data.List (foldl')
      +evenSum :: Integral a => [a] -> a
      +evenSum = (foldl' (+) 0) . (filter even)
      +

      Also, we could rename some parts to make it clearer:

      -
      --- Version 10 
      -import Data.List (foldl')
      -sum' :: (Num a) => [a] -> a
      -sum' = foldl' (+) 0
      -evenSum :: Integral a => [a] -> a
      -evenSum = sum' . (filter even)
      +
      +
      +
      -- Version 10 
      +import Data.List (foldl')
      +sum' :: (Num a) => [a] -> a
      +sum' = foldl' (+) 0
      +evenSum :: Integral a => [a] -> a
      +evenSum = sum' . (filter even)
        
      -
      +
      + +

      It is time to discuss a bit. What did we gain by using higher order functions?

      @@ -1390,20 +1432,23 @@ We want to get the sum of all even square of element of the list.

      Update the version 10 is extremely easy:

      -
      -squareEvenSum = sum' . (filter even) . (map (^2))
      -squareEvenSum' = evenSum . (map (^2))
      -squareEvenSum'' = sum' . (map (^2)) . (filter even)
      -
      + + +
      squareEvenSum = sum' . (filter even) . (map (^2))
      +squareEvenSum' = evenSum . (map (^2))
      +squareEvenSum'' = sum' . (map (^2)) . (filter even)
      +
      + +
      -

      We just had to add another “transformation function”4.

      +

      We just had to add another “transformation function”4.

      map (^2) [1,2,3,4] ⇔ [1,4,9,16]
       

      The map function simply apply a function to all element of a list.

      -

      We didn’t had to modify anything inside the function definition. +

      We didn’t had to modify anything inside the function definition. It feels more modular. But in addition you can think more mathematically about your function. You can then use your function as any other one. @@ -1417,8 +1462,8 @@ If you want to know how, I suggest you to read this quite fun article: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, Fokkinga and Paterson.

      This example should show you how great pure functional programming is. -Unfortunately, using pure functional programming isn’t well suited to all usages. -Or at least such a language hasn’t been found yet.

      +Unfortunately, using pure functional programming isn’t well suited to all usages. +Or at least such a language hasn’t been found yet.

      One of the great powers of Haskell is the ability to create DSLs (Domain Specific Language) @@ -1435,9 +1480,12 @@ essential aspect of Haskell: Types.

      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1451,7 +1499,7 @@ main = print $ evenSum [1..10]

      tl;dr:

        -
      • type Name = AnotherType is just an alias and the compiler doesn’t do any difference between Name and AnotherType.
      • +
      • type Name = AnotherType is just an alias and the compiler doesn’t do any difference between Name and AnotherType.
      • data Name = NameConstructor AnotherType make a difference.
      • data can construct structures which can be recursives.
      • deriving is magic and create functions for you.
      • @@ -1469,14 +1517,13 @@ It will be easy to detect where you used the wrong parameter at the wrong place

        Static typing is generally essential to reach fast execution time. But most statically typed languages are bad at generalizing concepts. -Haskell’s saving grace is that it can infer types.

        +Haskell’s saving grace is that it can infer types.

        Here is a simple example. The square function in Haskell:

        -
        -square x = x * x
        -
        +
        square x = x * x
        +

        This function can square any Numeral type. You can provide square with an Int, an Integer, a Float a Fractional and even Complex. Proof by example:

        @@ -1499,49 +1546,47 @@ Prelude Data.Complex> square (2 :+ 1)

        Now compare with the amount of code necessary in C:

        -
        -int     int_square(int x) { return x*x; }
        +
        int     int_square(int x) { return x*x; }
         
        -float   float_square(float x) {return x*x; }
        +float   float_square(float x) {return x*x; }
         
        -complex complex_square (complex z) {
        +complex complex_square (complex z) {
             complex tmp; 
             tmp.real = z.real * z.real - z.img * z.img;
        -    tmp.img = 2 * z.img * z.real;
        +    tmp.img = 2 * z.img * z.real;
         }
         
         complex x,y;
         y = complex_square(x);
        -
        +

        For each type, you need to write a new function. The only way to work around this problem is to use some meta-programming trick. For example using the pre-processor. In C++ there is a better way, the C++ templates:

        -
        -#include <iostream>
        -#include <complex>
        -using namespace std;
        +
        #include <iostream>
        +#include <complex>
        +using namespace std;
         
        -template<typename T>
        -T square(T x)
        +template<typename T>
        +T square(T x)
         {
        -    return x*x;
        +    return x*x;
         }
         
        -int main() {
        -    // int
        -    int sqr_of_five = square(5);
        +int main() {
        +    // int
        +    int sqr_of_five = square(5);
             cout << sqr_of_five << endl;
        -    // double
        -    cout << (double)square(5.3) << endl;
        -    // complex
        -    cout << square( complex<double>(5,3) ) 
        +    // double
        +    cout << (double)square(5.3) << endl;
        +    // complex
        +    cout << square( complex<double>(5,3) ) 
                  << endl;
        -    return 0;
        +    return 0;
         }
        -
        +

        C++ does a far better job than C. For more complex function the syntax can be hard to follow: @@ -1559,7 +1604,7 @@ But unlike dynamically typed languages, most errors are caught before the execut Generally, in Haskell:

        -

        “if it compiles it certainly does what you intended”

        +

        “if it compiles it certainly does what you intended”


        @@ -1571,31 +1616,33 @@ Generally, in Haskell:

        First you can use aliases or type synonyms.

        -
        -type Name   = String
        -type Color  = String
         
        -showInfos :: Name ->  Color -> String
        -showInfos name color =  "Name: " ++ name
        -                        ++ ", Color: " ++ color
        -name :: Name
        -name = "Robin"
        -color :: Color
        -color = "Blue"
        -main = putStrLn $ showInfos name color
        -
        + +
        type Name   = String
        +type Color  = String
        +
        +showInfos :: Name ->  Color -> String
        +showInfos name color =  "Name: " ++ name
        +                        ++ ", Color: " ++ color
        +name :: Name
        +name = "Robin"
        +color :: Color
        +color = "Blue"
        +main = putStrLn $ showInfos name color
        +
        + +

        02_Hard_Part/21_Types.lhs


        02_Hard_Part/22_Types.lhs

        -

        But it doesn’t protect you much. +

        But it doesn’t protect you much. Try to swap the two parameter of showInfos and run the program:

        -
        -    putStrLn $ showInfos color name
        -
        +
            putStrLn $ showInfos color name
        +

        It will compile and execute. In fact you can replace Name, Color and String everywhere. @@ -1604,18 +1651,21 @@ The compiler will treat them as completely identical.

        Another method is to create your own types using the keyword data.

        -
        -data Name   = NameConstr String
        -data Color  = ColorConstr String
         
        -showInfos :: Name ->  Color -> String
        -showInfos (NameConstr name) (ColorConstr color) =
        -      "Name: " ++ name ++ ", Color: " ++ color
         
        -name  = NameConstr "Robin"
        -color = ColorConstr "Blue"
        -main = putStrLn $ showInfos name color
        -
        +
        data Name   = NameConstr String
        +data Color  = ColorConstr String
        +
        +showInfos :: Name ->  Color -> String
        +showInfos (NameConstr name) (ColorConstr color) =
        +      "Name: " ++ name ++ ", Color: " ++ color
        +
        +name  = NameConstr "Robin"
        +color = ColorConstr "Blue"
        +main = putStrLn $ showInfos name color
        +
        + +

        Now if you switch parameters of showInfos, the compiler complains! A possible mistake you could never do again. @@ -1623,50 +1673,45 @@ The only price is to be more verbose.

        Also remark constructor are functions:

        -
        -NameConstr  :: String -> Name
        -ColorConstr :: String -> Color
        -
        +
        NameConstr  :: String -> Name
        +ColorConstr :: String -> Color
        +

        The syntax of data is mainly:

        -
        -data TypeName =   ConstructorName  [types]
        -                | ConstructorName2 [types]
        +
        data TypeName =   ConstructorName  [types]
        +                | ConstructorName2 [types]
                         | ...
        -
        +

        Generally the usage is to use the same name for the DataTypeName and DataTypeConstructor.

        Example:

        -
        -data Complex = Num a => Complex a a
        -
        +
        data Complex = Num a => Complex a a
        +

        Also you can use the record syntax:

        -
        -data DataTypeName = DataConstructor {
        -                      field1 :: [type of field1]
        -                    , field2 :: [type of field2]
        +
        data DataTypeName = DataConstructor {
        +                      field1 :: [type of field1]
        +                    , field2 :: [type of field2]
                             ...
        -                    , fieldn :: [type of fieldn] }
        -
        + , fieldn :: [type of fieldn] } +

        And many accessors are made for you. Furthermore you can use another order when setting values.

        Example:

        -
        -data Complex = Num a => Complex { real :: a, img :: a}
        -c = Complex 1.0 2.0
        -z = Complex { real = 3, img = 4 }
        +
        data Complex = Num a => Complex { real :: a, img :: a}
        +c = Complex 1.0 2.0
        +z = Complex { real = 3, img = 4 }
         real c ⇒ 1.0
         img z ⇒ 4
        -
        +

        02_Hard_Part/22_Types.lhs

        @@ -1678,43 +1723,50 @@ img z ⇒ 4

        You already encountered a recursive type: lists. You can re-create lists, but with a more verbose syntax:

        -
        -data List a = Empty | Cons a (List a)
        -
        +
        data List a = Empty | Cons a (List a)
        +

        If you really want to use an easier syntax you can use an infix name for constructors.

        -
        -infixr 5 :::
        -data List a = Nil | a ::: (List a)
        -
        +
        infixr 5 :::
        +data List a = Nil | a ::: (List a)
        +

        The number after infixr is the priority.

        If you want to be able to print (Show), read (Read), test equality (Eq) and compare (Ord) your new data structure you can tell Haskell to derive the appropriate functions for you.

        -
        -infixr 5 :::
        -data List a = Nil | a ::: (List a) 
        -              deriving (Show,Read,Eq,Ord)
        -
        + + +
        infixr 5 :::
        +data List a = Nil | a ::: (List a) 
        +              deriving (Show,Read,Eq,Ord)
        +
        + +

        When you add deriving (Show) to your data declaration, Haskell create a show function for you. -We’ll see soon how you can use your own show function.

        +We’ll see soon how you can use your own show function.

        -
        -convertList [] = Nil
        +
        +
        +
        convertList [] = Nil
         convertList (x:xs) = x ::: convertList xs
        -
        +
        + +
        -
        -main = do
        -      print (0 ::: 1 ::: Nil)
        -      print (convertList [0,1])
        -
        + + +
        main = do
        +      print (0 ::: 1 ::: Nil)
        +      print (convertList [0,1])
        +
        + +

        This prints:

        @@ -1731,26 +1783,32 @@ main = do

        Magritte, l'Arbre

        -

        We’ll just give another standard example: binary trees.

        +

        We’ll just give another standard example: binary trees.

        -
        -import Data.List
         
        -data BinTree a = Empty 
        -                 | Node a (BinTree a) (BinTree a) 
        -                              deriving (Show)
        -
        + +
        import Data.List
        +
        +data BinTree a = Empty 
        +                 | Node a (BinTree a) (BinTree a) 
        +                              deriving (Show)
        +
        + +

        We will also create a function which turns a list into an ordered binary tree.

        -
        -treeFromList :: (Ord a) => [a] -> BinTree a
        -treeFromList [] = Empty
        -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
        -                             (treeFromList (filter (>x) xs))
        -
        + + +
        treeFromList :: (Ord a) => [a] -> BinTree a
        +treeFromList [] = Empty
        +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
        +                             (treeFromList (filter (>x) xs))
        +
        + +

        Look at how elegant this function is. In plain English:

        @@ -1767,9 +1825,12 @@ In plain English:

      -
      -main = print $ treeFromList [7,2,4,8]
      -
      + + +
      main = print $ treeFromList [7,2,4,8]
      +
      + +

      You should obtain the following:

      @@ -1783,7 +1844,7 @@ main = print $ treeFromList [7,2,4,8]

      02_Hard_Part/31_Trees.lhs

      -

      Just for fun, let’s code a better display for our trees. +

      Just for fun, let’s code a better display for our trees. I simply had fun making a nice function to display trees in a general way. You can safely skip this part if you find it too difficult to follow.

      @@ -1793,93 +1854,104 @@ And it might also be useful to make our BinTree an instance of (Eq We will be able to test equality and compare trees.

      -
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -

      Without the deriving (Show), Haskell doesn’t create a show method for us. +

      Without the deriving (Show), Haskell doesn’t create a show method for us. We will create our own version of show. To achieve this, we must declare that our newly created type BinTree a is an instance of the type class Show. The general syntax is:

      -
      -instance Show (BinTree a) where
      -   show t = ... -- You declare your function here
      -
      +
      instance Show (BinTree a) where
      +   show t = ... -- You declare your function here
      +

      Here is my version of how to show a binary tree. -Don’t worry about the apparent complexity. +Don’t worry about the apparent complexity. I made a lot of improvements in order to display even stranger objects.

      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    -- treeshow pref Tree 
      -    --   shows a tree and starts each line with pref
      -    -- We don't display the Empty tree
      -    treeshow pref Empty = ""
      -    -- Leaf
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    -- treeshow pref Tree 
      +    --   shows a tree and starts each line with pref
      +    -- We don't display the Empty tree
      +    treeshow pref Empty = ""
      +    -- Leaf
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    -- Right branch is empty
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    -- Right branch is empty
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    -- Left branch is empty
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    -- Left branch is empty
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- Tree with left and right children non empty
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    -- Tree with left and right children non empty
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- shows a tree using some prefixes to make it nice
      +    -- shows a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replaces "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (show x)
      +    -- pshow replaces "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (show x)
       
      -    -- replaces one char by another string
      +    -- replaces one char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      -
      + | otherwise = x:[] -- "x" +
      + +

      The treeFromList method remains identical.

      -
      -treeFromList :: (Ord a) => [a] -> BinTree a
      -treeFromList [] = Empty
      -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      -                             (treeFromList (filter (>x) xs))
      -
      + + +
      treeFromList :: (Ord a) => [a] -> BinTree a
      +treeFromList [] = Empty
      +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      +                             (treeFromList (filter (>x) xs))
      +
      + +

      And now, we can play:

      -
      -main = do
      -  putStrLn "Int binary tree:"
      -  print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
      -
      + + +
      main = do
      +  putStrLn "Int binary tree:"
      +  print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
      +
      + +
      Int binary tree:
       < 7
      @@ -1900,10 +1972,13 @@ And each following line starts with a :.
       But we could also use another type.

      -
      -  putStrLn "\nString binary tree:"
      -  print $ treeFromList ["foo","bar","baz","gor","yog"]
      -
      + + +
        putStrLn "\nString binary tree:"
      +  print $ treeFromList ["foo","bar","baz","gor","yog"]
      +
      + +
      String binary tree:
       < "foo"
      @@ -1917,11 +1992,14 @@ But we could also use another type.

      make tree of trees!

      -
      -  putStrLn "\nBinary tree of Char binary trees:"
      -  print ( treeFromList 
      -           (map treeFromList ["baz","zara","bar"]))
      -
      + + +
        putStrLn "\nBinary tree of Char binary trees:"
      +  print ( treeFromList 
      +           (map treeFromList ["baz","zara","bar"]))
      +
      + +
      Binary tree of Char binary trees:
       < < 'b'
      @@ -1940,25 +2018,27 @@ make tree of trees!

      Yo Dawg Tree

      -
      -  putStrLn "\nTree of Binary trees of Char binary trees:"
      -  print $ (treeFromList . map (treeFromList . map treeFromList))
      -             [ ["YO","DAWG"]
      -             , ["I","HEARD"]
      -             , ["I","HEARD"]
      -             , ["YOU","LIKE","TREES"] ]
      -
      + + +
        putStrLn "\nTree of Binary trees of Char binary trees:"
      +  print $ (treeFromList . map (treeFromList . map treeFromList))
      +             [ ["YO","DAWG"]
      +             , ["I","HEARD"]
      +             , ["I","HEARD"]
      +             , ["YOU","LIKE","TREES"] ]
      +
      + +

      Which is equivalent to

      -
      -print ( treeFromList (
      -          map treeFromList 
      -             [ map treeFromList ["YO","DAWG"]
      -             , map treeFromList ["I","HEARD"]
      -             , map treeFromList ["I","HEARD"]
      -             , map treeFromList ["YOU","LIKE","TREES"] ]))
      -
      +
      print ( treeFromList (
      +          map treeFromList 
      +             [ map treeFromList ["YO","DAWG"]
      +             , map treeFromList ["I","HEARD"]
      +             , map treeFromList ["I","HEARD"]
      +             , map treeFromList ["YOU","LIKE","TREES"] ]))
      +

      and gives:

      @@ -1988,7 +2068,7 @@ make tree of trees!

      : : : `--'S'
      -

      Notice how duplicate trees aren’t inserted; +

      Notice how duplicate trees aren’t inserted; there is only one tree corresponding to "I","HEARD". We have this for (almost) free, because we have declared Tree to be an instance of Eq.

      @@ -2021,17 +2101,20 @@ Laziness is just a common implementation for non-strict languages.

      For example in Haskell you can do:

      -
      --- numbers = [1,2,..]
      -numbers :: [Integer]
      -numbers = 0:map (1+) numbers
       
      -take' n [] = []
      -take' 0 l = []
      -take' n (x:xs) = x:take' (n-1) xs
       
      -main = print $ take' 10 numbers
      -
      +
      -- numbers = [1,2,..]
      +numbers :: [Integer]
      +numbers = 0:map (1+) numbers
      +
      +take' n [] = []
      +take' 0 l = []
      +take' n (x:xs) = x:take' (n-1) xs
      +
      +main = print $ take' 10 numbers
      +
      + +

      And it stops.

      @@ -2059,87 +2142,98 @@ Also, there is a built-in function take which is equivalent to our This code is mostly the same as the previous one.
      -
      -import Debug.Trace (trace)
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      import Debug.Trace (trace)
      +import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
      +
      +
      + -
      -

      Suppose we don’t mind having an ordered binary tree. +

      Suppose we don’t mind having an ordered binary tree. Here is an infinite binary tree:

      -
      -nullTree = Node 0 nullTree nullTree
      -
      + + +
      nullTree = Node 0 nullTree nullTree
      +
      + +

      A complete binary tree where each node is equal to 0. Now I will prove you can manipulate this object using the following function:

      -
      --- take all element of a BinTree 
      --- up to some depth
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +
      +
      +
      -- take all element of a BinTree 
      +-- up to some depth
      +treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +

      See what occurs for this program:

      -
      -main = print $ treeTakeDepth 4 nullTree
      -
      +
      main = print $ treeTakeDepth 4 nullTree
      +

      This code compiles, runs and stops giving the following result:

      @@ -2161,48 +2255,56 @@ main = print $ treeTakeDepth 4 nullTree

      Just to heat up your neurones a bit more, -let’s make a slightly more interesting tree:

      +let’s make a slightly more interesting tree:

      -
      -iTree = Node 0 (dec iTree) (inc iTree)
      -        where
      -           dec (Node x l r) = Node (x-1) (dec l) (dec r) 
      -           inc (Node x l r) = Node (x+1) (inc l) (inc r) 
      -
      + + +
      iTree = Node 0 (dec iTree) (inc iTree)
      +        where
      +           dec (Node x l r) = Node (x-1) (dec l) (dec r) 
      +           inc (Node x l r) = Node (x+1) (inc l) (inc r) 
      +
      + +

      Another way to create this tree is to use a higher order function. This function should be similar to map, but should work on BinTree instead of list. Here is such a function:

      -
      --- apply a function to each node of Tree
      -treeMap :: (a -> b) -> BinTree a -> BinTree b
      -treeMap f Empty = Empty
      -treeMap f (Node x left right) = Node (f x) 
      +
      +
      +
      -- apply a function to each node of Tree
      +treeMap :: (a -> b) -> BinTree a -> BinTree b
      +treeMap f Empty = Empty
      +treeMap f (Node x left right) = Node (f x) 
                                            (treeMap f left) 
                                            (treeMap f right)
      -
      +
      + +
      -

      Hint: I won’t talk more about this here. +

      Hint: I won’t talk more about this here. If you are interested by the generalization of map to other data structures, search for functor and fmap.

      Our definition is now:

      -
      -infTreeTwo :: BinTree Int
      -infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) 
      +
      +
      +
      infTreeTwo :: BinTree Int
      +infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) 
                           (treeMap (\x -> x+1) infTreeTwo) 
      -
      +
      + +

      Look at the result for

      -
      -main = print $ treeTakeDepth 4 infTreeTwo
      -
      +
      main = print $ treeTakeDepth 4 infTreeTwo
      +
      <  0
       : |-- -1
      @@ -2224,11 +2326,14 @@ main = print $ treeTakeDepth 4 infTreeTwo
       
      -
      -main = do
      -  print $ treeTakeDepth 4 nullTree
      -  print $ treeTakeDepth 4 infTreeTwo
      -
      + + +
      main = do
      +  print $ treeTakeDepth 4 nullTree
      +  print $ treeTakeDepth 4 infTreeTwo
      +
      + +
      @@ -2241,7 +2346,7 @@ Now, some of the really hardcore stuff can start.

      If you are like me, you should get the functional style. You should also understand a bit more the advantages of laziness by default. -But you also don’t really understand where to start in order to make a real +But you also don’t really understand where to start in order to make a real program. And in particular:

      @@ -2292,9 +2397,9 @@ To use pure functions you could do action2 (purefunction x) for exa

      In this section, I will explain how to use IO, not how it works. -You’ll see how Haskell separates the pure from the impure parts of the program.

      +You’ll see how Haskell separates the pure from the impure parts of the program.

      -

      Don’t stop because you’re trying to understand the details of the syntax. +

      Don’t stop because you’re trying to understand the details of the syntax. Answers will come in the next section.

      What to achieve?

      @@ -2305,18 +2410,21 @@ Print the sum of the numbers

      -
      -toList :: String -> [Integer]
      -toList input = read ("[" ++ input ++ "]")
       
      -main = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  print $ sum (toList input)
      -
      + +
      toList :: String -> [Integer]
      +toList input = read ("[" ++ input ++ "]")
      +
      +main = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  print $ sum (toList input)
      +
      + +

      It should be straightforward to understand the behavior of this program. -Let’s analyze the types in more detail.

      +Let’s analyze the types in more detail.

      putStrLn :: String -> IO ()
       getLine  :: IO String
      @@ -2362,9 +2470,9 @@ The meaning of this sentence should be clearer by the end of the next section.
       

      03_Hell/01_IO/02_progressive_io_example.lhs

      -

      Now let’s see how this program behaves. +

      Now let’s see how this program behaves. For example, what occur if the user enter something strange? -Let’s try:

      +Let’s try:

          % runghc 02_progressive_io_example.lhs
           Enter a list of numbers (separated by comma):
      @@ -2381,16 +2489,18 @@ Use the type Maybe.
       It is a very common type in Haskell.

      -
      -import Data.Maybe
      -
      + + +
      import Data.Maybe
      +
      + +

      What is this thing? Maybe is a type which takes one parameter. Its definition is:

      -
      -data Maybe a = Nothing | Just a
      -
      +
      data Maybe a = Nothing | Just a
      +

      This is a nice way to tell there was an error while trying to create/compute a value. @@ -2398,46 +2508,55 @@ The maybeRead function is a great example of this. This is a function similar to the function read5, but if something goes wrong the returned value is Nothing. If the value is right, it returns Just <the value>. -Don’t try to understand too much of this function. +Don’t try to understand too much of this function. I use a lower level function than read; reads.

      -
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -
      + + +
      maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +
      + +

      Now to be a bit more readable, we define a function which goes like this: If the string has the wrong format, it will return Nothing. -Otherwise, for example for “1,2,3”, it will return Just [1,2,3].

      +Otherwise, for example for “1,2,3”, it will return Just [1,2,3].

      -
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -
      + + +
      getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +
      + +

      We simply have to test the value in our main function.

      -
      -main :: IO ()
      -main = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> print (sum l)
      -          Nothing -> error "Bad format. Good Bye."
      -
      + + +
      main :: IO ()
      +main = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> print (sum l)
      +          Nothing -> error "Bad format. Good Bye."
      +
      + +

      In case of error, we display a nice error message.

      -

      Note that the type of each expression in the main’s do block remains of the form IO a. +

      Note that the type of each expression in the main’s do block remains of the form IO a. The only strange construction is error. -I’ll say error msg will simply take the needed type (here IO ()).

      +I’ll say error msg will simply take the needed type (here IO ()).

      One very important thing to note is the type of all the functions defined so far. There is only one function which contains IO in its type: main. @@ -2467,31 +2586,37 @@ I certainly forget many advantages, but the three main reasons are:

      We keep the first part:

      -
      -import Data.Maybe
       
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -
      + +
      import Data.Maybe
      +
      +maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +
      + +

      Now, we create a function which will ask the user for an list of integers until the input is right.

      -
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      + + +
      askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
      +
      + +

      This function is of type IO [Integer]. Such a type means that we retrieved a value of type [Integer] through some IO actions. @@ -2501,19 +2626,22 @@ Some people might explain while waving their hands:

      «This is an [Integer] inside an IO»

      -

      If you want to understand the details behind all of this, you’ll have to read the next section. +

      If you want to understand the details behind all of this, you’ll have to read the next section. But sincerely, if you just want to use IO. Just practice a little and remember to think about the type.

      Finally our main function is quite simpler:

      -
      -main :: IO ()
      -main = do
      +
      +
      +
      main :: IO ()
      +main = do
         list <- askUser
      -  print $ sum list
      -
      + print $ sum list +
      + +

      We have finished with our introduction to IO. This was quite fast. Here are the main things to remember:

      @@ -2521,7 +2649,7 @@ This was quite fast. Here are the main things to remember:

      • in the do bloc, each expression must have the type IO a. You are then limited in the number of expressions available. -For example, getLine, print, putStrLn, etc…
      • +For example, getLine, print, putStrLn, etc…
      • Try to externalize the pure functions as much as possible.
      • the IO a type means: an IO action which returns an element of type a. IO represents actions; under the hood, IO a is the type of a function. @@ -2568,7 +2696,7 @@ But look at a typical main function:

        which must be passed on to the next action.

        We create a function bind or (>>=). -With bind we don’t need temporary names anymore.

        +With bind we don’t need temporary names anymore.

        main =
           action1 >>= action2 >>= action3 >>= action4
        @@ -2587,24 +2715,23 @@ With bind we don’t need temporary names anymore.

        Why did we use this strange syntax, and what exactly is this IO type? It looks a bit like magic.

        -

        For now let’s just forget all about the pure parts of our program, and focus +

        For now let’s just forget all about the pure parts of our program, and focus on the impure parts:

        -
        -askUser :: IO [Integer]
        -askUser = do
        -  putStrLn "Enter a list of numbers (separated by commas):"
        -  input <- getLine
        -  let maybeList = getListFromString input in
        -      case maybeList of
        -          Just l  -> return l
        -          Nothing -> askUser
        +
        askUser :: IO [Integer]
        +askUser = do
        +  putStrLn "Enter a list of numbers (separated by commas):"
        +  input <- getLine
        +  let maybeList = getListFromString input in
        +      case maybeList of
        +          Just l  -> return l
        +          Nothing -> askUser
         
        -main :: IO ()
        -main = do
        +main :: IO ()
        +main = do
           list <- askUser
        -  print $ sum list
        -
        + print $ sum list +

        First remark; it looks like an imperative structure. Haskell is powerful enough to make impure code look imperative. @@ -2623,38 +2750,34 @@ The fact that a file exists or not can be seen as different states of the world. It is explicitly said main is a function that potentially changes the state of the world. Its type is then something like:

        -
        -main :: World -> World
        -
        +
        main :: World -> World
        +

        Not all functions may have access to this variable. Those which have access to this variable are impure. -Functions to which the world variable isn’t provided are pure6.

        +Functions to which the world variable isn’t provided are pure6.

        Haskell considers the state of the world as an input variable to main. But the real type of main is closer to this one7:

        -
        -main :: World -> ((),World)
        -
        +
        main :: World -> ((),World)
        +

        The () type is the null type. Nothing to see here.

        -

        Now let’s rewrite our main function with this in mind:

        +

        Now let’s rewrite our main function with this in mind:

        -
        -main w0 =
        -    let (list,w1) = askUser w0 in
        -    let (x,w2) = print (sum list,w1) in
        +
        main w0 =
        +    let (list,w1) = askUser w0 in
        +    let (x,w2) = print (sum list,w1) in
             x 
        -
        +

        First, we note that all functions which have side effects must have the type:

        -
        -World -> (a,World)
        -
        +
        World -> (a,World)
        +

        Where a is the type of the result. For example, a getChar function should have the type World -> (Char,World).

        @@ -2685,37 +2808,34 @@ Under the hood, print will evaluate as:

      Now, if you look at the style of the main function, it is clearly awkward. -Let’s try to do the same to the askUser function:

      +Let’s try to do the same to the askUser function:

      -
      -askUser :: World -> ([Integer],World)
      -
      +
      askUser :: World -> ([Integer],World)
      +

      Before:

      -
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers:"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      +
      askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers:"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
      +

      After:

      -
      -askUser w0 =
      -    let (_,w1)     = putStrLn "Enter a list of numbers:" in
      -    let (input,w2) = getLine w1 in
      -    let (l,w3)     = case getListFromString input of
      -                      Just l   -> (l,w2)
      -                      Nothing  -> askUser w2
      -    in
      +
      askUser w0 =
      +    let (_,w1)     = putStrLn "Enter a list of numbers:" in
      +    let (input,w2) = getLine w1 in
      +    let (l,w3)     = case getListFromString input of
      +                      Just l   -> (l,w2)
      +                      Nothing  -> askUser w2
      +    in
               (l,w3)
      -
      +

      This is similar, but awkward. Look at all these temporary w? names.

      @@ -2726,38 +2846,34 @@ Look at all these temporary w? names.

      We see a pattern. Each line is of the form:

      -
      -let (y,w') = action x w in
      -
      +
      let (y,w') = action x w in
      +
      -

      Even if for some line the first x argument isn’t needed. +

      Even if for some line the first x argument isn’t needed. The output type is a couple, (answer, newWorldValue). Each function f must have a type similar to:

      -
      -f :: World -> (a,World)
      -
      +
      f :: World -> (a,World)
      +

      Not only this, but we can also note that we always follow the same usage pattern:

      -
      -let (y,w1) = action1 w0 in
      -let (z,w2) = action2 w1 in
      -let (t,w3) = action3 w2 in
      +
      let (y,w1) = action1 w0 in
      +let (z,w2) = action2 w1 in
      +let (t,w3) = action3 w2 in
       ...
      -
      +

      Each action can take from 0 to n parameters. And in particular, each action can take a parameter from the result of a line above.

      For example, we could also have:

      -
      -let (_,w1) = action1 x w0   in
      -let (z,w2) = action2 w1     in
      -let (_,w3) = action3 x z w2 in
      +
      let (_,w1) = action1 x w0   in
      +let (z,w2) = action2 w1     in
      +let (_,w3) = action3 x z w2 in
       ...
      -
      +

      And of course actionN w :: (World) -> (a,World).

      @@ -2775,237 +2891,221 @@ let (y,w2) = action2 w1 in
      -

      Jocker pencil trick

      +

      Jocker pencil trick

      Now, we will do a magic trick. -We will make the temporary world symbol “disappear”. +We will make the temporary world symbol “disappear”. We will bind the two lines. -Let’s define the bind function. +Let’s define the bind function. Its type is quite intimidating at first:

      -
      -bind :: (World -> (a,World)) 
      -        -> (a -> (World -> (b,World))) 
      -        -> (World -> (b,World)) 
      -
      +
      bind :: (World -> (a,World)) 
      +        -> (a -> (World -> (b,World))) 
      +        -> (World -> (b,World)) 
      +

      But remember that (World -> (a,World)) is the type for an IO action. -Now let’s rename it for clarity:

      +Now let’s rename it for clarity:

      -
      -type IO a = World -> (a, World)
      -
      +
      type IO a = World -> (a, World)
      +

      Some example of functions:

      -
      -getLine :: IO String
      -print :: Show a => a -> IO ()
      -
      +
      getLine :: IO String
      +print :: Show a => a -> IO ()
      +

      getLine is an IO action which takes a world as parameter and returns a couple (String,World). Which can be summarized as: getLine is of type IO String. -Which we also see as, an IO action which will return a String “embeded inside an IO”.

      +Which we also see as, an IO action which will return a String “embeded inside an IO”.

      The function print is also interesting. It takes one argument which can be shown. In fact it takes two arguments. The first is the value to print and the other is the state of world. It then returns a couple of type ((),World). -This means it changes the state of the world, but doesn’t yield anymore data.

      +This means it changes the state of the world, but doesn’t yield anymore data.

      This type helps us simplify the type of bind:

      -
      -bind :: IO a 
      -        -> (a -> IO b) 
      -        -> IO b
      -
      +
      bind :: IO a 
      +        -> (a -> IO b) 
      +        -> IO b
      +

      It says that bind takes two IO actions as parameter and return another IO action.

      Now, remember the important patterns. The first was:

      -
      -let (x,w1) = action1 w0 in
      -let (y,w2) = action2 x w1 in
      +
      let (x,w1) = action1 w0 in
      +let (y,w2) = action2 x w1 in
       (y,w2)
      -
      +

      Look at the types:

      -
      -action1  :: IO a
      -action2  :: a -> IO b
      -(y,w2)   :: IO b
      -
      +
      action1  :: IO a
      +action2  :: a -> IO b
      +(y,w2)   :: IO b
      +
      -

      Doesn’t it seem familiar?

      +

      Doesn’t it seem familiar?

      -
      -(bind action1 action2) w0 =
      -    let (x, w1) = action1 w0
      +
      (bind action1 action2) w0 =
      +    let (x, w1) = action1 w0
               (y, w2) = action2 x w1
      -    in  (y, w2)
      -
      + in (y, w2) +
      -

      The idea is to hide the World argument with this function. Let’s go: +

      The idea is to hide the World argument with this function. Let’s go: As an example imagine if we wanted to simulate:

      -
      -let (line1,w1) = getLine w0 in
      -let ((),w2) = print line1 in
      +
      let (line1,w1) = getLine w0 in
      +let ((),w2) = print line1 in
       ((),w2)
      -
      +

      Now, using the bind function:

      -
      -(res,w2) = (bind getLine (\l -> print l)) w0
      -
      +
      (res,w2) = (bind getLine (\l -> print l)) w0
      +

      As print is of type (World → ((),World)), we know res = () (null type). -If you didn’t see what was magic here, let’s try with three lines this time.

      +If you didn’t see what was magic here, let’s try with three lines this time.

      -
      -let (line1,w1) = getLine w0 in
      -let (line2,w2) = getLine w1 in
      -let ((),w3) = print (line1 ++ line2) in
      +
      let (line1,w1) = getLine w0 in
      +let (line2,w2) = getLine w1 in
      +let ((),w3) = print (line1 ++ line2) in
       ((),w3)
      -
      +

      Which is equivalent to:

      -
      -(res,w3) = bind getLine (\line1 ->
      -             bind getLine (\line2 -> 
      -               print (line1 ++ line2)))
      -
      +
      (res,w3) = bind getLine (\line1 ->
      +             bind getLine (\line2 -> 
      +               print (line1 ++ line2)))
      +
      -

      Didn’t you notice something? +

      Didn’t you notice something? Yes, no temporary World variables are used anywhere! This is MA. GIC.

      We can use a better notation. -Let’s use (>>=) instead of bind. +Let’s use (>>=) instead of bind. (>>=) is an infix function like (+); reminder 3 + 4 ⇔ (+) 3 4

      -
      -(res,w3) = getLine >>=
      -           \line1 -> getLine >>=
      -           \line2 -> print (line1 ++ line2)
      -
      +
      (res,w3) = getLine >>=
      +           \line1 -> getLine >>=
      +           \line2 -> print (line1 ++ line2)
      +

      Ho Ho Ho! Happy Christmas Everyone! Haskell has made syntactical sugar for us:

      -
      -do
      +
      do
         x <- action1
         y <- action2
         z <- action3
         ...
      -
      +

      Is replaced by:

      -
      -action1 >>= \x ->
      +
      action1 >>= \x ->
       action2 >>= \y ->
       action3 >>= \z ->
       ...
      -
      +

      Note you can use x in action2 and x and y in action3.

      But what about the lines not using the <-? Easy, another function blindBind:

      -
      -blindBind :: IO a -> IO b -> IO b
      +
      blindBind :: IO a -> IO b -> IO b
       blindBind action1 action2 w0 =
           bind action (\_ -> action2) w0
      -
      +
      -

      I didn’t simplify this definition for clarity purpose. -Of course we can use a better notation, we’ll use the (>>) operator.

      +

      I didn’t simplify this definition for clarity purpose. +Of course we can use a better notation, we’ll use the (>>) operator.

      And

      -
      -do
      +
      do
           action1
           action2
           action3
      -
      +

      Is transformed into

      -
      -action1 >>
      +
      action1 >>
       action2 >> 
       action3
      -
      +

      Also, another function is quite useful.

      -
      -putInIO :: a -> IO a
      -putInIO x = IO (\w -> (x,w))
      -
      +
      putInIO :: a -> IO a
      +putInIO x = IO (\w -> (x,w))
      +
      -

      This is the general way to put pure values inside the “IO context”. +

      This is the general way to put pure values inside the “IO context”. The general name for putInIO is return. This is quite a bad name when you learn Haskell. return is very different from what you might be used to.


      03_Hell/01_IO/21_Detailled_IO.lhs

      -

      To finish, let’s translate our example:

      +

      To finish, let’s translate our example:

      -
      +
      
      +askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers (separated by commas):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
       
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers (separated by commas):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      -main :: IO ()
      -main = do
      +main :: IO ()
      +main = do
         list <- askUser
      -  print $ sum list
      -
      + print $ sum list +

      Is translated into:

      -
      -import Data.Maybe
       
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -askUser :: IO [Integer]
      +
      +
      import Data.Maybe
      +
      +maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +askUser :: IO [Integer]
       askUser = 
      -    putStrLn "Enter a list of numbers (sep. by commas):" >>
      -    getLine >>= \input ->
      -    let maybeList = getListFromString input in
      -      case maybeList of
      -        Just l -> return l
      -        Nothing -> askUser
      +    putStrLn "Enter a list of numbers (sep. by commas):" >>
      +    getLine >>= \input ->
      +    let maybeList = getListFromString input in
      +      case maybeList of
      +        Just l -> return l
      +        Nothing -> askUser
       
      -main :: IO ()
      +main :: IO ()
       main = askUser >>=
      -  \list -> print $ sum list
      -
      + \list -> print $ sum list +
      + +

      You can compile this code to verify it keeps working.

      @@ -3039,19 +3139,18 @@ To be an instance of this type class, you must provide the functions (> The function (>>) will be derived from (>>=). Here is how the type class Monad is declared (mostly):

      -
      -class Monad m  where
      -  (>>=) :: m a -> (a -> m b) -> m b
      -  return :: a -> m a
      +
      class Monad m  where
      +  (>>=) :: m a -> (a -> m b) -> m b
      +  return :: a -> m a
       
      -  (>>) :: m a -> m b -> m b
      +  (>>) :: m a -> m b -> m b
         f >> g = f >>= \_ -> g
       
      -  -- You should generally safely ignore this function
      -  -- which I believe exists for historical reason
      -  fail :: String -> m a
      -  fail = error
      -
      + -- You should generally safely ignore this function + -- which I believe exists for historical reason + fail :: String -> m a + fail = error +

      Remarks:

      @@ -3064,7 +3163,7 @@ A better word should have been typeclass. That means a set of types. For a type to belong to a class, all functions of the class must be provided for this type.
    • In this particular example of type class, the type m must be a type that takes an argument. -for example IO a, but also Maybe a, [a], etc…
    • +for example IO a, but also Maybe a, [a], etc…
    • To be a useful monad, your function must obey some rules. If your construction does not obey these rules strange things might happens:

      @@ -3088,69 +3187,75 @@ It is particularly useful to remove very deep if..then..else.. cons if you can afford to follow a list of operations without being negative.

      -
      -deposit  value account = account + value
      +
      +
      +
      deposit  value account = account + value
       withdraw value account = account - value
       
      -eligible :: (Num a,Ord a) => a -> Bool
      +eligible :: (Num a,Ord a) => a -> Bool
       eligible account = 
      -  let account1 = deposit 100 account in
      -    if (account1 < 0) 
      -    then False
      -    else 
      -      let account2 = withdraw 200 account1 in
      -      if (account2 < 0) 
      -      then False
      -      else 
      -        let account3 = deposit 100 account2 in
      -        if (account3 < 0) 
      -        then False
      -        else 
      -          let account4 = withdraw 300 account3 in
      -          if (account4 < 0) 
      -          then False
      -          else 
      -            let account5 = deposit 1000 account4 in
      -            if (account5 < 0) 
      -            then False
      -            else
      -              True
      +  let account1 = deposit 100 account in
      +    if (account1 < 0) 
      +    then False
      +    else 
      +      let account2 = withdraw 200 account1 in
      +      if (account2 < 0) 
      +      then False
      +      else 
      +        let account3 = deposit 100 account2 in
      +        if (account3 < 0) 
      +        then False
      +        else 
      +          let account4 = withdraw 300 account3 in
      +          if (account4 < 0) 
      +          then False
      +          else 
      +            let account5 = deposit 1000 account4 in
      +            if (account5 < 0) 
      +            then False
      +            else
      +              True
      +
      +main = do
      +  print $ eligible 300 -- True
      +  print $ eligible 299 -- False
      +
      + -main = do - print $ eligible 300 -- True - print $ eligible 299 -- False -

      03_Hell/02_Monads/10_Monads.lhs


      03_Hell/02_Monads/11_Monads.lhs

      -

      Now, let’s make it better using Maybe and the fact that it is a Monad

      +

      Now, let’s make it better using Maybe and the fact that it is a Monad

      -
      -deposit :: (Num a) => a -> a -> Maybe a
      -deposit value account = Just (account + value)
       
      -withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      -withdraw value account = if (account < value) 
      -                         then Nothing 
      -                         else Just (account - value)
       
      -eligible :: (Num a, Ord a) => a -> Maybe Bool
      -eligible account = do
      +
      deposit :: (Num a) => a -> a -> Maybe a
      +deposit value account = Just (account + value)
      +
      +withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      +withdraw value account = if (account < value) 
      +                         then Nothing 
      +                         else Just (account - value)
      +
      +eligible :: (Num a, Ord a) => a -> Maybe Bool
      +eligible account = do
         account1 <- deposit 100 account 
         account2 <- withdraw 200 account1 
         account3 <- deposit 100 account2 
         account4 <- withdraw 300 account3 
         account5 <- deposit 1000 account4
      -  Just True
      +  Just True
      +
      +main = do
      +  print $ eligible 300 -- Just True
      +  print $ eligible 299 -- Nothing
      +
      + -main = do - print $ eligible 300 -- Just True - print $ eligible 299 -- Nothing -

      03_Hell/02_Monads/11_Monads.lhs

      @@ -3160,28 +3265,31 @@ main = do

      Not bad, but we can make it even better:

      -
      -deposit :: (Num a) => a -> a -> Maybe a
      -deposit value account = Just (account + value)
       
      -withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      -withdraw value account = if (account < value) 
      -                         then Nothing 
      -                         else Just (account - value)
       
      -eligible :: (Num a, Ord a) => a -> Maybe Bool
      +
      deposit :: (Num a) => a -> a -> Maybe a
      +deposit value account = Just (account + value)
      +
      +withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      +withdraw value account = if (account < value) 
      +                         then Nothing 
      +                         else Just (account - value)
      +
      +eligible :: (Num a, Ord a) => a -> Maybe Bool
       eligible account =
         deposit 100 account >>=
         withdraw 200 >>=
         deposit 100  >>=
         withdraw 300 >>=
         deposit 1000 >>
      -  return True
      +  return True
      +
      +main = do
      +  print $ eligible 300 -- Just True
      +  print $ eligible 299 -- Nothing
      +
      + -main = do - print $ eligible 300 -- Just True - print $ eligible 299 -- Nothing -

      We have proven that Monads are a good way to make our code more elegant. Note this idea of code organization, in particular for Maybe can be used @@ -3193,7 +3301,7 @@ In fact, this is the kind of construction we make naturally.

      The first element in the sequence being evaluated to Nothing will stop the complete evaluation. -This means you don’t execute all lines. +This means you don’t execute all lines. You have this for free, thanks to laziness.

    • @@ -3214,22 +3322,25 @@ But now a cooler example, lists.

      Here we go:

      -
      -import Control.Monad (guard)
      +
      +
      +
      import Control.Monad (guard)
       
       allCases = [1..10]
       
      -resolve :: [(Int,Int,Int)]
      -resolve = do
      +resolve :: [(Int,Int,Int)]
      +resolve = do
                     x <- allCases
                     y <- allCases
                     z <- allCases
                     guard $ 4*x + 2*y < z
      -              return (x,y,z)
      +              return (x,y,z)
      +
      +main = do
      +  print resolve
      +
      + -main = do - print resolve -

      MA. GIC. :

      @@ -3239,14 +3350,17 @@ main = do

      For the list monad, there is also a syntactical sugar:

      -
      -  print $ [ (x,y,z) | x <- allCases, 
      +
      +
      +
        print $ [ (x,y,z) | x <- allCases, 
                             y <- allCases, 
                             z <- allCases, 
                             4*x + 2*y < z ]
      -
      +
      + +
      -

      I won’t list all the monads, but there are many monads. +

      I won’t list all the monads, but there are many monads. Using monads simplifies the manipulation of several notions in pure languages. In particular, monad are very useful for:

      @@ -3256,10 +3370,10 @@ In particular, monad are very useful for:

    • generating pseudo random numbers,
    • keeping configuration state,
    • writing state,
    • -
    • +
    • -

      If you have followed me until here, then you’ve done it! +

      If you have followed me until here, then you’ve done it! You know monads8!

      and to understand when you can use them and create your own. But you already @@ -3287,7 +3401,7 @@ Unfortunately we removed two properties from our tree:

      In this section we will try to keep the first property. -Concerning the second one, we must relax it but we’ll discuss how to +Concerning the second one, we must relax it but we’ll discuss how to keep it as much as possible.

      @@ -3295,94 +3409,109 @@ keep it as much as possible.

      This code is mostly the same as the one in the [tree section](#trees).
      -
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
       
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      +-- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
      +
      +
      + -

      Our first step is to create some pseudo-random number list:

      -
      -shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
      -
      + + +
      shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
      +
      + +

      Just as a reminder, here is the definition of treeFromList

      -
      -treeFromList :: (Ord a) => [a] -> BinTree a
      -treeFromList []    = Empty
      -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      -                             (treeFromList (filter (>x) xs))
      -
      + + +
      treeFromList :: (Ord a) => [a] -> BinTree a
      +treeFromList []    = Empty
      +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      +                             (treeFromList (filter (>x) xs))
      +
      + +

      and treeTakeDepth:

      -
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +
      +
      +
      treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +

      See the result of:

      -
      -main = do
      -      putStrLn "take 10 shuffle"
      -      print $ take 10 shuffle
      -      putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
      -      print $ treeTakeDepth 4 (treeFromList shuffle)
      -
      + + +
      main = do
      +      putStrLn "take 10 shuffle"
      +      print $ take 10 shuffle
      +      putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
      +      print $ treeTakeDepth 4 (treeFromList shuffle)
      +
      + +
      % runghc 02_Hard_Part/41_Infinites_Structures.lhs
       take 10 shuffle
      @@ -3411,9 +3540,8 @@ Beware though, it will only work if you always have something to put into a bran
       
       

      For example

      -
      -treeTakeDepth 4 (treeFromList [1..]) 
      -
      +
      treeTakeDepth 4 (treeFromList [1..]) 
      +

      will loop forever. Simply because it will try to access the head of filter (<1) [2..]. @@ -3439,62 +3567,68 @@ But filter is not smart enought to understand that the result is th This code is mostly the same as the preceding one.

      -
      -import Debug.Trace (trace)
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      import Debug.Trace (trace)
      +import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
       
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +
      @@ -3506,23 +3640,26 @@ We generated only 4331 different numbers. To resolve this we make a slightly better shuffle function.

      -
      -shuffle = map rand [1..]
      -          where 
      -              rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2)
      -              p x = m*x^2 + n*x + o -- some polynome
      +
      +
      +
      shuffle = map rand [1..]
      +          where 
      +              rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2)
      +              p x = m*x^2 + n*x + o -- some polynome
                     m = 3123    
                     n = 31
                     o = 7641
                     c = 1237
      -
      +
      + +

      This shuffle function has the property (hopefully) not to have an upper nor lower bound. -But having a better shuffle list isn’t enough not to enter an infinite loop.

      +But having a better shuffle list isn’t enough not to enter an infinite loop.

      Generally, we cannot decide whether filter (<x) xs is empty. -Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. -This new version of code can create binary tree which don’t have the following property for some of its nodes:

      +Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. +This new version of code can create binary tree which don’t have the following property for some of its nodes:

      Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.

      @@ -3534,42 +3671,51 @@ Furthermore, by construction, each node value is unique in the tree.

      Here is our new version of treeFromList. We simply have replaced filter by safefilter.

      -
      -treeFromList :: (Ord a, Show a) => [a] -> BinTree a
      -treeFromList []    = Empty
      -treeFromList (x:xs) = Node x left right
      -          where 
      +
      +
      +
      treeFromList :: (Ord a, Show a) => [a] -> BinTree a
      +treeFromList []    = Empty
      +treeFromList (x:xs) = Node x left right
      +          where 
                     left = treeFromList $ safefilter (<x) xs
                     right = treeFromList $ safefilter (>x) xs
      -
      +
      + +
      -

      This new function safefilter is almost equivalent to filter but don’t enter infinite loop if the result is a finite list. +

      This new function safefilter is almost equivalent to filter but don’t enter infinite loop if the result is a finite list. If it cannot find an element for which the test is true after 10000 consecutive steps, then it considers to be the end of the search.

      -
      -safefilter :: (a -> Bool) -> [a] -> [a]
      +
      +
      +
      safefilter :: (a -> Bool) -> [a] -> [a]
       safefilter f l = safefilter' f l nbTry
      -  where
      +  where
             nbTry = 10000
             safefilter' _ _ 0 = []
             safefilter' _ [] _ = []
             safefilter' f (x:xs) n = 
      -                  if f x 
      -                     then x : safefilter' f xs nbTry 
      -                     else safefilter' f xs (n-1) 
      -
      + if f x + then x : safefilter' f xs nbTry + else safefilter' f xs (n-1) +
      + +

      Now run the program and be happy:

      -
      -main = do
      -      putStrLn "take 10 shuffle"
      -      print $ take 10 shuffle
      -      putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
      -      print $ treeTakeDepth 8 (treeFromList $ shuffle)
      -
      + + +
      main = do
      +      putStrLn "take 10 shuffle"
      +      print $ take 10 shuffle
      +      putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
      +      print $ treeTakeDepth 8 (treeFromList $ shuffle)
      +
      + +

      You should realize the time to print each value is different. This is because Haskell compute each value when it needs it. @@ -3591,26 +3737,25 @@ safefilter' f l = if filter f (take 10000 l) == [] then [] else filter f l

      -

      Explain why it doesn’t work and can enter into an infinite loop.

      +

      Explain why it doesn’t work and can enter into an infinite loop.

    • Suppose that shuffle is real random list with growing bounds. -If you study a bit this structure, you’ll discover that with probability 1, +If you study a bit this structure, you’ll discover that with probability 1, this structure is finite. Using the following code (suppose we could use safefilter' directly as if was not in the where of safefilter) find a definition of f such that with probability 1, -treeFromList’ shuffle is infinite. And prove it. +treeFromList’ shuffle is infinite. And prove it. Disclaimer, this is only a conjecture.
    • -
      -treeFromList' []  n = Empty
      -treeFromList' (x:xs) n = Node x left right
      -    where
      +
      treeFromList' []  n = Empty
      +treeFromList' (x:xs) n = Node x left right
      +    where
               left = treeFromList' (safefilter' (<x) xs (f n)
               right = treeFromList' (safefilter' (>x) xs (f n)
               f = ???
      -
      +

      04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs

      @@ -3629,7 +3774,7 @@ Thank you man.

      Even if most recent languages try to hide them, they are present.

    • -

      I know I’m cheating. But I will talk about non-strict later.

      +

      I know I’m cheating. But I will talk about non-strict later.

    • For the brave, a more complete explanation of pattern matching can be found here.

      @@ -3641,13 +3786,13 @@ Thank you man.

      Which itself is very similar to the javascript eval on a string containing JSON).

    • -

      There are some unsafe exceptions to this rule. But you shouldn’t see such use on a real application except maybe for debugging purpose.

      +

      There are some unsafe exceptions to this rule. But you shouldn’t see such use on a real application except maybe for debugging purpose.

    • For the curious the real type is data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}. All the # as to do with optimisation and I swapped the fields in my example. But mostly, the idea is exactly the same.

    • -

      Well, you’ll certainly need to practice a bit to get used to them

      +

      Well, you’ll certainly need to practice a bit to get used to them

    • @@ -3755,7 +3900,7 @@ Thank you man.

      Created: 02/08/2012 - Modified: 04/17/2012 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/Higher-order-function-in-zsh/code/functional.sh b/output/Scratch/en/blog/Higher-order-function-in-zsh/code/functional.sh index 5e3900291..bf99fa3bf 100644 --- a/output/Scratch/en/blog/Higher-order-function-in-zsh/code/functional.sh +++ b/output/Scratch/en/blog/Higher-order-function-in-zsh/code/functional.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Provide higer-order functions diff --git a/output/Scratch/en/blog/Higher-order-function-in-zsh/index.html b/output/Scratch/en/blog/Higher-order-function-in-zsh/index.html index 64414e7fa..3d6019fd5 100644 --- a/output/Scratch/en/blog/Higher-order-function-in-zsh/index.html +++ b/output/Scratch/en/blog/Higher-order-function-in-zsh/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -71,42 +74,40 @@ Simply because, the more I programmed with zsh the more I tended to work using f

      The minimal to have better code are the functions map, filter and fold.

      -

      Let’s compare. +

      Let’s compare. First a program which convert all gif to png in many different directories of different projects.

      Before ⇒

      -
      -# for each directory in projects dir
      -for toProject in /path/to/projects/*(/N); do
      -    # toProject is /path/to/projects/foo
      -    # project become foo (:t for tail)
      -    project=${toProject:t}
      -    for toResource in $toProject/resources/*.gif(.N); do
      -        convert $toResource ${toResource:r}.png && \
      -        \rm -f $toResource
      -    done
      -done
      -
      +
      # for each directory in projects dir
      +for toProject in /path/to/projects/*(/N); do
      +    # toProject is /path/to/projects/foo
      +    # project become foo (:t for tail)
      +    project=${toProject:t}
      +    for toResource in $toProject/resources/*.gif(.N); do
      +        convert $toResource ${toResource:r}.png && \
      +        \rm -f $toResource
      +    done
      +done
      +
        -
      • The (/N) means to select only directory and not to crash if there isn’t any.
      • -
      • The (.N) means to select only files and not to crash if there isn’t any.
      • +
      • The (/N) means to select only directory and not to crash if there isn’t any.
      • +
      • The (.N) means to select only files and not to crash if there isn’t any.
      • The :t means tail; if toto=/path/to/file.ext then ${toto:t}=file.ext.

      After ⇒

      -
      -gif_to_png() { convert $1 ${1:r}.png && \rm -f $1 }
      +
      gif_to_png() { convert $1 ${1:r}.png && \rm -f $1 }
       
      -handle_resources() { map gif_to_png $1/resources/*.gif(.N) }
      +handle_resources() { map gif_to_png $1/resources/*.gif(.N) }
       
       map handle_resources /path/to/projects/*(/N)
      -
      +

      No more bloc! -It might be a little bit harder to read if you’re not used to functional programming notation. +It might be a little bit harder to read if you’re not used to functional programming notation. But it is more concise and robusts.

      Another example with some tests.

      @@ -115,38 +116,36 @@ But it is more concise and robusts.

      Before ⇒

      -
      -for toProject in Projects/*; do
      -    project=$toProject:t
      -    if print -- project | grep -v s >/dev/null
      -    then
      -        print $project
      -        for toResource in $toProject/*(.N); do
      -            if print -- ${toResource:t} | grep $project >/dev/null; then
      -                print -- "X $toResource"
      -            fi
      -        done
      -    fi
      -done
      -
      +
      for toProject in Projects/*; do
      +    project=$toProject:t
      +    if print -- project | grep -v s >/dev/null
      +    then
      +        print $project
      +        for toResource in $toProject/*(.N); do
      +            if print -- ${toResource:t} | grep $project >/dev/null; then
      +                print -- "X $toResource"
      +            fi
      +        done
      +    fi
      +done
      +

      After ⇒

      -
      -contain_no_s() { print $1 | grep -v s }
      +
      contain_no_s() { print $1 | grep -v s }
       
       function verify_file_name {                               
      -    local project=$1:t
      -    contains_project_name() { print $1:t | grep $project }
      -    map "print -- X" $(filter contains_project_name $1/*(.N))
      +    local project=$1:t
      +    contains_project_name() { print $1:t | grep $project }
      +    map "print -- X" $(filter contains_project_name $1/*(.N))
       }
       
      -map verify_file_name $( filter contain_no_s Projects/* )
      -
      +map verify_file_name $( filter contain_no_s Projects/* ) +

      Also, the first verstion is a bit easier to read. But the second one is clearly far superior in architecture. -I don’t want to argue why here. +I don’t want to argue why here. Just believe me that the functional programming approach is superior.

      Actually I lack the lambda operator. @@ -154,72 +153,72 @@ If someone has an idea on how to create anonymous functions, just tell me, thank

      Here is the source code:

      -
      -
      -#!/usr/bin/env zsh
      +
       
      -# Provide higer-order functions 
      +
      #!/usr/bin/env zsh
       
      -# usage:
      -#
      -# $ foo(){print "x: $1"}
      -# $ map foo a b c d
      -# x: a
      -# x: b
      -# x: c
      -# x: d
      +# Provide higer-order functions 
      +
      +# usage:
      +#
      +# $ foo(){print "x: $1"}
      +# $ map foo a b c d
      +# x: a
      +# x: b
      +# x: c
      +# x: d
       function map {
      -    local func_name=$1
      +    local func_name=$1
           shift
      -    for elem in $@; print -- $(eval $func_name $elem)
      +    for elem in $@; print -- $(eval $func_name $elem)
       }
       
      -# $ bar() { print $(($1 + $2)) }
      -# $ fold bar 0 1 2 3 4 5
      -# 15
      -# -- but also
      -# $ fold bar 0 $( seq 1 100 )
      +# $ bar() { print $(($1 + $2)) }
      +# $ fold bar 0 1 2 3 4 5
      +# 15
      +# -- but also
      +# $ fold bar 0 $( seq 1 100 )
       function fold {
      -    if (($#<2)) {
      -        print -- "ERROR fold use at least 2 arguments" >&2
      +    if (($#<2)) {
      +        print -- "ERROR fold use at least 2 arguments" >&2
               return 1
           }
      -    if (($#<3)) {
      -        print -- $2
      +    if (($#<3)) {
      +        print -- $2
               return 0
      -    } else {
      +    } else {
               local acc
               local right
      -        local func_name=$1
      -        local init_value=$2
      -        local first_value=$3
      +        local func_name=$1
      +        local init_value=$2
      +        local first_value=$3
               shift 3
      -        right=$( fold $func_name $init_value $@ )
      -        acc=$( eval "$func_name $first_value $right" )
      -        print -- $acc
      +        right=$( fold $func_name $init_value $@ )
      +        acc=$( eval "$func_name $first_value $right" )
      +        print -- $acc
               return 0
           }
       }
       
      -# usage:
      -#
      -# $ baz() { print $1 | grep baz }
      -# $ filter baz titi bazaar biz
      -# bazaar
      +# usage:
      +#
      +# $ baz() { print $1 | grep baz }
      +# $ filter baz titi bazaar biz
      +# bazaar
       function filter {
      -    local predicate=$1
      +    local predicate=$1
           local result
           typeset -a result
           shift
      -    for elem in $@; do
      -        if eval $predicate $elem >/dev/null; then
      -            result=( $result $elem )
      -        fi
      -    done
      -    print $result
      +    for elem in $@; do
      +        if eval $predicate $elem >/dev/null; then
      +            result=( $result $elem )
      +        fi
      +    done
      +    print $result
       }
      -
      -
      +
      + @@ -336,7 +335,7 @@ function filter {
      Created: 09/28/2011 - Modified: 10/26/2011 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/Learn-Vim-Progressively/index.html b/output/Scratch/en/blog/Learn-Vim-Progressively/index.html index 85d4eb7a7..2a7c5822f 100644 --- a/output/Scratch/en/blog/Learn-Vim-Progressively/index.html +++ b/output/Scratch/en/blog/Learn-Vim-Progressively/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -73,7 +76,7 @@

      Learn vim and it will be your last text editor. -There isn’t any better text editor I know. +There isn’t any better text editor I know. Hard to learn, but incredible to use.

      I suggest you to learn it in 4 steps:

      @@ -85,16 +88,16 @@ Hard to learn, but incredible to use.

    • Use vim superpowers
    • -

      By the end of this journey, you’ll become a vim superstar.

      +

      By the end of this journey, you’ll become a vim superstar.

      But before we start, just a warning. Learning vim will be painful at first. It will take time. It will be a lot like playing a music instrument. -Don’t expect to be more efficient with vim than with another editor in less than 3 days. +Don’t expect to be more efficient with vim than with another editor in less than 3 days. In fact it will certainly take 2 weeks instead of 3 days.

      -

      1st Level – Survive

      +

      1st Level – Survive

      1. Install vim
      2. @@ -105,7 +108,7 @@ In fact it will certainly take 2 weeks instead of 3 days.

        In a standard editor, typing on the keyboard is enough to write something and see it on the screen. Not this time. Vim is in Normal mode. -Let’s get in Insert mode. +Let’s get in Insert mode. Type on the letter i.

        You should feel a bit better. @@ -127,7 +130,7 @@ And now, the list of command you can use in Normal mode to survive:

        Recommended:

          -
        • hjkl (highly recommended but not mandatory) → basic cursor move (←↓↑→). Hint: j look like a down arrow.
        • +
        • hjkl (highly recommended but not mandatory) → basic cursor move (←↓↑→). Hint: j look like a down arrow.
        • :help <command> → Show help about <command>, you can start using :help without anything else.
        @@ -143,14 +146,14 @@ With vim in Normal mode, it is a bit like if your Ctrl key is alway

        A last word about notations:

          -
        • instead of writing Ctrl-λ, I’ll write <C-λ>.
        • +
        • instead of writing Ctrl-λ, I’ll write <C-λ>.
        • command staring by : will must end by <enter>. For example, when I write :q it means :q<enter>.
        -

        2nd Level – Feel comfortable

        +

        2nd Level – Feel comfortable

        You know the commands required for survival. -It’s time to learn a few more commands. +It’s time to learn a few more commands. I suggest:

          @@ -217,17 +220,17 @@ I suggest:

          Take the time to integrate all of these command. Once done, you should be able to do every thing you are able to do on other editors. -But until now, it is a bit awkward. But follow me to the next level and you’ll see why.

          +But until now, it is a bit awkward. But follow me to the next level and you’ll see why.

          -

          3rd Level – Better. Stronger. Faster.

          +

          3rd Level – Better. Stronger. Faster.

          Congratulation reaching this far! We can start the interesting stuff. -At level 3, we’ll only talk about command which are compatible with the old vi.

          +At level 3, we’ll only talk about command which are compatible with the old vi.

          Better

          -

          Let’s look at how vim could help you to repeat yourself:

          +

          Let’s look at how vim could help you to repeat yourself:

          1. . → (dot) will repeat the last command,
          2. @@ -240,16 +243,16 @@ At level 3, we’ll only talk about command which are compatible with the ol
            • 2dd → will delete 2 lines
            • 3p → will paste the text 3 times
            • -
            • 100idesu [ESC] → will write “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “
            • -
            • . → Just after the last command will write again the 100 “desu “.
            • -
            • 3. → Will write 3 “desu” (and not 300, how clever).
            • +
            • 100idesu [ESC] → will write “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “
            • +
            • . → Just after the last command will write again the 100 “desu “.
            • +
            • 3. → Will write 3 “desu” (and not 300, how clever).

            Stronger

            Knowing how to move efficiently with vim is very important. -Don’t skip this section.

            +Don’t skip this section.

            1. NG → Go to line N
            2. @@ -265,7 +268,7 @@ Don’t skip this section.

            By default, word are composed of letter and the underscore character. -Let’s call a WORD a group of letter separated by blank characters. +Let’s call a WORD a group of letter separated by blank characters. If you want to consider WORDS, then just use uppercases:

              @@ -278,7 +281,7 @@ If you want to consider WORDS, then just use uppercases:

            -

            Now let’s talk about very efficient moves:

            +

            Now let’s talk about very efficient moves:

              @@ -306,12 +309,12 @@ Most commands can be used using the following general format:

            We also can do things like ye, yank from here to the end of the word. -But also y2/foo yank up to the second occurrence of “foo”.

            +But also y2/foo yank up to the second occurrence of “foo”.

            But what was true for y (yank), -is also true for d (delete), v (visual select), gU (uppercase), gu (lowercase), etc…

            +is also true for d (delete), v (visual select), gU (uppercase), gu (lowercase), etc…

            -

            4th Level – Vim Superpowers

            +

            4th Level – Vim Superpowers

            With all preceding commands you should be comfortable to use vim. But now, here are the killer features. @@ -368,7 +371,7 @@ Typically: 0<C-v><C-d>I-- [ESC]

            • ^ → go to start of the line
            • <C-v> → Start block selection
            • -
            • <C-d> → move down (could also be jjj or %, etc…)
            • +
            • <C-d> → move down (could also be jjj or %, etc…)
            • I-- [ESC] → write -- to comment each line
            @@ -378,7 +381,7 @@ Typically: 0<C-v><C-d>I-- [ESC]

            Completion: <C-n> and <C-p>.

            -

            In Insert mode, just type the start of a word, then type <C-p>, magic… +

            In Insert mode, just type the start of a word, then type <C-p>, magic… Completion

            Macros : qa do something q, @a, @@

            @@ -429,7 +432,7 @@ Once the selection made, you can:

            • <C-v>
            • -
            • go to desired line (jjj or <C-d> or /pattern or % etc…)
            • +
            • go to desired line (jjj or <C-d> or /pattern or % etc…)
            • $ go to the end of line
            • A, write text, ESC.
            @@ -443,7 +446,7 @@ Once the selection made, you can:

            • :split → create a split (:vsplit create a vertical split)
            • -
            • <C-w><dir> : where dir is any of hjkl or ←↓↑→ to change split.
            • +
            • <C-w><dir> : where dir is any of hjkl or ←↓↑→ to change split.
            • <C-w>_ (resp. <C-w>|) : maximise size of split (resp. vertical split)
            • <C-w>+ (resp. <C-w>-) : Grow (resp. shrink) split
            @@ -455,7 +458,7 @@ Once the selection made, you can:

            That was 90% of commands I use every day. I suggest you to learn no more than one or two new command per day. -After two to three weeks you’ll start to feel the power of vim in your hands.

            +After two to three weeks you’ll start to feel the power of vim in your hands.

            Learning Vim is more a matter of training than plain memorization. Fortunately vim comes with some very good tools and an excellent documentation. @@ -463,7 +466,7 @@ Run vimtutor until you are familiar with most basic commands. Also, you should read carefully this page: :help usr_02.txt.

            Then, you will learn about !, folds, registers, the plugins and many other features. -Learn vim like you’d learn piano and all should be fine.

            +Learn vim like you’d learn piano and all should be fine.

            + + @@ -70,9 +73,9 @@ I use a different password on all website.

            Disclamer, this is an unashamed attempt to make you download my iPhone app ;-). -You’re always here? -Even if you won’t download my app, you should read more. -My method doesn’t necessitate my app. +You’re always here? +Even if you won’t download my app, you should read more. +My method doesn’t necessitate my app. It is both safe and easy to use everyday.

            If you just want to use the tools without searching to understand why it is safe, just jump at the end of this article by clicking here.

            @@ -83,7 +86,7 @@ It is both safe and easy to use everyday.

            Even paranoid could have ennemies.

            -

            Imagine you find a really good password. You use it on GMail, Amazon, PayPal, Twitter, Facebook… +

            Imagine you find a really good password. You use it on GMail, Amazon, PayPal, Twitter, Facebook… One day you see a nice online game you want to try. They ask you your email and a password. Some week passes, and the host machine of this online game is hacked. @@ -125,9 +128,8 @@ Of course you can imagine some better transformation. But it is hard to find a v Knowing the result of a hash function, it is difficult to know what was their input. For example:

            -
            -hash("P45sW0r|)") = 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63
            -
            +
            hash("P45sW0r|)") = 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63
            +

            If someone has 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63, he will have hard time to recover P45sW0r|).

            @@ -156,7 +158,7 @@ This is why, for each website I need some other parameters:

            • the login name
            • -
            • the password’s length,
            • +
            • the password’s length,
            • the password number (in order to change it),
            • The output format: hexadecimal or base64.
            @@ -303,7 +305,7 @@ Further more using shorter password make it even harder for an attaquer to retri
      Created: 05/18/2011 - Modified: 10/26/2011 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/SVG-and-m4-fractals/code/yesodlogo.m4 b/output/Scratch/en/blog/SVG-and-m4-fractals/code/yesodlogo.m4 index 02ee411ab..39ab18ce6 100644 --- a/output/Scratch/en/blog/SVG-and-m4-fractals/code/yesodlogo.m4 +++ b/output/Scratch/en/blog/SVG-and-m4-fractals/code/yesodlogo.m4 @@ -1,4 +1,3 @@ - @@ -80,13 +83,13 @@ Then some believed it would be a good idea to invent many xmlUnfortunately, xml was made to transfert structured data. Not a format a human should see or edit directly. The sad reality is xml syntax is simply verbose and ugly. -Most of the time it shouldn’t be a problem, as nobody should see it. +Most of the time it shouldn’t be a problem, as nobody should see it. In a perfect nice world, we should never deal directly with xml but only use software which deal with it for us. Guess what? -Our world isn’t perfect. Too sad, a bunch of developer have to deal directly with this ugly xml.

      +Our world isn’t perfect. Too sad, a bunch of developer have to deal directly with this ugly xml.

      -

      Unfortunately xml isn’t the only case of misused format I know. -You have many format for which it would be very nice to add variables, loops, functions…

      +

      Unfortunately xml isn’t the only case of misused format I know. +You have many format for which it would be very nice to add variables, loops, functions…

      If like me you hate with passion xslt or writing xml, I will show you how you could deal with this bad format @@ -94,7 +97,7 @@ or language.

      The xslt Example

      -

      Let’s start by the worst case of misused xml I know: xslt. +

      Let’s start by the worst case of misused xml I know: xslt. Any developer who had to deal with xslt know how horrible it is.

      In order to reduce the verbosity of such a bad languages, there is a way. @@ -106,27 +109,24 @@ Any developer who had to deal with xslt know how horribl

    • Variable, instead of writing the natural myvar = value, here is the xslt way of doing this:
    • -
      -<xsl:variable name="myvar" select="value"/>
      -
      +
      <xsl:variable name="myvar" select="value"/>
      +
      • Printing something. Instead of print "Hello world!" here is the xslt equivalent:
      -
      -<xsl:text 
      -    disable-output-escaping="yes"><![CDATA[Hello world!
      -]]></xsl:text>
      -
      +
      <xsl:text 
      +    disable-output-escaping="yes"><![CDATA[Hello world!
      +]]></xsl:text>
      +
      • printing the value of a variable, instead of print myvar the xslt is:
      -
      -<xslt:value-of select="myvar"/>
      -
      +
      <xslt:value-of select="myvar"/>
      +
      • Just try to imagine how verbose it is to declare a function with this language.
      • @@ -134,30 +134,28 @@ Any developer who had to deal with xslt know how horribl

        The cure (m4 to the rescue)

        -
        -<?xml version="1.0" standalone="yes"?> <!-- YES its xml -->
        -<!-- ← start a comment, then write some m4 directives:
        -
        -define(`ydef',`<xsl:variable name="$1" select="$2"/>')
        -define(`yprint',`<xsl:text disable-output-escaping="yes"><![CDATA[$1]]></xsl:text>')
        -define(`yshow',`<xsl:value-of select="$1"/>')
        -
        --->
        -<!-- Yes, xml sucks to be read -->
        -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        -<!-- And it sucks even more to edit -->
        -<xsl:template match="/">
        +
        <?xml version="1.0" standalone="yes"?> <!-- YES its xml -->
        +<!-- ← start a comment, then write some m4 directives:
        +
        +define(`ydef',`<xsl:variable name="$1" select="$2"/>')
        +define(`yprint',`<xsl:text disable-output-escaping="yes"><![CDATA[$1]]></xsl:text>')
        +define(`yshow',`<xsl:value-of select="$1"/>')
        +
        +-->
        +<!-- Yes, xml sucks to be read -->
        +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        +<!-- And it sucks even more to edit -->
        +<xsl:template match="/">
             ydef(myvar,value)
             yprint(Hello world!)
             yshow(myvar)
        -</xsl:template>
        -
        +</xsl:template> +

        Now just compile this file:

        -
        -m4 myfile.m4 > myfile.xslt
        -
        +
        m4 myfile.m4 > myfile.xslt
        +

        Profit! Now xslt is more readable and easier to edit!

        @@ -168,12 +166,12 @@ At its beginning some people believed it would be the new Flash. Apparently, it

        Let me show you the result:

        -

        +

        Yesod logo made in SVG and m4 Click to view directly the svg. It might slow down your computers if you have an old one.

        -

        The positionning of the “esod” text with regards to the reversed “λ” was done by changing position in firebug. I didn’t had to manually regenerate to test.

        +

        The positionning of the “esod” text with regards to the reversed “λ” was done by changing position in firebug. I didn’t had to manually regenerate to test.

        Making such a fractal is mostly:

        @@ -185,63 +183,61 @@ Click to view directly the svg. It might slow down your
      • Stop when recursion is deep enough.
      • -

        If I had to do this for each step, I had make a lot of copy/paste in my svg, because the transformation is always the same, but I cannot say, use transformation named “titi”. Then instead of manually copying some xml, I used m4

        +

        If I had to do this for each step, I had make a lot of copy/paste in my svg, because the transformation is always the same, but I cannot say, use transformation named “titi”. Then instead of manually copying some xml, I used m4

        and here is the commented code:

        -
        -
        -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
        -<!--
        -     M4 Macros
        -define(`YTRANSFORMONE', `scale(.43) translate(-120,-69) rotate(-10)')
        -define(`YTRANSFORMTWO', `scale(.43) translate(-9,-67.5) rotate(10)')
        -define(`YTRANSFORMTHREE', `scale(.43) translate(53,41) rotate(120)')
        -define(`YGENTRANSFORM', `translate(364,274) scale(3)')
        -define(`YTRANSCOMPLETE', `
        -    <g id="level_$1">
        -        <use style="opacity: .8" transform="YTRANSFORMONE" xlink:href="#level_$2" />
        -        <use style="opacity: .8" transform="YTRANSFORMTWO" xlink:href="#level_$2" />
        -        <use style="opacity: .8" transform="YTRANSFORMTHREE" xlink:href="#level_$2" />
        -    </g>
        -    <use transform="YGENTRANSFORM" xlink:href="#level_$1" />
        -')
        - -->
        -<svg 
        -    xmlns="http://www.w3.org/2000/svg" 
        -    xmlns:xlink="http://www.w3.org/1999/xlink"
        -    x="64" y="64" width="512" height="512" viewBox="64 64 512 512"
        -    id="svg2" version="1.1">
        -    <g id="level_0"> <!-- some group, if I want to add other elements -->
        -        <!-- the text "λ" -->
        -        <text id="lambda" 
        -            fill="#333" style="font-family:Ubuntu; font-size: 100px"
        -            transform="rotate(180)">λ</text>
        -    </g>
        -    <!-- the text "esod" -->
        -    <text 
        -        fill="#333" 
        -        style="font-family:Ubuntu; font-size: 28px; letter-spacing: -0.10em" 
        -        x="-17.3" 
        -        y="69" 
        -        transform="YGENTRANSFORM">esod</text>
        -    <!-- ROOT ELEMENT -->
        -    <use transform="YGENTRANSFORM" xlink:href="#level_0" />
        +
         
        -    YTRANSCOMPLETE(1,0) <!-- First recursion -->
        -    YTRANSCOMPLETE(2,1) <!-- deeper -->
        -    YTRANSCOMPLETE(3,2) <!-- deeper -->
        -    YTRANSCOMPLETE(4,3) <!-- even deeper -->
        -    YTRANSCOMPLETE(5,4) <!-- Five level seems enough -->
        -</svg>
        -
        -
        +
        <?xml version="1.0" encoding="UTF-8" standalone="no"?>
        +<!--
        +     M4 Macros
        +define(`YTRANSFORMONE', `scale(.43) translate(-120,-69) rotate(-10)')
        +define(`YTRANSFORMTWO', `scale(.43) translate(-9,-67.5) rotate(10)')
        +define(`YTRANSFORMTHREE', `scale(.43) translate(53,41) rotate(120)')
        +define(`YGENTRANSFORM', `translate(364,274) scale(3)')
        +define(`YTRANSCOMPLETE', `
        +    <g id="level_$1">
        +        <use style="opacity: .8" transform="YTRANSFORMONE" xlink:href="#level_$2" />
        +        <use style="opacity: .8" transform="YTRANSFORMTWO" xlink:href="#level_$2" />
        +        <use style="opacity: .8" transform="YTRANSFORMTHREE" xlink:href="#level_$2" />
        +    </g>
        +    <use transform="YGENTRANSFORM" xlink:href="#level_$1" />
        +')
        + -->
        +<svg 
        +    xmlns="http://www.w3.org/2000/svg" 
        +    xmlns:xlink="http://www.w3.org/1999/xlink"
        +    x="64" y="64" width="512" height="512" viewBox="64 64 512 512"
        +    id="svg2" version="1.1">
        +    <g id="level_0"> <!-- some group, if I want to add other elements -->
        +        <!-- the text "λ" -->
        +        <text id="lambda" 
        +            fill="#333" style="font-family:Ubuntu; font-size: 100px"
        +            transform="rotate(180)">λ</text>
        +    </g>
        +    <!-- the text "esod" -->
        +    <text 
        +        fill="#333" 
        +        style="font-family:Ubuntu; font-size: 28px; letter-spacing: -0.10em" 
        +        x="-17.3" 
        +        y="69" 
        +        transform="YGENTRANSFORM">esod</text>
        +    <!-- ROOT ELEMENT -->
        +    <use transform="YGENTRANSFORM" xlink:href="#level_0" />
        +
        +    YTRANSCOMPLETE(1,0) <!-- First recursion -->
        +    YTRANSCOMPLETE(2,1) <!-- deeper -->
        +    YTRANSCOMPLETE(3,2) <!-- deeper -->
        +    YTRANSCOMPLETE(4,3) <!-- even deeper -->
        +    YTRANSCOMPLETE(5,4) <!-- Five level seems enough -->
        +</svg>
        +

        and I compiled it to svg and then to png with:

        -
        -m4 yesodlogo.m4 > yesodlogo.svg && convert yesodlogo.svg yesodlogo.png
        -
        +
        m4 yesodlogo.m4 > yesodlogo.svg && convert yesodlogo.svg yesodlogo.png
        +

        The main λ is duplicated 3 times. Each transformation is named by: YTRANSFORMONE, YTRANSFORMTWO and YTRANSFORMTHREE.

        @@ -258,7 +254,7 @@ It duplicates using the three transformations the preceding level.

      • At level 0 there is only one λ,
      • at level 1 there is 3 λ,
      • at level 2 there is 9 λ
      • -
      • etc…
      • +
      • etc…

      At the final 5th level there is 35=243 λ. @@ -392,7 +388,7 @@ to organize languages such as brainfuck.

      Created: 10/20/2011 - Modified: 11/16/2011 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/Typography-and-the-Web/index.html b/output/Scratch/en/blog/Typography-and-the-Web/index.html index 1401a90d8..56022bdc0 100644 --- a/output/Scratch/en/blog/Typography-and-the-Web/index.html +++ b/output/Scratch/en/blog/Typography-and-the-Web/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,13 +57,13 @@
      -

      Screenshot of first in small caps with and without ligatures.

      +

      Screenshot of first in small caps with and without ligatures.;

      -

      tl;dr: Web typography sucks and we’ll have to wait forever before it will be fixed.

      +

      tl;dr: Web typography sucks and we’ll have to wait forever before it will be fixed.

      @@ -79,10 +82,10 @@ We can all create better web typography ourselves, today.»

      First, what is a ligatures?

      -

      A ligature example

      +

      A ligature example;

      What is the problem between the Web and ligatures? -The first one is: you cannot search them. For example, try to search the word “first”:

      +The first one is: you cannot search them. For example, try to search the word “first”:

      • first ← No ligature, no problem
      • @@ -102,28 +105,27 @@ The first one is: you cannot search them. For example, try to search the word &l

        Here is a screenshot of what I see:

        -

        Screenshot of first in small caps with and without ligatures.

        +

        Screenshot of first in small caps with and without ligatures.;

        -

        The browser isn’t able to understand that the ligature character “” should render as fi when rendered in small caps. And one part of the problem is you should choose to display a character in small caps using css.

        +

        The browser isn’t able to understand that the ligature character “” should render as fi when rendered in small caps. And one part of the problem is you should choose to display a character in small caps using css.

        This way, how could you use a ligature Unicode character on a site on which you could change the css?

        -

        Let’s compare to LaTeX.

        +

        Let’s compare to LaTeX.

        -

        LaTeX screenshot

        +

        LaTeX screenshot;

        -

        If you take attention to detail, you’ll see the first “first” contains a ligature. Of course the second render nicely. The code I used were:

        +

        If you take attention to detail, you’ll see the first “first” contains a ligature. Of course the second render nicely. The code I used were:

        -
        -\item first
        -\item {\sc first}
        -
        +
        \item first
        +\item {\sc first}
        +

        LaTeX was intelligent enough to create himself the ligatures when needed.

        -

        The “” ligature is rare and not rendered in LaTeX by default. But if you want you could also render rare ligature using XƎLaTeX:

        +

        The “” ligature is rare and not rendered in LaTeX by default. But if you want you could also render rare ligature using XƎLaTeX:

        -

        XeLaTeX ligatures

        +

        XeLaTeX ligatures;

        I took this image from the excellent article of Dario Taraborelli.

        @@ -133,9 +135,9 @@ Simply imagine the number of strange little exceptions:

        • The text is rendered in small caps, I cannot use ligature.
        • The current word contains a ligature unicode character, I should search for ligature in this one.
        • -
        • The current font does not defined the ligature unicode character, we shouldn’t use it, etc
        • +
        • The current font does not defined the ligature unicode character, we shouldn’t use it, etc
        • A javascript command changed the CSS, I should verify if I had to revert the insertion of ligatures characters
        • -
        • etc…
        • +
        • etc…

        Nonetheless if someone has a solution, I would be happy to hear about it.

        @@ -247,7 +249,7 @@ Simply imagine the number of strange little exceptions:

      Created: 02/02/2012 - Modified: 02/02/2012 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/Yesod-excellent-ideas/index.html b/output/Scratch/en/blog/Yesod-excellent-ideas/index.html index a6018bb1a..0e7d09f43 100644 --- a/output/Scratch/en/blog/Yesod-excellent-ideas/index.html +++ b/output/Scratch/en/blog/Yesod-excellent-ideas/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -63,7 +66,7 @@

      tl;dr:

      Yesod is a web framework which recently reached the maturity for which you should consider to use it. -Before telling you why you should learn Haskell and use yesod, I will talk about ideas yesod introduced and I didn’t saw in other frameworks before.

      +Before telling you why you should learn Haskell and use yesod, I will talk about ideas yesod introduced and I didn’t saw in other frameworks before.

      @@ -71,18 +74,17 @@ Before telling you why you should learn Haskell and use yesod, I will talk about

      Type safety

      -

      Let’s start by an obligatory link from xkcd:

      +

      Let’s start by an obligatory link from xkcd:

      SQL injection by a mom

      When you create a web application, a lot of time is spent dealing with strings. -Strings for URL, HTML, JavaScript, CSS, SQL, etc… +Strings for URL, HTML, JavaScript, CSS, SQL, etc… To prevent malicious usage you have to protect each strings to be sure, no script will pass from one point to another. Suppose a user enter this user name:

      -
      -Newton<script>alert("An apple fall")</script>
      -
      +
      Newton<script>alert("An apple fall")</script>
      +

      You must transform each < into &lt;. Without this transformation alert will appear each time you try to display this user name. @@ -93,19 +95,18 @@ And the right protection is made by default to prevent problems.

      Yesod does its best to handle cross scripting issues. Both between the client and the server and between the server and your DB. Here is an example:

      -
      <a href=@[AnotherPageR]>Go to the other page
      -
      +
      <a href=@[AnotherPageR]>Go to the other page
      +

      As AnotherPageR is of type URL and it could not contains something nefarious. It will be an URL safe. Not something like:

      -
      -falselink"><script> bad_code(); </script><a href="pipo
      -
      +
      falselink"><script> bad_code(); </script><a href="pipo
      +

      Widgets

      -

      Yesod’s widgets are different from javascript widget. +

      Yesod’s widgets are different from javascript widget. For yesod, widgets are sets of small parts of a web application. If you want to use many widgets in a same page yesod do the work. Some examples of widget are:

      @@ -114,7 +115,7 @@ Some examples of widget are:

    • the footer of a webpage,
    • the header of a webpage with a menu,
    • a button which appears only when scrolling down,
    • -
    • etc…
    • +
    • etc…
    • For each of this part, you might need,

      @@ -137,11 +138,10 @@ htmlbody = ...

      The real syntax is:

      -
      -toWidgetHeader cassiusFile "button.cassius"
      -toWidgetHeader juliusFile "button.julius"
      -toWidget       hamletFile "buttonTemplate.hamlet"
      -
      +
      toWidgetHeader cassiusFile "button.cassius"
      +toWidgetHeader juliusFile "button.julius"
      +toWidget       hamletFile "buttonTemplate.hamlet"
      +

      Note the awesome Shakespearean inspired name convention. Another good reason to use yesod.

      @@ -154,9 +154,8 @@ Another good reason to use yesod.

      And when your page render, yesod makes it easy to render everything nicely:

      -
      -myBigWidget =  menuWidget >> contentWidget >> footerWidget
      -
      +
      myBigWidget =  menuWidget >> contentWidget >> footerWidget
      +

      Furthermore, if you use say 10 widgets each with a bit of CSS, yesod will create a unique and compressed CSS file. Except if you expressed a need to change the header by using different CSS.

      @@ -172,18 +171,16 @@ myBigWidget = menuWidget >> contentWidget >> footerWidget Therefore it can optimize it. Of course two routes must not interfere.

      -
      -/blog/2003  Date2003R
      +
      /blog/2003  Date2003R
       /blog/$DATE DateR
      -
      +
      -

      is invalid by default (you can make it valid, but I don’t think it is a good idea).

      +

      is invalid by default (you can make it valid, but I don’t think it is a good idea).

      -

      You’d better

      +

      You’d better

      -
      -/blog/$DATE DateR
      -
      +
      /blog/$DATE DateR
      +

      and test if date = 2003 inside the handler.

      @@ -195,7 +192,7 @@ Of course two routes must not interfere.

    • Good ideas, excellent community. I follow yesod from some month now and the speed at which the project progress is incredible.
    • -

      If you are a haskeller, I believe you shouldn’t fear the special syntax imposed by the standard yesod way of doing things. +

      If you are a haskeller, I believe you shouldn’t fear the special syntax imposed by the standard yesod way of doing things. Just try it more than the firsts basic tutorials.

      Until here I believe it goes in the right direction. @@ -318,7 +315,7 @@ Even if I believe the real future is by generating HTML pages from the client (u

      Created: 10/04/2011 - Modified: 10/26/2011 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore index 579b41811..813f097ef 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore @@ -1,4 +1,3 @@ - cabal-dev dist .static-cache diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs index 650f2efd7..4ba6b6040 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs @@ -1,4 +1,3 @@ - module Handler.Echo where import Import diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Mirror.hs b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Mirror.hs index 130816d6c..c7a006ad2 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Mirror.hs +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Mirror.hs @@ -1,4 +1,3 @@ - module Handler.Mirror where import Import diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/article.hamlet b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/article.hamlet index 354638ec7..6c32d426c 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/article.hamlet +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/article.hamlet @@ -1,3 +1,2 @@ -

      #{articleTitle article}
      #{articleContent article} diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/articles.hamlet b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/articles.hamlet index 71d6d1cb0..9ef45ac2c 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/articles.hamlet +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/articles.hamlet @@ -1,4 +1,3 @@ -

      Articles $if null articles -- Show a standard message if there is no article diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius index 83f57106e..b576178f7 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius @@ -1,4 +1,3 @@ - body { font-family: Helvetica, sans-serif; font-size: 18px; } diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet index 22a4b5e2a..38745cf3b 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet @@ -1,2 +1 @@ -

      #{theText} diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet index 233632918..c81d17042 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet @@ -1,4 +1,3 @@ -

      Enter your text
      diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/posted.hamlet b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/posted.hamlet index 0ee9df855..bf5ca7a62 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/posted.hamlet +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/code/posted.hamlet @@ -1,4 +1,3 @@ -

      You've just posted

      #{postedText}#{T.reverse postedText}


      diff --git a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/index.html b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/index.html index 4c5d512f0..96ee9bbf2 100644 --- a/output/Scratch/en/blog/Yesod-tutorial-for-newbies/index.html +++ b/output/Scratch/en/blog/Yesod-tutorial-for-newbies/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -68,7 +71,7 @@

      tl;dr: A simple yesod tutorial. Yesod is a Haskell web framework. -You shouldn’t need to know Haskell.

      +You shouldn’t need to know Haskell.

      Table of content
      @@ -116,18 +119,18 @@ than C, C++ or Java for example. One of the best property of Haskell being:

      -

      “If your program compile it will be - very close to what the programmer intended”.

      +

      “If your program compile it will be + very close to what the programmer intended”.

      Haskell web frameworks handle parallel tasks perfectly. For example even better than node.js3.

      -

      Thousands of Agent Smith

      +

      Thousands of Agent Smith

      From the pure technical point of view, Haskell seems to be the perfect web development tool. -Weaknesses of Haskell certainly won’t be technical:

      +Weaknesses of Haskell certainly won’t be technical:

      • Hard to grasp Haskell
      • @@ -136,7 +139,7 @@ Weaknesses of Haskell certainly won’t be technical:

      • There is no heroku for Haskell (even if Greg Weber did it, it was more a workaround).
      -

      I won’t say these are not important drawbacks. +

      I won’t say these are not important drawbacks. But, with Haskell your web application will have both properties to absorb an impressive number of parallel request securely and to adapt to change.

      @@ -149,7 +152,7 @@ and to adapt to change.

    • Yesod
    • -

      I don’t think there is a real winner between these three framework. +

      I don’t think there is a real winner between these three framework. The choice I made for yesod is highly subjective. I just lurked a bit and tried some tutorials. I had the feeling yesod make a better job at helping newcomers. @@ -161,18 +164,18 @@ Of course I might be wrong since it is a matter of feeling.

      Why did I write this article? The yesod documentation and particularly the book are excellent. But I missed an intermediate tutorial. -This tutorial won’t explain all details. +This tutorial won’t explain all details. I tried to give a step by step of how to start from a five minute tutorial to an almost production ready architecture. Furthermore explaining something to others is a great way to learn. -If you are used to Haskell and Yesod, this tutorial won’t learn you much. +If you are used to Haskell and Yesod, this tutorial won’t learn you much. If you are completely new to Haskell and Yesod it might hopefully helps you. Also if you find yourself too confused by the syntax, it might helps to read this article

      -

      During this tutorial you’ll install, initialize and configure your first yesod project. +

      During this tutorial you’ll install, initialize and configure your first yesod project. Then there is a very minimal 5 minutes yesod tutorial to heat up and verify the awesomeness of yesod. -Then we will clean up the 5 minutes tutorial to use some “best practices”. +Then we will clean up the 5 minutes tutorial to use some “best practices”. Finally there will be a more standard real world example; a minimal blog system.

      Before the real start

      @@ -185,10 +188,9 @@ is to download the Haskell PlatformOnce done, you need to install yesod. Open a terminal session and do:

      -
      -~ cabal update
      -~ cabal install yesod cabal-dev
      -
      +
      ~ cabal update
      +~ cabal install yesod cabal-dev
      +

      There are few steps but it should take some time to finish.

      @@ -197,18 +199,16 @@ Open a terminal session and do:

      You are now ready to initialize your first yesod project. Open a terminal and type:

      -
      -~ yesod init
      -
      +
      ~ yesod init
      +

      Enter your name, choose yosog for the project name and enter Yosog for the name of the Foundation. Finally choose sqlite. Now, start the development cycle:

      -
      -~ cd yosog
      -~ cabal-dev install && yesod --dev devel
      -
      +
      ~ cd yosog
      +~ cabal-dev install && yesod --dev devel
      +

      This will compile the entire project. Be patient it could take a while the first time. Once finished a server is launched and you could visit it by clicking this link:

      @@ -221,9 +221,12 @@ Once finished a server is launched and you could visit it by clicking this link: Note: if something is messed up use the following command line inside the project directory. -
      -\rm -rf dist/* ; cabal-dev install && yesod --dev devel
      -
      + + +
      \rm -rf dist/* ; cabal-dev install && yesod --dev devel
      +
      + +
      @@ -239,23 +242,21 @@ but it is a good practice.

      Copy this .gitignore file into the yosog folder.

      -
      -
      -cabal-dev
      +
      +
      +
      cabal-dev
       dist
       .static-cache
       static/tmp
       *.sqlite3
      -
      -
      +

      Then initialize your git repository:

      -
      -~ git init .
      -~ git add .
      -~ git commit -a -m "Initial yesod commit"
      -
      +
      ~ git init .
      +~ git add .
      +~ git commit -a -m "Initial yesod commit"
      +

      We are almost ready to start.

      @@ -266,7 +267,7 @@ and a local web server listening the port 3000. If we modify a file inside this directory, yesod should try to recompile as fast as possible the site. Instead of explaining the role of every file, -let’s focus only on the important files/directories for this tutorial:

      +let’s focus only on the important files/directories for this tutorial:

      1. config/routes
      2. @@ -281,7 +282,7 @@ let’s focus only on the important files/directories for this tutorial:

      - + @@ -293,13 +294,13 @@ let’s focus only on the important files/directories for this tutorial:

      - +
      The written typeThe written type Its meaning
      config/routesis where you’ll configure the map url → Code.is where you’ll configure the map url → Code.
      Handler/
      config/modelsis where you’ll configure the persistent objects (database tables).is where you’ll configure the persistent objects (database tables).
      -

      During this tutorial we’ll modify other files as well, -but we won’t explore them in detail.

      +

      During this tutorial we’ll modify other files as well, +but we won’t explore them in detail.

      Also note, shell commands are executed in the root directory of your project instead specified otherwise.

      @@ -308,16 +309,16 @@ but we won’t explore them in detail.

      Echo

      To verify the quality of the security of the yesod framework, -let’s make a minimal echo application.

      +let’s make a minimal echo application.

      Goal:

      -

      Make a server that when accessed /echo/[some text] should return a web page containing “some text” inside an h1 bloc.

      +

      Make a server that when accessed /echo/[some text] should return a web page containing “some text” inside an h1 bloc.

      In a first time, we must declare the url of the form /echo/... are meaningful. -Let’s take a look at the file config/routes:

      +Let’s take a look at the file config/routes:

       /static StaticR Static getStatic
      @@ -345,23 +346,22 @@ I am not particularly fan of the big R notation but this is the standard convent
       Application.hs:31:1: Not in scope: `getEchoR'
       
      -

      Why? Simply because we didn’t written the code for the handler EchoR. +

      Why? Simply because we didn’t written the code for the handler EchoR. Edit the file Handler/Home.hs and append this:

      -
      -getEchoR :: String -> Handler RepHtml
      -getEchoR theText = do
      -    defaultLayout $ do
      +
      getEchoR :: String -> Handler RepHtml
      +getEchoR theText = do
      +    defaultLayout $ do
               [whamlet|<h1>#{theText}|]
      -
      +
      -

      Don’t worry if you find all of this a bit cryptic. +

      Don’t worry if you find all of this a bit cryptic. In short it just declare a function named getEchoR with one argument (theText) of type String. When this function is called, it return a Handler RepHtml whatever it is. But mainly this will encapsulate our expected result inside an html text.

      After saving the file, you should see yesod recompile the application. -When the compilation is finished you’ll see the message: Starting devel application.

      +When the compilation is finished you’ll see the message: Starting devel application.

      Now you can visit: http://localhost:3000/echo/Yesod%20rocks!

      @@ -383,18 +383,17 @@ A malicious user could not hide some bad script inside.

      The url string is put inside a url type. Then the interesting part in the url is put inside a String type. To pass from url type to String type some transformation are made. -For example, replace all “%20” by space characters. +For example, replace all “%20” by space characters. Then to show the String inside an html document, the string is put inside an html type. -Some transformations occurs like replace “<” by “&lt;”. +Some transformations occurs like replace “<” by “&lt;”. Thanks to yesod, this tedious job is done for us.

      -
      -"http://localhost:3000/echo/some%20text<a>" :: URL
      +
      "http://localhost:3000/echo/some%20text<a>" :: URL
                           ↓
      -              "some text<a>"                :: String
      +              "some text<a>"                :: String
                           ↓
      -          "some text &lt;a&gt;"             :: Html 
      -
      + "some text <a>" :: Html +

      Yesod is not only fast, it helps us to remain secure. It protects us from many common errors in other paradigms. @@ -409,42 +408,41 @@ We will clean up many details:

    • Use a general css (cleaner than the empty by default)
    • Dispatch handler code into different files
    • Use Data.Text instead of String
    • -
    • Put our “views”4 inside the template directory
    • +
    • Put our “views”4 inside the template directory

    Use a better css

    It is nice to note, the default template is based on html5 boilerplate. -Let’s change the default css. +Let’s change the default css. Add a file named default-layout.lucius inside the templates/ directory containing:

    -
    -
    -body {
    -    font-family: Helvetica, sans-serif; 
    -    font-size: 18px; }
    -#main {
    -    padding: 1em;
    -    border: #CCC solid 2px;
    -    border-radius: 5px;
    -    margin: 1em;
    -    width: 37em;
    -    margin: 1em auto;
    -    background: #F2F2F2;
    -    line-height: 1.5em;
    -    color: #333; }
    -.required { margin: 1em 0; }
    -.optional { margin: 1em 0; }
    -label { width: 8em; display: inline-block; }
    -input, textarea { background: #FAFAFA}
    -textarea { width: 27em; height: 9em;}
    -ul { list-style: square; }
    -a { color: #A56; }
    -a:hover { color: #C58; }
    -a:active { color: #C58; }
    -a:visited { color: #943; }
    -
    -
    + + +
    body {
    +    font-family: Helvetica, sans-serif; 
    +    font-size: 18px; }
    +#main {
    +    padding: 1em;
    +    border: #CCC solid 2px;
    +    border-radius: 5px;
    +    margin: 1em;
    +    width: 37em;
    +    margin: 1em auto;
    +    background: #F2F2F2;
    +    line-height: 1.5em;
    +    color: #333; }
    +.required { margin: 1em 0; }
    +.optional { margin: 1em 0; }
    +label { width: 8em; display: inline-block; }
    +input, textarea { background: #FAFAFA}
    +textarea { width: 27em; height: 9em;}
    +ul { list-style: square; }
    +a { color: #A56; }
    +a:hover { color: #C58; }
    +a:active { color: #C58; }
    +a:visited { color: #943; }
    +

    Personally I would prefer if such a minimal css was put with the scaffolding tool. I am sure somebody already made such a minimal css which give the impression @@ -453,20 +451,19 @@ But I digress.

    Separate Handlers

    -

    Generally you don’t want to have all your code inside a unique file. +

    Generally you don’t want to have all your code inside a unique file. This is why we will separate our handlers. In a first time create a new file Handler/Echo.hs containing:

    -
    -module Handler.Echo where
    +
    module Handler.Echo where
     
    -import Import
    +import Import
     
    -getEchoR :: String -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    +getEchoR :: String -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
             [whamlet|<h1>#{theText}|]
    -
    +

    Do not forget to remove the getEchoR function inside Handler/Home.hs.

    @@ -478,11 +475,10 @@ Just after Handler.Home, add:

    We must also declare this new Handler module inside Application.hs. -Just after the “import Handler.Home”, add:

    +Just after the “import Handler.Home”, add:

    -
    -import Handler.Echo
    -
    +
    import Handler.Echo
    +

    This is it.

    @@ -495,9 +491,8 @@ Just after the “import Handler.Home”, add:

    To declare it, add this import directive to Foundation.hs (just after the last one):

    -
    -import Data.Text
    -
    +
    import Data.Text
    +

    We have to modify config/routes and our handler accordingly. Replace #String by #Text in config/routes:

    @@ -508,18 +503,17 @@ Replace #String by #Text in config/routes

    And do the same in Handler/Echo.hs:

    -
    -
    -module Handler.Echo where
    +
     
    -import Import
    +
    module Handler.Echo where
     
    -getEchoR :: Text -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    +import Import
    +
    +getEchoR :: Text -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
             [whamlet|<h1>#{theText}|]
    -
    -
    +

    Use templates

    @@ -527,20 +521,18 @@ getEchoR theText = do We should put this part inside another file. Create the new file templates/echo.hamlet containing:

    -
    -
    -<h1> #{theText}
    -
    -
    + + +
    <h1> #{theText}
    +

    and modify the handler Handler/Echo.hs:

    -
    -getEchoR :: Text -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    -        $(widgetFile "echo")
    -
    +
    getEchoR :: Text -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
    +        $(widgetFile "echo")
    +

    At this point, our web application is structured between different files. Handler are grouped, we use Data.Text and our views are in templates. @@ -548,13 +540,13 @@ It is the time to make a slightly more complex example.

    Mirror

    -

    Neo touching a mirror

    +

    Neo touching a mirror

    -

    Let’s make another minimal application. +

    Let’s make another minimal application. You should see a form containing a text field and a validation button. -When you enter some text (for example “Jormungad”) and validate, +When you enter some text (for example “Jormungad”) and validate, the next page present you the content and its reverse appended to it. -In our example it should return “JormungaddagnumroJ”.

    +In our example it should return “JormungaddagnumroJ”.

    First, add a new route:

    @@ -565,59 +557,56 @@ In our example it should return “JormungaddagnumroJ”.

    This time the path /mirror will accept GET and POST requests. Add the corresponding new Handler file:

    -
    -
    -module Handler.Mirror where
    +
     
    -import Import
    -import qualified Data.Text as T
    +
    module Handler.Mirror where
     
    -getMirrorR :: Handler RepHtml
    -getMirrorR = do
    -    defaultLayout $ do
    -        $(widgetFile "mirror")
    +import Import
    +import qualified Data.Text as T
     
    -postMirrorR :: Handler RepHtml
    -postMirrorR =  do
    -        postedText <- runInputPost $ ireq textField "content"
    -        defaultLayout $ do
    -            $(widgetFile "posted")
    -
    -
    +getMirrorR :: Handler RepHtml +getMirrorR = do + defaultLayout $ do + $(widgetFile "mirror") -

    Don’t forget to declare it inside yosog.cabal and Application.hs.

    +postMirrorR :: Handler RepHtml +postMirrorR = do + postedText <- runInputPost $ ireq textField "content" + defaultLayout $ do + $(widgetFile "posted") +
    + +

    Don’t forget to declare it inside yosog.cabal and Application.hs.

    We will need to use the reverse function provided by Data.Text which explain the additional import.

    -

    The only new thing here is the line that get the POST parameter named “content”. +

    The only new thing here is the line that get the POST parameter named “content”. If you want to know more detail about it and form in general you can take look at the yesod book.

    Create the two corresponding templates:

    -
    -
    -<h1> Enter your text
    -<form method=post action=@{MirrorR}>
    -    <input type=text name=content>
    -    <input type=submit>
    -
    -
    + -
    -
    -<h1>You've just posted
    -<p>#{postedText}#{T.reverse postedText}
    -<hr>
    -<p><a href=@{MirrorR}>Get back
    -
    -
    +
    <h1> Enter your text
    +<form method=post action=@{MirrorR}>
    +    <input type=text name=content>
    +    <input type=submit>
    +
    + + + +
    <h1>You've just posted
    +<p>#{postedText}#{T.reverse postedText}
    +<hr>
    +<p><a href=@{MirrorR}>Get back
    +

    And that is all. -This time, we won’t need to clean up. +This time, we won’t need to clean up. We may have used another way to generate the form -but we’ll see this in the next section.

    +but we’ll see this in the next section.

    Just try it by clicking here.

    @@ -661,24 +650,23 @@ If you forget it, there will be an error.

    After the route and the model, we write the handler. First, declare a new Handler module. Add import Handler.Blog inside Application.hs and add it into yosog.cabal. -Let’s write the content of Handler/Blog.hs. +Let’s write the content of Handler/Blog.hs. We start by declaring the module and by importing some block necessary to handle Html in forms.

    -
    -module Handler.Blog
    +
    module Handler.Blog
         ( getBlogR
         , postBlogR
         , getArticleR
         )
    -where
    +where
     
    -import Import
    +import Import
     
    --- to use Html into forms
    -import Yesod.Form.Nic (YesodNic, nicHtmlField)
    -instance YesodNic App
    -
    +-- to use Html into forms +import Yesod.Form.Nic (YesodNic, nicHtmlField) +instance YesodNic App +

    Remark: it is a best practice to add the YesodNic instance inside Foundation.hs. I put this definition here to make things easier but you should see a warning about this orphan instance. @@ -687,58 +675,55 @@ To put the include inside Foundation.hs is left as an exercice to the reader.Hint: Do not forget to put YesodNic and nicHtmlField inside the exported objects of the module.

    -
    -entryForm :: Form Article
    -entryForm = renderDivs $ Article
    -    <$> areq   textField "Title" Nothing
    -    <*> areq   nicHtmlField "Content" Nothing
    -
    +
    entryForm :: Form Article
    +entryForm = renderDivs $ Article
    +    <$> areq   textField "Title" Nothing
    +    <*> areq   nicHtmlField "Content" Nothing
    +

    This function defines a form for adding a new article. -Don’t pay attention to all the syntax. +Don’t pay attention to all the syntax. If you are curious you can take a look at Applicative Functor. You just have to remember areq is for required form input. Its arguments being: areq type label default_value.

    -
    --- The view showing the list of articles
    -getBlogR :: Handler RepHtml
    -getBlogR = do
    -    -- Get the list of articles inside the database.
    -    articles <- runDB $ selectList [] [Desc ArticleTitle]
    -    -- We'll need the two "objects": articleWidget and enctype
    -    -- to construct the form (see templates/articles.hamlet).
    +
    -- The view showing the list of articles
    +getBlogR :: Handler RepHtml
    +getBlogR = do
    +    -- Get the list of articles inside the database.
    +    articles <- runDB $ selectList [] [Desc ArticleTitle]
    +    -- We'll need the two "objects": articleWidget and enctype
    +    -- to construct the form (see templates/articles.hamlet).
         (articleWidget, enctype) <- generateFormPost entryForm
    -    defaultLayout $ do
    -        $(widgetFile "articles")
    -
    + defaultLayout $ do + $(widgetFile "articles") +

    This handler should display a list of articles. We get the list from the DB and we construct the form. Just take a look at the corresponding template:

    -
    -
    -<h1> Articles
    +
    +
    +
    <h1> Articles
     $if null articles
         -- Show a standard message if there is no article
    -    <p> There are no articles in the blog
    +    <p> There are no articles in the blog
     $else
         -- Show the list of articles
    -    <ul>
    -        $forall Entity articleId article <- articles
    -            <li> 
    -                <a href=@{ArticleR articleId} > #{articleTitle article}
    -<hr>
    -  <form method=post enctype=#{enctype}>
    -    ^{articleWidget}
    -    <div>
    -        <input type=submit value="Post New Article">
    -
    -
    + <ul> + $forall Entity articleId article <- articles + <li> + <a href=@{ArticleR articleId} > #{articleTitle article} +<hr> + <form method=post enctype=#{enctype}> + ^{articleWidget} + <div> + <input type=submit value="Post New Article"> +

    You should remark we added some logic inside the template. -There is a test and a “loop”.

    +There is a test and a “loop”.

    Another very interesting part is the creation of the form. The articleWidget was created by yesod. @@ -749,20 +734,19 @@ But we have to create the submit button.

    Get back to Handler/Blog.hs.

    -
    --- we continue Handler/Blog.hs
    -postBlogR :: Handler RepHtml
    -postBlogR = do
    +
    -- we continue Handler/Blog.hs
    +postBlogR :: Handler RepHtml
    +postBlogR = do
         ((res,articleWidget),enctype) <- runFormPost entryForm
    -    case res of 
    -         FormSuccess article -> do 
    +    case res of 
    +         FormSuccess article -> do 
                 articleId <- runDB $ insert article
    -            setMessage $ toHtml $ (articleTitle article) <> " created"
    -            redirect $ ArticleR articleId 
    -         _ -> defaultLayout $ do
    -                setTitle "Please correct your entry form"
    -                $(widgetFile "articleAddError")
    -
    + setMessage $ toHtml $ (articleTitle article) <> " created" + redirect $ ArticleR articleId + _ -> defaultLayout $ do + setTitle "Please correct your entry form" + $(widgetFile "articleAddError") +

    This function should be used to create a new article. We handle the form response. @@ -778,47 +762,43 @@ If things goes right:

    Here is the content of the error Page:

    -
    -<form method=post enctype=#{enctype}>
    +
    <form method=post enctype=#{enctype}>
         ^{articleWidget}
    -    <div>
    -        <input type=submit value="Post New Article">
    -
    + <div> + <input type=submit value="Post New Article"> +

    Finally we need to display an article:

    -
    -getArticleR :: ArticleId -> Handler RepHtml
    -getArticleR articleId = do
    +
    getArticleR :: ArticleId -> Handler RepHtml
    +getArticleR articleId = do
         article <- runDB $ get404 articleId
    -    defaultLayout $ do
    +    defaultLayout $ do
             setTitle $ toHtml $ articleTitle article
    -        $(widgetFile "article")
    -
    + $(widgetFile "article") +

    The get404 function try to do a get on the DB. If it fails it return a 404 page. The rest should be clear. Here is the content of templates/article.hamlet:

    -
    -
    -<h1> #{articleTitle article}
    -<article> #{articleContent article}
    -
    -
    + + +
    <h1> #{articleTitle article}
    +<article> #{articleContent article}
    +

    The blog system is finished. Just for fun, you can try to create an article with the following content:

    -
    -<p>A last try to <em>cross script</em> 
    -   and <em>SQL injection</em></p>
    -<p>Here is the first try: 
    -   <script>alert("You loose");</script></p>
    -<p> And Here is the last </p>
    -"); DROP TABLE ARTICLE;;
    -
    +
    <p>A last try to <em>cross script</em> 
    +   and <em>SQL injection</em></p>
    +<p>Here is the first try: 
    +   <script>alert("You loose");</script></p>
    +<p> And Here is the last </p>
    +"); DROP TABLE ARTICLE;;
    +

    Conclusion

    @@ -829,11 +809,11 @@ I made it very minimal.

    you should take a look at the recent i18n blog tutorial. It will be obvious I inspired my own tutorial on it. -You’ll learn in a very straightforward way how easy it is to use authorizations, +You’ll learn in a very straightforward way how easy it is to use authorizations, Time and internationalization.

    -

    If, on the other hand you don’t know Haskell. -Then you shouldn’t jump directly to web programming. +

    If, on the other hand you don’t know Haskell. +Then you shouldn’t jump directly to web programming. Haskell is a very complex and unusual language. My advice to go as fast as possible in using Haskell for web programming is:

    @@ -852,7 +832,7 @@ My advice to go as fast as possible in using Haskell for web programming is:

  • Use hlint as soon as possible to get good habits.
  • -

    As you should see, if you don’t already know Haskell, +

    As you should see, if you don’t already know Haskell, the path is long but I guaranty you it will be very rewarding!

    ps: You can download the source of this yesod blog tutorial at @@ -869,7 +849,7 @@ the path is long but I guaranty you it will be very rewarding!

    If you are curious, you can search about the Fibonacci node.js troll. Without any tweaking, Haskell handled this problem perfectly. I tested it myself using yesod instead of Snap.

  • -

    By view I mean yesod widget’s hamlet, lucius and julius files.

    +

    By view I mean yesod widget’s hamlet, lucius and julius files.

  • @@ -985,7 +965,7 @@ the path is long but I guaranty you it will be very rewarding!

    Created: 01/15/2012 - Modified: 04/11/2012 + Modified: 04/26/2012
    Entirely done with diff --git a/output/Scratch/en/blog/feed/feed.xml b/output/Scratch/en/blog/feed/feed.xml index e5e872644..868ec9d41 100644 --- a/output/Scratch/en/blog/feed/feed.xml +++ b/output/Scratch/en/blog/feed/feed.xml @@ -38,10 +38,10 @@ <li><a href="#introduction">Introduction</a> <ul> <li><a href="#install">Install</a></li> - <li><a href="#don-t-be-afraid">Don&rsquo;t be afraid</a></li> + <li><a href="#don-t-be-afraid">Don’t be afraid</a></li> <li><a href="#very-basic-haskell">Very basic Haskell</a> <ul> -...</ul></li></ul></li></ul></div></hr></center></blockquote></div></p> + ...</ul></li></ul></li></ul></div></hr></center></blockquote></div></p> tag:yannesposito.com,2012-02-02:/Scratch/en/blog/Typography-and-the-Web/ @@ -53,13 +53,13 @@ yannesposito.com - <p><img alt="Screenshot of first in small caps with and without ligatures." src="/Scratch/img/blog/Typography-and-the-Web/first_sc_screenshot.png" /></p> + <p><img alt="Screenshot of first in small caps with and without ligatures." src="/Scratch/img/blog/Typography-and-the-Web/first_sc_screenshot.png" />;</p> <div class="intro"> -<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> Web typography sucks and we&rsquo;ll have to wait forever before it will be fixed.</p> +<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> Web typography sucks and we’ll have to wait forever before it will be fixed.</p> </div> @@ -68,13 +68,13 @@ <p>I stumbled upon <a href="http://opentypography.org/">open typography</a>. Their main message is:</p> <blockquote> - <p>«There is no reason to wait for browser development to catch up. -We can all create better web typography ourselves, today.»</p> + <p>«There is no reason to wait for browser development to catch up. +We can all create better web typography ourselves, today.»</p> </blockquote> <p>As somebody who tried to make my website using some nice typography features and in particular <em>ligatures</em>, I believe this is wrong.</p> -<p>I already made an automa...</p></p> +<p>I already made an automatic sys...</p></p> tag:yannesposito.com,2012-01-15:/Scratch/en/blog/Yesod-tutorial-for-newbies/ @@ -96,7 +96,7 @@ We can all create better web typography ourselves, today.»</p> <p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> A simple yesod tutorial. Yesod is a Haskell web framework. -You shouldn&rsquo;t need to know Haskell. </p> +You shouldn’t need to know Haskell. </p> <blockquote> <center><span class="sc"><b>Table of content</b></span></center> @@ -109,7 +109,7 @@ You shouldn&rsquo;t need to know Haskell. </p> <li><a href="#some-last-minute-words">Some last minute words</a></li> </ul> </li> - ...</ul></blockquote></div></p> + <li></li></ul></blockquote></div></p> tag:yannesposito.com,2011-10-20:/Scratch/en/blog/SVG-and-m4-fractals/ @@ -159,7 +159,7 @@ Then some believed it would be a good idea to invent many <span class="sc"> <p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span></p> <p><a href="http://www.yesodweb.com">Yesod</a> is a web framework which recently reached the maturity for which you should consider to use it. -Before telling you why you should learn Haskell and use yesod, I will talk about ideas yesod introduced and I didn&rsquo;t saw in other frameworks before.</p> +Before telling you why you should learn Haskell and use yesod, I will talk about ideas yesod introduced and I didn’t saw in other frameworks before.</p> </div> @@ -167,11 +167,11 @@ Before telling you why you should learn Haskell and use yesod, I will talk about <h2 id="type-safety">Type safety</h2> -<p>Let&rsquo;s start by an obligatory link from <a href="http://xkcd.com">xkcd</a>:</p> +<p>Let’s start by an obligatory link from <a href="http://xkcd.com">xkcd</a>:</p> <p><img src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" alt="SQL injection by a mom" /></p> -<p>When you create a web application, a lot of time is spent...</p></p></p> +<p>When you create a web application, a lot of time is spent dealing wit...</p></p></p> tag:yannesposito.com,2011-09-28:/Scratch/en/blog/Higher-order-function-in-zsh/ @@ -200,13 +200,14 @@ Simply because, the more I programmed with zsh the more I tended to work using f <p>The minimal to have better code are the functions <code>map</code>, <code>filter</code> and <code>fold</code>.</p> -<p>Let&rsquo;s compare. +<p>Let’s compare. First a program which convert all gif to png in many different directories of different projects.</p> -<p>Before ⇒</p> +<p>Before ⇒</p> -<pre class="twilight"> -<span class="Comment"><span class="Comment">#</span> for each directory in...</span></pre></p> +<pre><code class="zsh"># for each directory in projects dir +for toProject in /path/to/projects/*(/N); do +...</code></pre></p> tag:yannesposito.com,2011-09-28:/Scratch/en/blog/programming-language-experience/ @@ -230,15 +231,16 @@ First a program which convert all gif to png in many different directories of di <h3 id="basic"><code>BASIC</code></h3> -<p><img alt="Title image" src="/Scratch/img/blog/programming-language-experience/basic.gif" class="left" /> -The language of my firsts programs! +<p><img alt="Title image" src="/Scratch/img/blog/programming-language-experience/basic.gif" class=" left" /></p> + +<p>The language of my firsts programs! I was about 10, with an <code>MO5</code> and <code>Amstrad CPC 6128</code> and even with my <code>Atari STe</code>. This is the language of <code>GOTO</code>s. -Ô nostalgia. +Ô nostalgia. Unfortunately this might be the only interesting part of this language.</p> <p>Today this language is obsolescent. -It is not even good to learn programming...</p></p></p> +It is not even good to learn pro...</p></p></p> tag:yannesposito.com,2011-08-25:/Scratch/en/blog/Learn-Vim-Progressively/ @@ -250,7 +252,7 @@ It is not even good to learn programming...</p></p></p>yannesposito.com - <p><img alt="Über leet use vim!" src="/Scratch/img/blog/Learn-Vim-Progressively/uber_leet_use_vim.jpg" /></p> + <p><img alt="Über leet use vim!" src="/Scratch/img/blog/Learn-Vim-Progressively/uber_leet_use_vim.jpg" /></p> <div class="intro"> @@ -269,14 +271,14 @@ It is not even good to learn programming...</p></p></p> + <li>Fe...</li></ol></p> tag:yannesposito.com,2011-08-17:/Scratch/en/blog/A-more-convenient-diff/ @@ -292,17 +294,23 @@ Hard to learn, but incredible to use.</p> <p>This is why, when you use <code>git</code> it will use a better formatting and colorize it.</p> -<p>Here is the script I use when I want to use human readable <code>diff</code> à la git. </p> +<p>Here is the script I use when I want to use human readable <code>diff</code> à la git. </p> -<div class="code"><div class="file"><a href="/Scratch/en/blog/A-more-convenient-diff/code/ydiff"> &#x27A5; ydiff </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Comment"><span class="Comment">#</span>!/usr/bin/env zsh</span> +<div class="codefile"><a href="/Scratch/en/blog/A-more-convenient-diff/code/ydiff">&#x27A5; ydiff</a></div> -<span class="Comment"><span class="Comment">#</span> Load colors helpers</span> -autoload -U colors <span class="Keyword">&amp;&amp;</span> colors +<pre><code class="zsh">#!/usr/bin/env zsh + +# Load colors helpers +autoload -U colors &amp;&amp; colors function colorize_diff { - <span class="Keyword">while</span> read line<span class="Keyword">;</span> </pre></div></div> + while read line; do + case ${line[0]} in + +) print -n $fg[green];; + -) print -n $fg[red];; + @) # Display in cyan the @@ positions @@ + if [[ ${line[1]} = '@' ]]; then + line=$(print $line | perl -pe 's#(\@\@[^\@]*\@\@)(.*)$#'$fg[cyan]'$1'$reset_...</code></pre> tag:yannesposito.com,2011-07-10:/Scratch/en/blog/Haskell-Mandelbrot/ @@ -316,11 +324,16 @@ function colorize_diff { <p>Here is the obfuscated code:</p> -<div class="code"><div class="file"><a href="/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs"> &#x27A5; animandel.hs </a></div><div class="withfile"> -<pre class="twilight"> -a=27;b=79;c=<span class="Constant">C</span>(-2.0,-1.0);d=<span class="Constant">C</span>(1.0,1.0);e=<span class="Constant">C</span>(-2.501,-1.003) -<span class="Keyword">newtype</span> <span class="Constant">C</span> = <span class="Constant">C</span> (<span class="Constant">Double</span>,<span class="Constant">Double</span>) <span class="Keyword">deriving</span> (<span class="Constant">Show</span>,<span class="Constant">Eq</span>) -<span class="Keyword">instance</span> <span class="Constant">Num</span> <span class="Constant">C</span> <span class="Keyword">where</span> </pre></div></div> +<div class="codefile"><a href="/Scratch/en/blog/Haskell-Mandelbrot/code/animandel.hs">&#x27A5; animandel.hs</a></div> + +<pre><code class="haskell">a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003) +newtype C = C (Double,Double) deriving (Show,Eq) +instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0) +r(C(x,y))=x;i(C(x,y))=y +f c z 0=0;f c z n=if(r(abs(z))&gt;2)then n else f c ((z*z)+c) (n-1) +h j k = map (\z-&gt;(f (C z) (C(0,0)) 32,(fst z&gt;l - q/2))) [(x,y)|y&lt;-[p,(p+((o-p)/a))..o],x&lt;-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b +u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&amp;$@#"!!i):rst p;rst True="\n";rst False="" +main = putStrLn $ im 0 ...</code></pre> tag:yannesposito.com,2011-05-18:/Scratch/en/blog/Password-Management/ @@ -348,9 +361,9 @@ I use a different password on all website.</p> <p>Disclamer, this is an unashamed attempt to make you download my iPhone app&nbsp;;-). -You&rsquo;re always here? -Even if you won&rsquo;t download my app, you should read more. -My method doesn&rsquo;t necessitate my app. +You’re always here? +Even if you won’t download my app, you should read more. +My method doesn’t necessitate my app. It is both safe and easy to use everyday.</p> <p>If you just want to <em>use</em> the tools without searching to understand why it is safe, just jump at the </p></p></div></p> @@ -388,7 +401,7 @@ It is both safe and easy to use everyday.</p> <p><em>Update</em>: I might change my mind now. Why? I just discovered a <a href="https://github.com/rstacruz/js2coffee">js2coffee converter</a>. Furthermore Denis Knauf told me about a <code>CoffeeScript.eval</code> function. -And as Denis said: &ldquo;it is time to use Coffeescript as a javascript with Ruby-like syntax not a Ruby-like programming language&rdquo;.</p> +And as Denis said: “it is time to use Coffeescript as a javascript with Ruby-like syntax not a Ruby-like programming language”.</p> </div> @@ -467,7 +480,7 @@ When we are used to <span style="text-transform: uppercase">L<sup style <p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> Played to process a <code>wav</code> file. <code>C</code> was easier and cleaner than Ruby.</p> -<p>edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didn&rsquo;t had any portability consideration. This is only a <em>hack</em>.</p> +<p>edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didn’t had any portability consideration. This is only a <em>hack</em>.</p> </div> @@ -476,11 +489,11 @@ When we are used to <span style="text-transform: uppercase">L<sup style <p>I had to compute the sum of the absolute values of data of a <code>.wav</code> file. For efficiency (and fun) reasons, I had chosen <code>C</code> language.</p> -<p>I didn&rsquo;t programmed in <code>C</code> for a long time. +<p>I didn’t programmed in <code>C</code> for a long time. From my memory it was a pain to read and write to files. But in the end I was really impressed by the code I get. It was really clean. -This is even more impressive know...</p> +This is even more impressive knowing I used m...</p> tag:yannesposito.com,2010-10-10:/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ @@ -492,7 +505,7 @@ This is even more impressive know...</p> yannesposito.com - <p><img alt="Title image" src="/Scratch/img/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/main.png" class="clean" /></p> + <p><img alt="Title image" src="/Scratch/img/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/main.png" class="clean " /></p> <div class="intro"> @@ -513,12 +526,12 @@ This is even more impressive know...</p> </div> -<p>I&rsquo;ve (re)discovered how to become S/MIME compliant. +<p>I’ve (re)discovered how to become S/MIME compliant. I am now suprised how easy it was. Some years ago it was far more difficult. -Now I&rsquo;m able to sign and encrypt my emails.</p> +Now I’m able to sign and encrypt my emails.</p> -</p> +<h2 id="why-is-it-important">Why is it...</h2></p> tag:yannesposito.com,2010-10-06:/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/ @@ -540,7 +553,7 @@ But the major problem came from, <code>font-shadow</code> and gradie Then my new design obey to the following rules:</p> <ul> - <li>no CSS element begining by &lsquo;-moz&rsquo; or &lsquo;-webkit&rsquo;, etc&hellip;,</li> + <li>no CSS element begining by ‘-moz’ or ‘-webkit’, etc…,</li> <li>no text shadow,</li> <li>clean (I mean delete) most javascript.</li> </ul> @@ -561,15 +574,17 @@ Then my new design obey to the following rules:</p> <p>You can remark at the bottom of each page I provide a last modification date. This label was first calculated using the <code>mtime</code> of the file on the file system. But many times I modify this date just to force some recompilation. -Therefore the date wasn&rsquo;t a date of <em>real</em> modification.</p> +Therefore the date wasn’t a date of <em>real</em> modification.</p> <p>I use <a href="http://git-scm.org">git</a> to version my website. And fortunately I can know the last date of <em>real</em> change of a file. This is how I do this with <a href="http://nanoc.stoneship.org">nanoc</a>:</p> -<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb"> &#x27A5; gitmtime.rb </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Keyword">def</span> </pre></div></div> +<div class="codefile"><a href="/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb">&#x27A5; gitmtime.rb</a></div> + +<pre><code class="ruby">def gitmtime + filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'') + ext...</code></pre> tag:yannesposito.com,2010-09-02:/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/ @@ -586,12 +601,19 @@ here are two functions to add to your code to have <code>base64</code&g <p>To use it, simply copy the code in your class and use as this:</p> -<pre class="twilight"> -<span class="CCCPreprocessorLine">#<span class="CCCPreprocessorDirective">import</span> <span class="String"><span class="String">&lt;</span>CommonCrypto/CommonDigest.h<span class="String">&gt;</span></span></span> +<pre><code class="objective-c">#import &lt;CommonCrypto/CommonDigest.h&gt; ... -<span class="Support">NSString</span> *b64_hash = [<span class="Variable">self</span> <span class="SupportFunction">b64_sha1<span class="SupportFunction">:</span></span><span class="String"><span class="String">@&quot;</span>some NSString to be sha1'ed<span class="String">&quot;</span></span>]; +NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"]; ... -</pre> +NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"]; +</code></pre> + +<p>The <code>base64</code> algorithm must be programmed by hand on iPhone!</p> + +<div class="codefile"><a href="/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c">&#x27A5; iphone_base64_sha1.c</a></div> + +<pre><code class="c"> +- (unsigned char *)sha1:(NSString *)baseString result:(...</code></pre> tag:yannesposito.com,2010-08-31:/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ @@ -604,25 +626,26 @@ here are two functions to add to your code to have <code>base64</code&g <p>I had to send a mail using only command line. -I was surprised it isn&rsquo;t straightforward at all. -I didn&rsquo;t had <code>pine</code> nor <code>mutt</code> or anything like that. +I was surprised it isn’t straightforward at all. +I didn’t had <code>pine</code> nor <code>mutt</code> or anything like that. Just <code>mail</code> and <code>mailx</code>.</p> <p>What Internet say (via google) is</p> -<pre class="twilight"> -uuencode fic.jpg fic.jpg <span class="Keyword">|</span> mail -s <span class="String"><span class="String">'</span>Subject<span class="String">'</span></span> -</pre> +<pre><code class="zsh">uuencode fic.jpg fic.jpg | mail -s 'Subject' +</code></pre> <p>I tried it. And it works almost each times. -But for my file, it didn&rsquo;t worked. +But for my file, it didn’t worked. I compressed it to <code>.gz</code>, <code>.bz2</code> and <code>.zip</code>. Using <code>.bz2</code> format it worked nicely, but not with other formats. Instead of having an attached file I saw this in my email.</p> <pre> -begin 664 fic....</pre> +begin 664 fic.jpg +M(R$O=7-R+V)I;B]E;G8@&gt;G-H"GAL&lt;STD,0H*9F]R(&QI;F4@:6X@)"@\("1X +M;',@*0H@("`@9&amp;-R/20H96-H;R`D;&amp;EN92!\(&amp;%...</pre> tag:yannesposito.com,2010-08-23:/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/ @@ -645,9 +668,12 @@ But here is the conf to make it work on heroku.</p> <p>The root of my files is <code>/output</code>. You only need to create a <code>config.ru</code><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> file:</p> -<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru"> &#x27A5; config.ru </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Keyword">require</span> </pre></div></div> +<div class="codefile"><a href="/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru">&#x27A5; config.ru</a></div> + +<pre><code class="ruby">require 'rubygems' +require 'rack' +require 'rack/contrib' +require 'rack-rewr...</code></pre> tag:yannesposito.com,2010-08-11:/Scratch/en/blog/2010-07-09-Indecidabilities/ @@ -686,10 +712,10 @@ But here is the conf to make it work on heroku.</p> <p>If a demiurge made our world, he certainly had a great sense of humor. After this read, you should be convinced. -I&rsquo;ll pretend to be him. -I&rsquo;ll create a simplified world. +I’ll pretend to be him. +I’ll create a simplified world. A world that obey to simple mathematical rules. -And I&rsquo;ll tell you about one of ...</p> +And I’ll tell you about one of the curse on this ...</p> tag:yannesposito.com,2010-07-31:/Scratch/en/blog/2010-07-31-New-style-after-holidays/ @@ -720,7 +746,7 @@ I was inspired by Readability and iBooks<small>&copy;</small> (t <p>Some <a href="http://reddit.com">Reddit</a> users reported my website was really long to load and to scroll. -They thinks it was because of the &lsquo;1px shadow&rsquo; I apply on all the text. +They thinks it was because of the ‘1px shadow’ I apply on all the text. I was a bit surprised, because I make some test into a really <em>slow</em> virtual machine. And all have always worked fine. In fact, what slow down so much are by order of importance:</p> <ol> @@ -732,7 +758,7 @@ I was a bit surprised, because I make some test into a really <em>slow< <p>On Safari on Mac there is absolutely no rendering time problem. But when I use Chrome under Linux it is almost unusable.</p> -<p>Safari and Chrome use webkit, when you access my website with javascript enabled, an additionnal browser specific CSS is loaded....</p> +<p>Safari and Chrome use webkit, when you access my website with javascript enabled, an additionnal browser specific CSS is loaded. Until now I...</p> tag:yannesposito.com,2010-07-05:/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/ @@ -754,7 +780,7 @@ I was a bit surprised, because I make some test into a really <em>slow< <li>Tried to make <a href="http://yannesposito.com/Softwares/YPassword.html">YPassword</a> in jQuery and with Cappuccino.</li> <li>Cappuccino nice in desktop browser but 1.4MB, not compatible with iPhone.</li> <li>jQuery not as nice as the Cappuccino version but 106KB. iPhone compatible.</li> - <li>I&rsquo;ll give a try to Dashcode 3.</li> + <li>I’ll give a try to Dashcode 3.</li> </ul> @@ -767,7 +793,7 @@ I was a bit surprised, because I make some test into a really <em>slow< <p>Before start, I must say I know Cappuccino and jQuery are no more comparable than Cocoa and the C++ standard library. One is oriented for user interface while the other is and helper for low level programming. -Nonetheless I used these two to make the same web application. This is why I compare the exp...</p></div></hr> +Nonetheless I used these two to make the same web application. This is why I compare the experienc...</p></div></hr> tag:yannesposito.com,2010-06-19:/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/ @@ -781,12 +807,26 @@ Nonetheless I used these two to make the same web application. This is why I com <p>Here is a fast and easy way to create jQuery popup.</p> -<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js"> &#x27A5; essai.js </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Comment"><span class="Comment">//</span> --- code popup ---</span> -<span class="Storage">function</span> <span class="Entity">openPopup</span>() { - <span class="Keyword">$</span>(<span class="Variable">this</span>).clone(<span class="Constant">false</span>).appendTo(<span class="Keyword">$</span>(<span class="String"><span class="String">&quot;</span>#_code<span class="String">&quot;</span></span>)); - <span class="Keyword">$</span>(<span class="String"><span class="String">&quot;</span>#_code<span class="String">&quot;</span></span></pre></div></div> +<div class="codefile"><a href="/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js">&#x27A5; essai.js</a></div> + +<pre><code class="javascript">// --- code popup --- +function openPopup() { + $(this).clone(false).appendTo($("#_code")); + $("#_code").show(); +} + +function closePopup() { + $("#_code").html(""); + $("#_code").hide(); +} + +function initCode() { + $(".code").click(openPopup); + $(".code").css({cursor: "pointer"}); + $('body').append('&lt;div id="_code"&gt;&lt;/div&gt;'); + $('#_code').css( { 'text-align': "justify", position: "fixed", + left:0, top:0, width: "100%", height: "100%", + "background-color": "rgba(0, 0, 0, 0.8)", 'z-index':2000, 'pad...</code></pre> tag:yannesposito.com,2010-06-17:/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/ @@ -805,10 +845,17 @@ First you should look on how <a href="/Scratch/en/blog/2010-06-17-track-event <p>I check if the key <code>admin</code> is not set in the cookie before adding the visit.</p> -<pre class="twilight"> - <span class="Storage">var</span> admin <span class="Keyword">=</span> <span class="Keyword">$</span>.<span class="SupportConstant">cookie</span>(<span class="String"><span class="String">'</span>admin<span class="String">'</span></span>); - <span class="Keyword">if</span> (<span class="Keyword">!</span> admin) { - ...</pre> +<pre><code class="javascript"> var admin = $.cookie('admin'); + if (! admin) { + // put your analytics code here + } else { + console.log("[WARNING] you're HIDDEN to analytics"); + } +</code></pre> + +<p>then create two <span class="sc">html</span> files. One to hide:</p> + +<div class="codefile"></div> tag:yannesposito.com,2010-06-17:/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/ @@ -824,8 +871,18 @@ First you should look on how <a href="/Scratch/en/blog/2010-06-17-track-event <p>First in your <span class="sc">html</span> you need to use <a href="http://jquery.com">jQuery</a> and a javscript file I named <code>yga.js</code>:</p> -<pre class="twilight"> -<span class="EmbeddedSource"> <span class="EmbeddedSource">&lt;</span><span class="MetaTagInline">script</span> <span class="MetaTagInline">type</span>=<span class="String"><span class="String">&quot;</span>text/javascript<span class="String">&quot;</span></span> <span class="MetaTagInline">src</span>=<span class="String"><span class="String">&quot;</span>jquery.js<span class="String">&quot;</span></span><span class="EmbeddedSource">&gt;</span><span class="EmbeddedSource">&lt;/</span><span class="MetaTagInline">scr...</span></span></pre> +<pre><code class="html"> &lt;script type="text/javascript" src="jquery.js"&gt;&lt;/script&gt; + &lt;script type="text/javascript" src="yga.js"&gt;&lt;/script&gt; +</code></pre> + +<p>And here is the <code>yga.js</code> file:</p> + +<div class="codefile"><a href="/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/code/yga.js">&#x27A5; yga.js</a></div> + +<pre><code class="javascript">$(document).ready( function() { + // add an event to all link for google analytics + $('a').click(function () { + // tell analytics to save ...</code></pre> tag:yannesposito.com,2010-06-15:/Scratch/en/blog/2010-06-15-Get-my-blog-engine/ @@ -849,7 +906,7 @@ You can get it on <a href="http://github.com/yogsototh/nanoc3_blog">github <li><a href="http://intensedebate.org">intenseDebate</a> comments integration (asynchronous)&nbsp;;</li> <li>Portable with and without javascript, XHTML Strict 1.0 / CSS3,</li> <li>Write in markdown format (no HTML editing needed),</li> - <li>Typographic ameliorations (no &lsquo;:&rsquo; ...</li></ul> + <li>Typographic ameliorations (no ‘:’ starting a l...</li></ul> tag:yannesposito.com,2010-06-14:/Scratch/en/blog/2010-06-14-multi-language-choices/ @@ -867,7 +924,7 @@ Most people advice me to have one file per language. Generally it ends with:< <pre class="twilight"> Bonjour, -voici un exemple de texte en français. +voici un exemple de texte en français. [image](url) </pre> @@ -880,12 +937,12 @@ here is an example of english text. <p>This way of handling translations force you to write completely an article in one language, copy it, and translate it.</p> -<p>However, most of time, there are common parts like images, source code, etc&hellip; +<p>However, most of time, there are common parts like images, source code, etc… When I want to correct some mistake on these parts, I have to make twice the work. With sometimes adding another mistake in only one language.</p> <p>This is why I preferred to handle it differently. I use <em>tags</em> on a single file. -Finally my files...</p> +Finally my files looks l...</p> tag:yannesposito.com,2010-05-24:/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ @@ -910,7 +967,7 @@ Finally my files...</p> <li>Used a pen and a sheet of paper</li> <li>Made some math.</li> <li>Crushed the problem in 10 minutes</li> - <li>Conclusion: The pragmatism shouldn&rsquo;t mean &ldquo;never use theory&rdquo;.</li> + <li>Conclusion: The pragmatism shouldn’t mean “never use theory”.</li> </ul> @@ -921,7 +978,7 @@ Finally my files...</p> <p>For my job, I needed to resolve a problem. It first seems not too hard. Then I started working directly on my program. -I entered in the <em>infernal</em>: ...</p> +I entered in the <em>infernal</em>: <em>try &amp; repa...</em></p> tag:yannesposito.com,2010-05-19:/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ @@ -937,9 +994,24 @@ I entered in the <em>infernal</em>: ...</p> <p>Here is an example:</p> -<pre class="twilight"> -<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span> <span class="MetaTagAll">class</span>=<span class="String"><span class="String">&quot;</span>corps<span class="String">&quot;</span></span><span class="MetaTagAll">&gt;</span></span> - <span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span> <span class="MetaTagAll">class</span>=<span class="String"><span class="String">&quot;</span>intro</span></span></pre> +<pre><code class="html">&lt;div class="corps"&gt; + &lt;div class="intro"&gt; + &lt;p&gt;Introduction&lt;/p&gt; + &lt;/div&gt; + &lt;p&gt;The first paragraph&lt;/p&gt; + &lt;img src="/img/img.png" alt="an image"/&gt; + &lt;p&gt;Another long paragraph&lt;/p&gt; +&lt;/div&gt; +</code></pre> + +<p>After the cut, I obtain:</p> + +<pre><code class="html">&lt;div class="corps"&gt; + &lt;div class="intro"&gt; + &lt;p&gt;Introduction&lt;/p&gt; + &lt;/div&gt; + &lt;p&gt;The first paragraph&lt;/p&gt; + ...</code></pre> tag:yannesposito.com,2010-05-17:/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/ @@ -957,14 +1029,14 @@ I entered in the <em>infernal</em>: ...</p> <p>The more you wait to do something, the more difficult it is to start doing it.</p> </blockquote> -<p>I had to write another post for this blog. I had added many article idea in my todolist. But, I made many other things, and I&rsquo;ve always said (until now), I&rsquo;ll do this later. What changed my mind is the haunt of this simple remark about how to be productive in programming. +<p>I had to write another post for this blog. I had added many article idea in my todolist. But, I made many other things, and I’ve always said (until now), I’ll do this later. What changed my mind is the haunt of this simple remark about how to be productive in programming. &gt; Stop write <code>TODO</code> in your code and make it now!<br /> -&gt; You&rsquo;ll be surprised by the results.</p> +&gt; You’ll be surprised by the results.</p> <p>In short: &gt; <strong>Just do it!</strong> ou <strong>Juste fait le</strong> comme auraient dit les nuls.</p> -<p>Finally I&rsquo;ll certainly write blog post more often for a ...</p></p> +<p>Finally I’ll certainly write blog post more often for a short period of time.</p></p> tag:yannesposito.com,2010-03-23:/Scratch/en/blog/2010-03-23-Encapsulate-git/ @@ -1010,32 +1082,39 @@ clientB: project adapted for client B</p> <p>Standard:</p> -<div><pre class="twilight"> -git clone git@github.com:yogsototh/project.git -</pre></div> +<div> + +<pre><code class="zsh">git clone git@github.com:yogsototh/project.git +</code></pre> + +</div> <p>Using HTTPS port:</p> -<div><pre class="twilight"> -git clone git+ssh://git@github.com:443/yogsototh/project.git -</pre></div> +<div> + +<pre><code class="zsh">git clone git+ssh://git@github.com:443/yogsototh/project.git +</code></pre> + +</div> <h2 id="clone-all-branches">clone all branches</h2> <p><code>git clone</code> can only fetch the master branch.</p> -<p>If you don&rsquo;t have much branches, you can simply use clone your project and then use the following command:</p> +<p>If you don’t have much branches, you can simply use clone your project and then use the following command:</p> -<div><pre class="twilight"> -git branch --track local_branch remote_branch -</pre></div> +<div> + +<pre><code class="zsh">git branch --track local_branch remote_branch +</code></pre> + +</div> <p>for example:</p> -<div><pre class="twilight"> -$ git clone git@github:yogsototh/example.git -$ git branch -master * -$ git ...</pre></div> +<div> + +<pre><code class="zsh">$ git clone git@github:yogsototh/example.git...</code></pre></div> tag:yannesposito.com,2010-02-23:/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/ @@ -1052,10 +1131,24 @@ Particularly when transformations you want to make are easy.</p> <p>I wanted to know how to get file extension from filename the fastest way possible. There is 3 natural way of doing this:</p> -<div><pre class="twilight"> -<span class="Comment"><span class="Comment">#</span> regexp</span> -str.<span class="Entity">match</span>(<span class="StringRegexp"><span class="StringRegexp">/</span></span><span class="StringRegexp"><span class="StringRegexp"><span class="StringRegexp">[</span>^.<span class="StringRegexp">]</span></span>*$</span><span class="StringRegexp"><span class="StringRegexp">/</span></span>); -ext<span class="Keyword">=</span><span class="Variable"><span class="Variable">...</span></span></pre></div> +<div> + +<pre><code class="ruby"># regexp +str.match(/[^.]*$/); +ext=$&amp; + +# split +ext=str.split('.')[-1] + +# File module +ext=File.extname(str) +</code></pre> + +</div> + +<p>At first sight I believed that the regexp should be faster than the split because it could be many <code>.</code> in a filename. But in reality, most of time there is only one dot and I realized the split will be faster. But not the fastest way. There is a function dedicated to this work in the <code>File</code> module.</p> + + tag:yannesposito.com,2010-02-18:/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/ @@ -1067,17 +1160,38 @@ ext<span class="Keyword">=</span><span class="Variable"><sp yannesposito.com - <p>Strangely enough, I didn&rsquo;t find any built-in tool to split a file by keyword. I made one myself in <code>awk</code>. I put it here mostly for myself. But it could also helps someone else. + <p>Strangely enough, I didn’t find any built-in tool to split a file by keyword. I made one myself in <code>awk</code>. I put it here mostly for myself. But it could also helps someone else. The following code split a file for each line containing the word <code>UTC</code>.</p> -<div><pre class="twilight"> -<span class="Comment"><span class="Comment">#</span>!/usr/bin/env awk</span> -<span class="Entity">BEGIN</span>{i=0;} -<span class="StringRegexp"><span class="StringRegexp">/</span>UTC<span class="StringRegexp">/</span></span> { +<div> + +<pre><code class="perl">#!/usr/bin/env awk +BEGIN{i=0;} +/UTC/ { i+=1; - FIC=<span class="SupportFunction">sprintf</span>(<span class="String"><span class="String">&quot;</span>fic.%03d<span class="String">&quot;</span></span>,i); + FIC=sprintf("fic.%03d",i); } -{<span class="SupportFunction">print</span> <span class="Variable"></span></pre></div> +{print $0&gt;&gt;FIC} +</code></pre> + +</div> + +<p>In my real world example, I wanted one file per day, each line containing UTC being in the following format:</p> + +<pre class="twilight"> +Mon Dec 7 10:32:30 UTC 2009 +</pre> + +<p>I then finished with the following code:</p> + +<div> + +<pre><code class="perl">#!/usr/bin/env awk +BEGIN{i=0;} +/UTC/ { + date=$1$2$3; + if ( date != olddate ) { + oldda...</code></pre></div> tag:yannesposito.com,2010-02-16:/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/ @@ -1089,7 +1203,7 @@ The following code split a file for each line containing the word <code>UT yannesposito.com - <p>In my <a href="previouspost">previous post</a> I had given some trick to match all except something. On the same idea, the trick to match the smallest possible string. Say you want to match the string between &lsquo;a&rsquo; and &lsquo;b&rsquo;, for example, you want to match:</p> + <p>In my <a href="previouspost">previous post</a> I had given some trick to match all except something. On the same idea, the trick to match the smallest possible string. Say you want to match the string between ‘a’ and ‘b’, for example, you want to match:</p> <pre class="twilight"> a.....<span class="Constant"><strong>a......b</strong></span>..b..a....<span class="Constant"><strong>a....b</strong></span>... @@ -1106,7 +1220,7 @@ a.....<span class="Constant"><strong>a......b</strong></spa <pre class="twilight"> /a.*?b/ -<span class="Constant"><strong>...</strong></span></pre> +<span class="Constant"><strong>a.....a......b</strong></span></pre> tag:yannesposito.com,2010-02-15:/Scratch/en/blog/2010-02-15-All-but-something-regexp/ @@ -1120,16 +1234,23 @@ a.....<span class="Constant"><strong>a......b</strong></spa <p>Sometimes you cannot simply write:</p> -<div><pre class="twilight"> -<span class="Keyword">if</span> str.<span class="Entity">match</span>(regexp) <span class="Keyword">and</span> - <span class="Keyword">not</span> str.<span class="Entity">match</span>(other_regexp) +<div> + +<pre><code class="ruby">if str.match(regexp) and + not str.match(other_regexp) do_something -</pre></div> +</code></pre> + +</div> <p>and you have to make this behaviour with only one regular expression. But, there exists a major problem: the complementary of a regular language might not be regular. Then, for some expression it is absolutely impossible to negate a regular expression.</p> -<p>But sometimes with some simple regular expression it should be possible<sup><a href="#note1">&dagger;</a></sup>. Say you want to match everything containing the some word say <code>bull</code> but don&rsquo;t want to match </p> +<p>But sometimes with some simple regular expression it should be possible<sup><a href="#note1">†</a></sup>. Say you want to match everything containing the some word say <code>bull</code> but don’t want to match <code>bullshit</code>. Here is a nice way to do that:</p> + +<div> + +<pre><code class="ruby"># match all string containing 'bull' (bullshit comprised)...</code></pre></div> diff --git a/output/Scratch/en/blog/index.html b/output/Scratch/en/blog/index.html index 6d6a95a16..e40e36819 100644 --- a/output/Scratch/en/blog/index.html +++ b/output/Scratch/en/blog/index.html @@ -220,7 +220,7 @@ Popular Articles
    -
    Title image + @@ -246,7 +246,7 @@ Popular Articles $('.tag.selected').removeClass('selected'); $('#tag_'+id).addClass('selected'); } -

    alternate reality

      +

      alternate reality

      Mac

      mac

      Mac

      macros

      - Modified: 04/24/2012 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/blog/mvc/index.html b/output/Scratch/en/blog/mvc/index.html index b81789f6c..5b98b9e96 100644 --- a/output/Scratch/en/blog/mvc/index.html +++ b/output/Scratch/en/blog/mvc/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,17 +57,17 @@

      Why This article and for whom?

      -

      Many website explaining how MVC works. But I can’t found one who explain why.

      +

      Many website explaining how MVC works. But I can’t found one who explain why.

      -

      I have difficulties to obey some principle don’t know why I should use it. And something better than the:

      +

      I have difficulties to obey some principle don’t know why I should use it. And something better than the:

      -

      “Smarter people than you decided you have to do so”

      +

      “Smarter people than you decided you have to do so”

      This article is for people who like me want to understand the real motivation of using an MVC architecture and which advantage it gives you.

      -

      Let’s start making a project from scratch pretending we don’t know anything about the MVC architecture. Then let see how we’ll naturally derive to an MVC architecture.

      +

      Let’s start making a project from scratch pretending we don’t know anything about the MVC architecture. Then let see how we’ll naturally derive to an MVC architecture.

      → Next

      diff --git a/output/Scratch/en/blog/programming-language-experience/index.html b/output/Scratch/en/blog/programming-language-experience/index.html index b628e2d52..886ed2e13 100644 --- a/output/Scratch/en/blog/programming-language-experience/index.html +++ b/output/Scratch/en/blog/programming-language-experience/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,8 +69,9 @@

      BASIC

      -

      Title image -The language of my firsts programs! +

      Title image

      + +

      The language of my firsts programs! I was about 10, with an MO5 and Amstrad CPC 6128 and even with my Atari STe. This is the language of GOTOs. Ô nostalgia. @@ -78,31 +82,30 @@ It is not even good to learn programming. I know some compiler exists now. But this is not enough to try to learn it.

      -
      -READY
      -10 PRINT "HELLO WORLD!"
      +
      READY
      +10 PRINT "HELLO WORLD!"
       20 GOTO 10
       RUN
      -
      +

      I also remember I copied some game source code from some magazine. Most lines were like:

      -
      -3110 DATA FA,01,FF,FF,FF,FF,00,23,22,43,DA,DE,EE,FF,FF,FF,00,03,4A,F2
      -
      +
      3110 DATA FA,01,FF,FF,FF,FF,00,23,22,43,DA,DE,EE,FF,FF,FF,00,03,4A,F2
      +

      What a pleasure!

      -

      Dragon fractal -I was about 10 when I played with logo to draw on the computer.

      +

      Dragon fractal

      -

      I remember the Bach’s music while the program loaded.

      +

      I was about 10 when I played with logo to draw on the computer.

      + +

      I remember the Bach’s music while the program loaded.

      At that time we had to load the program into the memory using tapes. -This one was a rare one. It didn’t made an awfull ‘Krrrkrr cssssss krrr’ noise.

      +This one was a rare one. It didn’t made an awfull ‘Krrrkrr cssssss krrr’ noise.

      Some years after, I used it to learn programming to my college student. It was really good as a first language. @@ -110,8 +113,7 @@ Making fractals is like a game for children.

      Here is an example of code. It draws the dragon fractal.

      -
      -HIDETURTLE
      +
      HIDETURTLE
       
       PENUP
       SETXY -200 0
      @@ -120,10 +122,10 @@ PENDOWN
       
       to dragon :degree :size
           setpensize 1
      -    if :size>5  [setpensize 2]
      -    if :size>10 [setpensize 3]
      -    if :size>20 [setpensize 4]
      -    if :size>40 [setpensize 5]
      +    if :size>5  [setpensize 2]
      +    if :size>10 [setpensize 3]
      +    if :size>20 [setpensize 4]
      +    if :size>40 [setpensize 5]
           ifelse :degree=0 [ 
               fd :size 
           ][
      @@ -135,7 +137,7 @@ to dragon :degree :size
       end
       
       dragon 6 3000
      -
      +

      Pascal

      @@ -148,9 +150,9 @@ In the end I prefer C.

      C

      -

      Pointer representation from Dancing links

      +

      Pointer representation from Dancing links

      -

      The pointer’s language.

      +

      The pointer’s language.

      Le programming language.

      @@ -161,12 +163,12 @@ If you want to have good quality code, knowing C is almost mandatory.

      This language is close to machine language. So much, there is (mostly) a linear relation between the size of your code and the size of the compiled one.

      -

      In short, each time you write a C instruction there won’t be anything strange that will occurs, like starting a long algorithm behind the scene.

      +

      In short, each time you write a C instruction there won’t be anything strange that will occurs, like starting a long algorithm behind the scene.

      It is very close to the machine while keeping sufficient abstractions to be fun.

      I made a lot of program with it. -From sort algorithms to AI ones (SAT3), system, network prgramming, etc… +From sort algorithms to AI ones (SAT3), system, network prgramming, etc… It is a very useful language that will help you understand how things works on your computer. Most modern computer language hide a lot of informations on what occurs. This is not the case with C.

      @@ -175,7 +177,7 @@ This is not the case with C.

      The super-clean one.

      -

      I liked ADA. I must confess I didn’t used it a lot. +

      I liked ADA. I must confess I didn’t used it a lot. May be one day I will try it again. I was impressed by asynchronous programming with it. What you need to know is this old language had certainly inspired most new object oriented languages.

      @@ -184,12 +186,12 @@ What you need to know is this old language had certainly inspired most new objec

      Until here I just described imperative languages without any object notion.

      -

      More clearly, the language didn’t helped you to structure your program.

      +

      More clearly, the language didn’t helped you to structure your program.

      In order to limit the number of bugs, particularly for huge programs, we started to think about how to organize computer programs. In the end, from the imperatives language culture, it produced the Object Oriented programming (OOP). -Beware, the Object Oriented programming isn’t a miracle. Proof? How many bug-free software do you use? -Furthermore, OOP doesn’t fit all problems. +Beware, the Object Oriented programming isn’t a miracle. Proof? How many bug-free software do you use? +Furthermore, OOP doesn’t fit all problems. But to make a bank application, an application which help to manage stock, clients or text archives. I mean an information system, the OOP is not so bad.

      @@ -197,7 +199,7 @@ I mean an information system, the OOP is not so bad.

      C++

      -

      Messy router

      +

      Messy router

      The ugly

      @@ -213,30 +215,30 @@ But in reality, T have to be only char or char16. Then I had to reduce my alphabet to 216 letters. Except for some application, the alphabet must be far larger than that.

      -

      To conclude, I’d say, C++ is very good if you work alone or with a fixed subset of its features.

      +

      To conclude, I’d say, C++ is very good if you work alone or with a fixed subset of its features.

      Eiffel

      -

      Eiffel tower construction

      +

      Eiffel tower construction

      Yes, it is a really nice language. Full object in mind. Far cleaner than C++. -But it isn’t so popular. +But it isn’t so popular. Behind C++ there is a large community to help new users and to write libraries. Furthermore, I preferred working with C++. At that time I programmed a lot with C and like its syntax.

      Java

      -

      Holy Grail from the Monty Python

      +

      Holy Grail from the Monty Python

      The first time I heard about Java it was le Grail!

      Perfect portability, your program will work on all platform. -There was incrusted inside the language architecture concepts to help limit mistakes, and force you to use good programming habits. But…

      +There was incrusted inside the language architecture concepts to help limit mistakes, and force you to use good programming habits. But…

      But It is extremely verbose. -And limitations are quite boring if you know what you’re doing.

      +And limitations are quite boring if you know what you’re doing.

      For example, there is no multiple inheritance. Generally it is a coherent choice when there are a way to compensate. @@ -252,32 +254,32 @@ And I add to make a bunch of copy/paste inside all my subclasses! Copy/paste are exactly what should be avoided the most by object oriented languages.

      Another thing: threads. -I was forced to make my own thread management system to avoid locks and notifications between threads (wait the end of this thread, …). +I was forced to make my own thread management system to avoid locks and notifications between threads (wait the end of this thread, …). At that time I used Java 1.5. This problem should have been solved with Java 1.6. I wish it is the case, but lacking such an essential feature for a language was very bad.

      In the same idea, it was very long to wait for the foreach loops.

      -

      After my experience, I don’t recommend Java. +

      After my experience, I don’t recommend Java. Portability does not worth this price.

      GUI protability means mediocre experience on all platforms. -Any system it might be (wxWidget, QT, etc…).

      +Any system it might be (wxWidget, QT, etc…).

      -

      The Java ideology is “closed”. But it resolve a big problem. +

      The Java ideology is “closed”. But it resolve a big problem. It helps medium to low quality developper to work in team without the ability to make too much harm to the product. A good programmer will be able to make very interresting things with it thought. -Please note I didn’t say Java programmer are bad programmer.

      +Please note I didn’t say Java programmer are bad programmer.

      Objective-C

      -

      Xcode Logo

      +

      Xcode Logo

      The language I learned and used only to make application on Apple© platform. I learned Objective-C just after Python. It was hard to do it. -At first I didn’t liked the syntax and many other details. +At first I didn’t liked the syntax and many other details. But it is this kind of language you like more and more you use it. In fact, Objective-C is a simple language, but associated with the Cocoa framework it is a really good tool. Cocoa is very different to other framework I used before. @@ -288,17 +290,17 @@ It might seems like small details on paper, but once you start using it, it make

      Even if Objective-C is a relatively low level language. Its dynamic typing ability make it very good for GUI programming. I recommand to continue working with this language. -In the end you’ll certainely find it better than expected.

      +In the end you’ll certainely find it better than expected.

      Modern Scripting Languages

      PHP

      -

      A Jacky Touch Car

      +

      A Jacky Touch Car

      This small script language that we used all to make our website in the time of animated gifs.

      -

      Nice but no more. Apparently there were a lot of progress since PHP5. Maybe one day I’ll use it again. But behind it, this language has a “script kiddies only” reputation. +

      Nice but no more. Apparently there were a lot of progress since PHP5. Maybe one day I’ll use it again. But behind it, this language has a “script kiddies only” reputation. Also long history of easy to make security holes.

      In reality PHP is just behind C for the abstraction level. @@ -306,28 +308,28 @@ Therefore it has a lot of organisation problems and make it easier to create bug For web applications it is a real problem.

      PHP remains for me the SQL injection language. -I make a bit of PHP not so long ago, and it was a pain to protect my application to SQL injection. Yep, I didn’t found any standard library to make this, but I didn’t searched a lot.

      +I make a bit of PHP not so long ago, and it was a pain to protect my application to SQL injection. Yep, I didn’t found any standard library to make this, but I didn’t searched a lot.

      Python

      -

      Python. Do you speak it?

      +

      Python. Do you speak it?

      Revelation!

      -

      When you were used to work with compiled languages (C++, Java) and you start learning Python, it’s like a punch in the face. +

      When you were used to work with compiled languages (C++, Java) and you start learning Python, it’s like a punch in the face. Programming like it always should have been. -Everything is natural, it’s magic. +Everything is natural, it’s magic. Yes, as good as this. But something so good must have some drawback.

      And yes, like all interpreted languages, Python is slow. Beware, no just a bit slow like 2 or 3 times slower than C. (like Java for example). No, really slow, about 10 to 20 times slower than C. -Argh… Note it is completely usable for many things.

      +Argh… Note it is completely usable for many things.

      Awk

      -

      If you have to “filter” some files and the filter is not too complicated awk is the ideal language to do this. +

      If you have to “filter” some files and the filter is not too complicated awk is the ideal language to do this. For example, if you want to know which words in a text file are most used. I used it to modify hundred of XML files in an easier manner than XSLT.

      @@ -337,9 +339,8 @@ I used it to modify hundred of XML files in an easier manner than XSLT.

      Or at least, all other collaboratos must be excellent programmers. But the very good feature is the integration of some perl syntax.

      -
      -$var =~ s/toto/titi/g
      -
      +
      $var =~ s/toto/titi/g
      +

      This program will replace every toto by titi inside the $var variable. The Perl code is often very compact and usally unreadable. @@ -401,10 +402,10 @@ Generally it takes me no more than some hours to some days to learn a new progra Concerning haskell, this is very different. To master haskell you need to understand very hard concepts. Monads and Arrows are some of them. -I didn’t understand them before I read some scientific paper. +I didn’t understand them before I read some scientific paper. Many week will be necessary to master it perfectly (if someone does). -Also the community is very friendly and nice. There is no “LOL! URAN00B! RTFM!” -And no concession has been made to make this language more popular (I’m looking at you C++, Java and Javascript). +Also the community is very friendly and nice. There is no “LOL! URAN00B! RTFM!” +And no concession has been made to make this language more popular (I’m looking at you C++, Java and Javascript). This langage remain pure (I know there are two meaning).

      Unpopular Languages

      @@ -417,17 +418,15 @@ It contains a linear solver. This is really useful to draw things. For example if you write:

      -
      -AA=1/3[A,B]
      -
      +
      AA=1/3[A,B]
      +

      It will place the point AA between the point A and B. More precisely at the barycenter (2xA + B)/3.

      -
      -X=whatever[A,B]
      -X=whatever[C,D]
      -
      +
      X=whatever[A,B]
      +X=whatever[C,D]
      +

      This second example, will place the point X at the intersection of the two segments AB and CD.

      @@ -576,7 +575,7 @@ But I used all of these languages.

      Created: 09/28/2011 - Modified: 10/26/2011 + Modified: 04/26/2012
      Entirely done with diff --git a/output/Scratch/en/error/401-authorization_required/index.html b/output/Scratch/en/error/401-authorization_required/index.html index ed89f162e..841847087 100644 --- a/output/Scratch/en/error/401-authorization_required/index.html +++ b/output/Scratch/en/error/401-authorization_required/index.html @@ -55,7 +55,7 @@
      -

      If you don’t have the password or believe it is an error you can mail me at .

      +

      If you don’t have the password or believe it is an error you can mail me at yann.esposito@gmail.com.

      @@ -63,7 +63,7 @@
      -

      Vous pouvez me contacter par mail: .

      +

      Vous pouvez me contacter par mail: yann.esposito@gmail.com.

      diff --git a/output/Scratch/en/error/403-forbidden/index.html b/output/Scratch/en/error/403-forbidden/index.html index 10ff7f698..a7695ae1d 100644 --- a/output/Scratch/en/error/403-forbidden/index.html +++ b/output/Scratch/en/error/403-forbidden/index.html @@ -55,7 +55,7 @@
      -

      Contact:

      +

      Contact: yann.esposito@gmail.com

      diff --git a/output/Scratch/en/error/404-not_found/index.html b/output/Scratch/en/error/404-not_found/index.html index 156cd1a09..2403e2953 100644 --- a/output/Scratch/en/error/404-not_found/index.html +++ b/output/Scratch/en/error/404-not_found/index.html @@ -29,8 +29,8 @@
      -

      The page you’re looking at doesn’t exists on the server. -If you have followed an internal link, you can tell me by mail and I’ll fix it as soon as possible.

      +

      The page you’re looking at doesn’t exists on the server. +If you have followed an internal link, you can tell me by mail yann.esposito@gmail.com and I’ll fix it as soon as possible.

      @@ -38,8 +38,8 @@ If you have followed an internal link, you can tell me by mail and I’ll f
      -

      La page que vous recherchez n’est pas présente sur le site. -Si vous avez suivi un lien vous pouvez me prévenir par mail et je réparerai ça dès que possible.

      +

      La page que vous recherchez n’est pas présente sur le site. +Si vous avez suivi un lien vous pouvez me prévenir par mail yann.esposito@gmail.com et je réparerai ça dès que possible.

      diff --git a/output/Scratch/en/error/408-request_timed_out/index.html b/output/Scratch/en/error/408-request_timed_out/index.html index 008aa04f9..e772dad58 100644 --- a/output/Scratch/en/error/408-request_timed_out/index.html +++ b/output/Scratch/en/error/408-request_timed_out/index.html @@ -55,7 +55,7 @@
      -

      Contact:

      +

      Contact: yann.esposito@gmail.com

      diff --git a/output/Scratch/en/error/500-internal_server_error/index.html b/output/Scratch/en/error/500-internal_server_error/index.html index 631cbc440..3cf990de9 100644 --- a/output/Scratch/en/error/500-internal_server_error/index.html +++ b/output/Scratch/en/error/500-internal_server_error/index.html @@ -55,7 +55,7 @@
      -

      Contact:

      +

      Contact: yann.esposito@gmail.com

      diff --git a/output/Scratch/en/error/503-service_unavailable/index.html b/output/Scratch/en/error/503-service_unavailable/index.html index 248b34e83..a9719838f 100644 --- a/output/Scratch/en/error/503-service_unavailable/index.html +++ b/output/Scratch/en/error/503-service_unavailable/index.html @@ -55,7 +55,7 @@
      -

      Contact:

      +

      Contact: yann.esposito@gmail.com

      diff --git a/output/Scratch/en/index.html b/output/Scratch/en/index.html index 48528ddc9..ba8f7fc30 100644 --- a/output/Scratch/en/index.html +++ b/output/Scratch/en/index.html @@ -103,7 +103,7 @@ Copyright ©, Yann Esposito
      - Modified: 04/24/2012 + Modified: 05/02/2012
      Entirely done with diff --git a/output/Scratch/en/rss/index.html b/output/Scratch/en/rss/index.html index 605ffc501..7e0df1d85 100644 --- a/output/Scratch/en/rss/index.html +++ b/output/Scratch/en/rss/index.html @@ -72,7 +72,7 @@

      My explanation

      -

      It is an easy way to aggregate all the update made on websites you like in a single place. You’ll never have to surf many website to see if there is news on a website.

      +

      It is an easy way to aggregate all the update made on websites you like in a single place. You’ll never have to surf many website to see if there is news on a website.

      choose an aggregator

      @@ -85,11 +85,11 @@ It is great for content you never want to forgot some article. It is not really

      Subscribe to some website news

      -

      Once you have chosen your aggregator, you only have to subscribe to websites you like. Do do this, you only have to click on the RSS icon in the top bar of your navigator. Or generally a nice icon is shown into the page you’re reading.

      +

      Once you have chosen your aggregator, you only have to subscribe to websites you like. Do do this, you only have to click on the RSS icon in the top bar of your navigator. Or generally a nice icon is shown into the page you’re reading.

      Get the news

      -

      Now, you’ll only have to use your RSS client. And you’ll see all news on all subscribed websites. There is no more need to surf many websites. All news which interest you are in the same place.

      +

      Now, you’ll only have to use your RSS client. And you’ll see all news on all subscribed websites. There is no more need to surf many websites. All news which interest you are in the same place.

      diff --git a/output/Scratch/en/softwares/yaquabubbles/index.html b/output/Scratch/en/softwares/yaquabubbles/index.html index f753556d9..0e4c80894 100644 --- a/output/Scratch/en/softwares/yaquabubbles/index.html +++ b/output/Scratch/en/softwares/yaquabubbles/index.html @@ -65,7 +65,7 @@

      YAquaBubbles is a QuartzComposer Screensaver. It was one of my first try but the result was nice.

      -

      YAquaBubbles.dmg

      +

      YAquaBubbles.dmg

      diff --git a/output/Scratch/en/softwares/yclock/index.html b/output/Scratch/en/softwares/yclock/index.html index 7474c5a2a..10fab941d 100644 --- a/output/Scratch/en/softwares/yclock/index.html +++ b/output/Scratch/en/softwares/yclock/index.html @@ -62,7 +62,7 @@ It has three themes: white, black and red. It is based on a QuartzComposition and with some little Objective-C code to handle gently the frame per second.

      -

      YClock.dmg

      +

      YClock.dmg

      diff --git a/output/Scratch/en/softwares/ypassword/index.html b/output/Scratch/en/softwares/ypassword/index.html index b5910b289..783b66baa 100644 --- a/output/Scratch/en/softwares/ypassword/index.html +++ b/output/Scratch/en/softwares/ypassword/index.html @@ -70,7 +70,7 @@
    • A command line tool.
    -

    I’ll soon release an iPhone application.

    +

    I’ll soon release an iPhone application.

    diff --git a/output/Scratch/en/validation/index.html b/output/Scratch/en/validation/index.html index 8ef52c211..0666f97b5 100644 --- a/output/Scratch/en/validation/index.html +++ b/output/Scratch/en/validation/index.html @@ -64,9 +64,9 @@

    Using these properties broke my validation but work really well in the two most recent and main browsers -(Safari 4 and Firefox 3.5 at the time I’m writing these lines). -If you don’t use these browser the page is -correctly displayed but not with all the ‘eyecandy’ effects.

    +(Safari 4 and Firefox 3.5 at the time I’m writing these lines). +If you don’t use these browser the page is +correctly displayed but not with all the ‘eyecandy’ effects.

    I believed in the benefits of CSS validation, this is why there is alway the diff --git a/output/Scratch/fr/about/contact/index.html b/output/Scratch/fr/about/contact/index.html index d74192528..02a8865dc 100644 --- a/output/Scratch/fr/about/contact/index.html +++ b/output/Scratch/fr/about/contact/index.html @@ -60,9 +60,9 @@

    Avatar

    -

    yann.esposito@gmail.com
    +

    yann.esposito@gmail.com Suivez moi sur twitter
    - Mes “bookmarks” pinboard
    + Mes “bookmarks” pinboard
    Open Source github
    stackoverflow

    diff --git a/output/Scratch/fr/about/index.html b/output/Scratch/fr/about/index.html index f7b320eaf..943a44344 100644 --- a/output/Scratch/fr/about/index.html +++ b/output/Scratch/fr/about/index.html @@ -69,7 +69,7 @@
    -

    Livres que j’aime :

    +

    Livres que j’aime :

    • Goëdel, Escher & Bach [Hofstadter]
    • @@ -77,7 +77,7 @@
    • Baudolino [Eco]
    -

    Films que j’aime :

    +

    Films que j’aime :

    @@ -115,11 +115,11 @@

    Ma petite histoire

    -

    J’ai eu mon doctorat en Informatique en décembre 2004 au LIF (Marseille). Pendant les trois années de ma formation j’ai non seulement fait de la recherche en informatique mais j’ai aussi enseigné à des étudiants de l’Université. J’ai aussi développé un programme informatique dont le but était de vérifier expérimentalement mes résultats théoriques.

    +

    J’ai eu mon doctorat en Informatique en décembre 2004 au LIF (Marseille). Pendant les trois années de ma formation j’ai non seulement fait de la recherche en informatique mais j’ai aussi enseigné à des étudiants de l’Université. J’ai aussi développé un programme informatique dont le but était de vérifier expérimentalement mes résultats théoriques.

    -

    J’ai ensuite fait un Post Doctorat au Laboratoire Hubert Curient de Saint-Etienne. Ma mission consistait à développer une application scientifique (SEDiL). Cette application visait comme public des biologistes et devait avoir une interface utilisateur agréable à utiliser.

    +

    J’ai ensuite fait un Post Doctorat au Laboratoire Hubert Curient de Saint-Etienne. Ma mission consistait à développer une application scientifique (SEDiL). Cette application visait comme public des biologistes et devait avoir une interface utilisateur agréable à utiliser.

    -

    Ajourd’hui je travaille pour AirFrance© via Astek. Ce travail est très diversifié. Il demande la connaissance des technologies Web, CMS mais aussi de la programmation en Perl, JSP, meta-programmation…

    +

    Ajourd’hui je travaille pour AirFrance© via Astek. Ce travail est très diversifié. Il demande la connaissance des technologies Web, CMS mais aussi de la programmation en Perl, JSP, meta-programmation…

    diff --git a/output/Scratch/fr/about/technical_details/index.html b/output/Scratch/fr/about/technical_details/index.html index b258a1688..94b9b2832 100644 --- a/output/Scratch/fr/about/technical_details/index.html +++ b/output/Scratch/fr/about/technical_details/index.html @@ -76,8 +76,8 @@ comme

    Les commentaires de blog sont gérés par -disqus intense debate. Ainsi je n’ai besoin pour héberger mons site que -d’un serveur de pages statiques. Ce qui a de nombreux avantages. +disqus intense debate. Ainsi je n’ai besoin pour héberger mons site que +d’un serveur de pages statiques. Ce qui a de nombreux avantages. Principalement concernant la charge et la sécurité du serveur.

    @@ -89,7 +89,7 @@ et que j'ai conçu ce site quasiment ex nihilo.

    Si au contraire vous voulez connaitre tous les détails je vous suggère -d’aller lire mon article concernant nanoc.

    +d’aller lire mon article concernant nanoc.

    diff --git a/output/Scratch/fr/blog/01_nanoc/index.html b/output/Scratch/fr/blog/01_nanoc/index.html index fd38ec819..076466eea 100644 --- a/output/Scratch/fr/blog/01_nanoc/index.html +++ b/output/Scratch/fr/blog/01_nanoc/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -52,18 +55,18 @@
    -

    Qu’est-ce que nanoc ?

    +

    Qu’est-ce que nanoc ?

    -

    Il ne s’agit pas exactement d’un +

    Il ne s’agit pas exactement d’un CMS, -mais plutôt d’un système de gestion de pages statiques.

    +mais plutôt d’un système de gestion de pages statiques.

    Il faut programmer sois-même les pages web, -le code pour engendrer les menus…

    +le code pour engendrer les menus…

    -

    J’ai programmé des filtres pour rendre ce site multilangue par exemple

    +

    J’ai programmé des filtres pour rendre ce site multilangue par exemple

    -

    Vous pourrez trouver beaucoup d’informations sur +

    Vous pourrez trouver beaucoup d’informations sur le site officiel de nanoc.

    diff --git a/output/Scratch/fr/blog/02_ackgrep/code/ack b/output/Scratch/fr/blog/02_ackgrep/code/ack index 38a093374..8b80a5e6b 100644 --- a/output/Scratch/fr/blog/02_ackgrep/code/ack +++ b/output/Scratch/fr/blog/02_ackgrep/code/ack @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh (($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 } diff --git a/output/Scratch/fr/blog/02_ackgrep/index.html b/output/Scratch/fr/blog/02_ackgrep/index.html index 4ac04d755..435fff368 100644 --- a/output/Scratch/fr/blog/02_ackgrep/index.html +++ b/output/Scratch/fr/blog/02_ackgrep/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,11 +57,11 @@

    Mise à jour

    -

    Comme Andy Lester me l’a fait remarqué. ack est un simple fichier perl qu’il suffit de copier dans son répertoire personnel ~/bin. Maintenant j’ai ack sur mon serveur professionnel.

    +

    Comme Andy Lester me l’a fait remarqué. ack est un simple fichier perl qu’il suffit de copier dans son répertoire personnel ~/bin. Maintenant j’ai ack sur mon serveur professionnel.

    -

    Il suffit d’aller sur http://betterthangrep.com pour le télécharger.

    +

    Il suffit d’aller sur http://betterthangrep.com pour le télécharger.

    -

    Sincèrement, je ne comprend pas qu’ack ne soit pas une commande implémentée par défaut sur les systèmes UNIX. Je ne peux vraiment plus m’en passer, il m’est devenu aussi essentiel qu’un which ou un find.

    +

    Sincèrement, je ne comprend pas qu’ack ne soit pas une commande implémentée par défaut sur les systèmes UNIX. Je ne peux vraiment plus m’en passer, il m’est devenu aussi essentiel qu’un which ou un find.

    @@ -71,39 +74,42 @@

    Un des mes usages principaux de grep est

    -
    -grep 'pattern' **/*(.)
    -
    + + +
    grep 'pattern' **/*(.)
    + +
    -

    La plupart du temps c’est suffisant, mais ajouter de la coloration -améliore beaucoup l’utilité de cette commande. Il existe déjà un outil -pour ça : il s’appelle ack-grep sous Ubuntu. -Comme je ne peux pas l’installer sur le serveur de mon entreprise, -j’en ai créé un moi-même en quelques lignes :

    +

    La plupart du temps c’est suffisant, mais ajouter de la coloration +améliore beaucoup l’utilité de cette commande. Il existe déjà un outil +pour ça : il s’appelle ack-grep sous Ubuntu. +Comme je ne peux pas l’installer sur le serveur de mon entreprise, +j’en ai créé un moi-même en quelques lignes :

    -
    -
    -#!/usr/bin/env zsh
    -(($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 }
    +    
    +
    +
    +
    #!/usr/bin/env zsh
    +(($#<1)) && { print 'usage: ack "regexp"' >&2; exit 1 }
     
     listeFic=( **/*(.) )
     autoload zargs
    -zargs -- $listeFic -- grep $1 | perl -ne 'use Term::ANSIColor;
    -if (m/([^:]*)(:.*)('$1')(.*)/) {
    -    print color("green").$1;
    -    print color("reset").$2;
    -    print color("black","on_yellow").$3;
    -    print color("reset").$4."\n";
    -} '
    -    
    -
    -
    +zargs -- $listeFic -- grep $1 | perl -ne 'use Term::ANSIColor; +if (m/([^:]*)(:.*)('$1')(.*)/) { + print color("green").$1; + print color("reset").$2; + print color("black","on_yellow").$3; + print color("reset").$4."\n"; +} ' + + +

    Pour mon utilisation personnelle et celle de mon équipe -c’est suffisant. J’espère que ça pourra vous aider.

    +c’est suffisant. J’espère que ça pourra vous aider.

    diff --git a/output/Scratch/fr/blog/03_losthighway/03_losthighway_1/index.html b/output/Scratch/fr/blog/03_losthighway/03_losthighway_1/index.html index 984b2d68b..b18935604 100644 --- a/output/Scratch/fr/blog/03_losthighway/03_losthighway_1/index.html +++ b/output/Scratch/fr/blog/03_losthighway/03_losthighway_1/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,42 +69,42 @@
    -

    Tout d’abord, il est clair que comprendre le film comme simplement un film fantastique ne fonctionne pas. En suivant ce point d’entrée on en fini pas de se heurter à des détails incompréhensibles.

    +

    Tout d’abord, il est clair que comprendre le film comme simplement un film fantastique ne fonctionne pas. En suivant ce point d’entrée on en fini pas de se heurter à des détails incompréhensibles.

    -

    Mon hypothèse de départ c’est que le film dépeint la représentation de la réalité que s’en fait Fred. -Chaque fois qu’il essaye d’échapper à la réalité, celle-ci finira par le rattraper.

    +

    Mon hypothèse de départ c’est que le film dépeint la représentation de la réalité que s’en fait Fred. +Chaque fois qu’il essaye d’échapper à la réalité, celle-ci finira par le rattraper.

    -

    Fred a commis un acte horrible, un meurtre, et essaye de réparer sa mémoire pour accepter son acte. Il va alors s’inventer des réalitées alternatives.

    +

    Fred a commis un acte horrible, un meurtre, et essaye de réparer sa mémoire pour accepter son acte. Il va alors s’inventer des réalitées alternatives.

      -
    • Dans un premier temps il tue sa femme (Renée) parce qu’elle le trompe.
    • +
    • Dans un premier temps il tue sa femme (Renée) parce qu’elle le trompe.
    • Dans la deuxième partie, il est plus faible. La version blonde de Renée va le manipuler pour tuer Dick Laurent.
    • Dans la troisième partie il tue Dick Laurent

    Quelle est la validité de ce choix ?

    -

    Cet interprétation me semble valide à cause du dialogue au début du film avec les policier qui demande au protagoniste s’il a une caméra :

    +

    Cet interprétation me semble valide à cause du dialogue au début du film avec les policier qui demande au protagoniste s’il a une caméra :

    -

    “Do you own a video camera?”
    -“No, Fred hates them.”
    -“I like to remember things my own way.”
    -“What do you mean by that?”
    -“How I remember them, not necessarily the way they happened.”

    +

    “Do you own a video camera?”
    +“No, Fred hates them.”
    +“I like to remember things my own way.”
    +“What do you mean by that?”
    +“How I remember them, not necessarily the way they happened.”

    -

    Ce que l’on peut traduire approximativement par :

    +

    Ce que l’on peut traduire approximativement par :

    -

    – Avez-vous une caméra ?
    -– Non, Fred les détestes.
    -– J’aime me rappeler les choses à ma façon.
    -– Qu’entendez-vous par là ?
    -– Je me rapelle des choses pas nécessairement comme elles se sont passées.

    +

    – Avez-vous une caméra ?
    +– Non, Fred les détestes.
    +– J’aime me rappeler les choses à ma façon.
    +– Qu’entendez-vous par là ?
    +– Je me rapelle des choses pas nécessairement comme elles se sont passées.

    -

    Ainsi, ce que l’on voit n’est pas la réalité, mais la réalité telle que le conçoit Fred. Il est donc le Dieu de cette réalité. Ainsi les interprétations mystiques faisant intervenir le Diable ont une certaine validité.

    +

    Ainsi, ce que l’on voit n’est pas la réalité, mais la réalité telle que le conçoit Fred. Il est donc le Dieu de cette réalité. Ainsi les interprétations mystiques faisant intervenir le Diable ont une certaine validité.

    diff --git a/output/Scratch/fr/blog/03_losthighway/03_losthighway_2/index.html b/output/Scratch/fr/blog/03_losthighway/03_losthighway_2/index.html index 898a31267..b518c90ba 100644 --- a/output/Scratch/fr/blog/03_losthighway/03_losthighway_2/index.html +++ b/output/Scratch/fr/blog/03_losthighway/03_losthighway_2/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,25 +61,25 @@
    -

    Qui est l’homme mystérieux ?

    +

    Qui est l’homme mystérieux ?

    -

    l'homme mystérieux

    +

    l'homme mystérieux

    Qui est donc ce personnage étrange et inquiétant ? -Un être capable d’ubiquité qui dit être invité par Fred dans sa maison ? +Un être capable d’ubiquité qui dit être invité par Fred dans sa maison ? Sans sourcils, le visage blême, les yeux écarquillés fixant sans relâche les faits et gestes de Fred.

    -

    C’est certainement une des clés du film. +

    C’est certainement une des clés du film. À mon avis, il représente la partie mauvaise de Fred. -Certainement la jalousie. Si j’étais Catholique je dirai Satan, le tentateur. -Il n’agit jamais, mais ne fait qu’observer et filmer. -Par contre c’est lui qui donne les armes à Fred pour tuer Dick Laurent. -Fred l’a laissé entrer chez lui et il ne peut plus s’en débarrasser. +Certainement la jalousie. Si j’étais Catholique je dirai Satan, le tentateur. +Il n’agit jamais, mais ne fait qu’observer et filmer. +Par contre c’est lui qui donne les armes à Fred pour tuer Dick Laurent. +Fred l’a laissé entrer chez lui et il ne peut plus s’en débarrasser. Un peu comme le Iago de Shakespeare est enfermé dans sa jalousie. -Le personnage mystérieux prend toute l’importance, il le ronge de l’intérieur. -Il aide Fred à accomplir les actes de violences et aussi l’oblige à se souvenir de la réalité.

    +Le personnage mystérieux prend toute l’importance, il le ronge de l’intérieur. +Il aide Fred à accomplir les actes de violences et aussi l’oblige à se souvenir de la réalité.

    -

    Quand il fait l’amour à Renée il voit le visage de l’homme mystérieux à la place du visage de sa femme. En réalité, il s’agit de la même personne d’après Fred. Ce serait donc elle qui est la source de son mal intérieur.

    +

    Quand il fait l’amour à Renée il voit le visage de l’homme mystérieux à la place du visage de sa femme. En réalité, il s’agit de la même personne d’après Fred. Ce serait donc elle qui est la source de son mal intérieur.

    @@ -181,7 +184,7 @@ Il aide Fred à accomplir les actes de violences et aussi l’oblige à se s
    Écrit le : 04/08/2009 - modifié le : 09/05/2010 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/03_losthighway/03_losthighway_3/index.html b/output/Scratch/fr/blog/03_losthighway/03_losthighway_3/index.html index b9cb0f18d..17a0b32bd 100644 --- a/output/Scratch/fr/blog/03_losthighway/03_losthighway_3/index.html +++ b/output/Scratch/fr/blog/03_losthighway/03_losthighway_3/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,11 +63,11 @@

    Qui filme et dépose les cassettes ?

    -

    C’est certainement l’homme mystérieux (ou Fred lui-même) qui est à l’origine de ces cassettes. +

    C’est certainement l’homme mystérieux (ou Fred lui-même) qui est à l’origine de ces cassettes. Le rôle des cassettes est double :

      -
    • Rappeler à Fred la réalité. D’après Fred les cassettes video correspondent à la réalité. Il a beau essayer de se cacher la réalité, les cassettes finissent par aller jusqu’au bout et il se voit en train de tuer Renée.
    • +
    • Rappeler à Fred la réalité. D’après Fred les cassettes video correspondent à la réalité. Il a beau essayer de se cacher la réalité, les cassettes finissent par aller jusqu’au bout et il se voit en train de tuer Renée.
    • La cassette peut aussi faire référence aux cassettes de films pornographique dans laquelle Renée a peut-être tournée dans la réalité ?
    diff --git a/output/Scratch/fr/blog/03_losthighway/03_losthighway_4/index.html b/output/Scratch/fr/blog/03_losthighway/03_losthighway_4/index.html index 9ffc79713..214ae803c 100644 --- a/output/Scratch/fr/blog/03_losthighway/03_losthighway_4/index.html +++ b/output/Scratch/fr/blog/03_losthighway/03_losthighway_4/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,27 +61,27 @@
    -

    Que s’est-il vraiment passé ?

    +

    Que s’est-il vraiment passé ?

    -

    Ici, tout n’est pas donné, on garde une assez grande liberté. Mais on a des indices.

    +

    Ici, tout n’est pas donné, on garde une assez grande liberté. Mais on a des indices.

    Hypothese n°1

    -

    Je dirais que le protagoniste est un garagiste qui est tombé amoureux d’une actrice porno. Il l’a certainement vu la première fois accompagnant le fameux Dick Laurent. Voyant qu’il ne peut pas l’avoir pour lui, fou de jalousie il tue Dick Laurent dans un motel où celui-ci à couché avec Renée.

    +

    Je dirais que le protagoniste est un garagiste qui est tombé amoureux d’une actrice porno. Il l’a certainement vu la première fois accompagnant le fameux Dick Laurent. Voyant qu’il ne peut pas l’avoir pour lui, fou de jalousie il tue Dick Laurent dans un motel où celui-ci à couché avec Renée.

    -

    On a la liberté de décider s’il a vraiment tué la femme ou pas. -Dans ma première vision du film, j’avais envie de dire qu’il ne la tue pas. Mais qu’une fois le meurtre commis, il va chez elle, sonne pour lui annoncer la mort de Dick Laurent. Il a alors juste le temps de s’enfuir, la police à ses trousses.

    +

    On a la liberté de décider s’il a vraiment tué la femme ou pas. +Dans ma première vision du film, j’avais envie de dire qu’il ne la tue pas. Mais qu’une fois le meurtre commis, il va chez elle, sonne pour lui annoncer la mort de Dick Laurent. Il a alors juste le temps de s’enfuir, la police à ses trousses.

    Hypothese n°2

    -

    La première partie resemble à la réalité. Il a vraiment tué sa femme. Il se fait arrété et condamné (certainement à mort). Par contre on ne sait pas s’il est aussi allé tuer Andy.

    +

    La première partie resemble à la réalité. Il a vraiment tué sa femme. Il se fait arrété et condamné (certainement à mort). Par contre on ne sait pas s’il est aussi allé tuer Andy.

    -

    alors c’est laquelle ?

    +

    alors c’est laquelle ?

    La seconde hypothèse me semble plus vraisemblable, car il y a plus de recoupements possibles. -La première me semble aussi cohérente. C’est cette première hypothèse que j’avais émise lors de mon premier visionnage du film.

    +La première me semble aussi cohérente. C’est cette première hypothèse que j’avais émise lors de mon premier visionnage du film.

    -

    Ce qui montre la force de ce film c’est de se dire qu’il y a de nombreuses autres hypothèses qui pourraient aussi bien fonctionner. C’est le fameux effet Rashomon. Plusieurs personnes peuvent décrire de façon cohérentes ce qu’elles ont vu, mais toutes les descriptions sont incohérentes entres-elles.

    +

    Ce qui montre la force de ce film c’est de se dire qu’il y a de nombreuses autres hypothèses qui pourraient aussi bien fonctionner. C’est le fameux effet Rashomon. Plusieurs personnes peuvent décrire de façon cohérentes ce qu’elles ont vu, mais toutes les descriptions sont incohérentes entres-elles.

    @@ -88,11 +91,11 @@ La première me semble aussi cohérente. C’est cette première hypothèse

    Conclusion

    -

    Il y aurait encore beaucoup à dire sur l’analyse de ce film. Mais il me semble que j’ai rassemblé l’essentiel des clés pour sa compréhension.

    +

    Il y aurait encore beaucoup à dire sur l’analyse de ce film. Mais il me semble que j’ai rassemblé l’essentiel des clés pour sa compréhension.

    -

    Il me semble qu’avoir à l’esprit l’effet “test de Rorschach” est essentiel lors de la visualisation de ce film.

    +

    Il me semble qu’avoir à l’esprit l’effet “test de Rorschach” est essentiel lors de la visualisation de ce film.

    -

    J’aimerai avoir votre opinion ; mon interprétation tient-elle la route ?

    +

    J’aimerai avoir votre opinion ; mon interprétation tient-elle la route ?

    diff --git a/output/Scratch/fr/blog/03_losthighway/index.html b/output/Scratch/fr/blog/03_losthighway/index.html index 6b586ea4a..433ab829b 100644 --- a/output/Scratch/fr/blog/03_losthighway/index.html +++ b/output/Scratch/fr/blog/03_losthighway/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@
    -

    Lost Highway

    +

    blogimage( ‘intro.jpg’ , ‘Lost Highway’ )

    Lost Highway ne laisse pas indiférent. Le revoir ne lasse pas même s'il parrait complètement obscur. @@ -69,46 +72,46 @@ Je donne cependant quelques clés que j'ai découverte qui aident à suivre le f Ces clés devraient vous aider à vous faire votre propre idée du film...
    -

    La première fois que j’ai vu Lost Highway je me suis senti un peu perdu. -J’en ai alors cherché le sens. Voilà ce que j’ai pu trouver sur Internet :

    +

    La première fois que j’ai vu Lost Highway je me suis senti un peu perdu. +J’en ai alors cherché le sens. Voilà ce que j’ai pu trouver sur Internet :

      -
    • Fred passe un pacte avec le diable incarné par l’homme en noir ;
    • -
    • l’homme mystérieux est une (la) caméra ;
    • -
    • seule la première histoire est vrai, la suite étant l’imagination de Fred ;
    • +
    • Fred passe un pacte avec le diable incarné par l’homme en noir ;
    • +
    • l’homme mystérieux est une (la) caméra ;
    • +
    • seule la première histoire est vrai, la suite étant l’imagination de Fred ;

    sans compter les multiples avis trouvés sur les forums. Tout cela ne me paraissait pas convaincant. -J’ai alors réussi à trouver deux articles (en anglais) qui proposent de bien meilleures interprétations. Mais aucun des deux ne m’a complètement convaincu :

    +J’ai alors réussi à trouver deux articles (en anglais) qui proposent de bien meilleures interprétations. Mais aucun des deux ne m’a complètement convaincu :

    • le permier est sur mediacircus,
    • le second qui développe presque la même interprétation que la première est vraiment très détaillé sur jasonweb.
    -

    Il faut garder à l’esprit qu’il n’existe pas une seule interprétation possible et cohérente du film. Seul David Lynch pourrait donner l’explication complète du film.

    +

    Il faut garder à l’esprit qu’il n’existe pas une seule interprétation possible et cohérente du film. Seul David Lynch pourrait donner l’explication complète du film.

    Je donne quelques clés aidant à suivre le film sans être complètement perdu. Ces clés devraient vous aider à vous faire votre propre idée du film.

    Le test de Rorschach

    -

    test de Rorschach -À l’instar du protagoniste chacun voit dans ce film ce qu’il a envie d’y voir. Nous pouvons nous y perdre simplement parce que nous pouvons nous perdre dans notre propre esprit. C’est une invitation à la réflexion. Regarder ce film c’est un peu comme passer un test de Rorschach. Qu’y voit-on ? Chacun y met un peu de sa propre personnalité dans l’explication du film.

    +

    leftblogimage(‘rorschach.gif’ , ‘test de Rorschach’) +À l’instar du protagoniste chacun voit dans ce film ce qu’il a envie d’y voir. Nous pouvons nous y perdre simplement parce que nous pouvons nous perdre dans notre propre esprit. C’est une invitation à la réflexion. Regarder ce film c’est un peu comme passer un test de Rorschach. Qu’y voit-on ? Chacun y met un peu de sa propre personnalité dans l’explication du film.

      -
    • Si vous êtes un mystique, vous verrez dans l’homme mystérieux un démon
    • -
    • si vous êtes plus psychanalytique vous y verrez une partie inconsciente du protagoniste…
    • +
    • Si vous êtes un mystique, vous verrez dans l’homme mystérieux un démon
    • +
    • si vous êtes plus psychanalytique vous y verrez une partie inconsciente du protagoniste…
    -

    En général en essayant d’expliquer ce film, on se perd un peu dans notre pensée. Et souvent on échoue à tout expliquer. Il y a toujours un point qui rend la construction incohérente avec le film. C’est pourquoi rechercher une explication unique est un entreprise vaine.

    +

    En général en essayant d’expliquer ce film, on se perd un peu dans notre pensée. Et souvent on échoue à tout expliquer. Il y a toujours un point qui rend la construction incohérente avec le film. C’est pourquoi rechercher une explication unique est un entreprise vaine.

    -

    Interprétation ≠ Explication

    +

    Interprétation ≠ Explication

    Je donne une interprétation et non pas une explication. Ma vision des choses, me semble cohérente. Cependant il est très probable que mon adhésion au film soit très différente de la votre. -Il y a certainement beaucoup d’autres explications qui restent cohérentes.

    +Il y a certainement beaucoup d’autres explications qui restent cohérentes.

    -

    J’écris cet article, parce que j’ai l’impression d’en avoir trouver une qui marche pour plus de 97% du film (peut-être 100%, mais j’en doute, il faudrait que je le revois encore une fois).

    +

    J’écris cet article, parce que j’ai l’impression d’en avoir trouver une qui marche pour plus de 97% du film (peut-être 100%, mais j’en doute, il faudrait que je le revois encore une fois).

    @@ -221,7 +224,7 @@ Il y a certainement beaucoup d’autres explications qui restent cohérentes
    Écrit le : 04/08/2009 - modifié le : 09/05/2010 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/04_drm/index.html b/output/Scratch/fr/blog/04_drm/index.html index 5b38504b3..ddeefbf75 100644 --- a/output/Scratch/fr/blog/04_drm/index.html +++ b/output/Scratch/fr/blog/04_drm/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,13 +63,13 @@

    Protections anti-copie = Belle connerie (+1)!

    -

    Ma femme a acheté pour environ 500€ (au moins) de séries télé sur iTunes. Mais elles s’est trompé pour la première saison de Battlestar Galactica. Qu’elle a téléchargé en anglais. Hors comme les séries sont protégées, on ne peut simplement pas voir la série avec des sous-titres !

    +

    Ma femme a acheté pour environ 500€ (au moins) de séries télé sur iTunes. Mais elles s’est trompé pour la première saison de Battlestar Galactica. Qu’elle a téléchargé en anglais. Hors comme les séries sont protégées, on ne peut simplement pas voir la série avec des sous-titres !

    WTF?

    -

    Résultat des courses, ma femme n’achetera plus de séries sur iTunes tant qu’elles resteront protégées par DRM, qu’elle ne pourront pas être gravées sur DVD (pour être regardées sur la télé). Et comme elle est bien moins prompte à acheter des DVD que simplement cliquer sur un bouton.

    +

    Résultat des courses, ma femme n’achetera plus de séries sur iTunes tant qu’elles resteront protégées par DRM, qu’elle ne pourront pas être gravées sur DVD (pour être regardées sur la télé). Et comme elle est bien moins prompte à acheter des DVD que simplement cliquer sur un bouton.

    @@ -75,7 +78,7 @@ Ca fera nettement moins d'argent pour vous les ayant-droits&nbs

    Et ma femme ne pourra pas voir ces épisodes.
    -C’est ce qu’on appelle une cooperation ‘LOSE-LOSE’.

    +C’est ce qu’on appelle une cooperation ‘LOSE-LOSE’.

    diff --git a/output/Scratch/fr/blog/05_git_create_remote_branch/code/git-create-new-branch.sh b/output/Scratch/fr/blog/05_git_create_remote_branch/code/git-create-new-branch.sh index b72b97815..6b36930d3 100644 --- a/output/Scratch/fr/blog/05_git_create_remote_branch/code/git-create-new-branch.sh +++ b/output/Scratch/fr/blog/05_git_create_remote_branch/code/git-create-new-branch.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh if (($#<1)); then diff --git a/output/Scratch/fr/blog/05_git_create_remote_branch/index.html b/output/Scratch/fr/blog/05_git_create_remote_branch/index.html index 7cffb29f1..7b7e65367 100644 --- a/output/Scratch/fr/blog/05_git_create_remote_branch/index.html +++ b/output/Scratch/fr/blog/05_git_create_remote_branch/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,32 +59,33 @@

    Créer une branche Git externe facilement

    -

    J’utilise Git pour synchroniser des projets personnels. -C’est pourquoi quand je crée une branche locale je souhaite quasiment toujours qu’elle soit aussi créée en externe (remote).

    +

    J’utilise Git pour synchroniser des projets personnels. +C’est pourquoi quand je crée une branche locale je souhaite quasiment toujours qu’elle soit aussi créée en externe (remote).

    -

    Voici le script que j’utilise pour accomplir cette tâche :

    +

    Voici le script que j’utilise pour accomplir cette tâche :

    -
    -
    -#!/usr/bin/env zsh
    +    
     
    -if (($#<1)); then
    -    print -- "usage: $0:t branch_name" >&2
    +
    +
    #!/usr/bin/env zsh
    +
    +if (($#<1)); then
    +    print -- "usage: $0:t branch_name" >&2
         exit 1
    -fi
    +fi
    +
    +branch=$1
    +git br ${branch}
    +git co ${branch}
    +git config branch.${branch}.remote origin
    +git config branch.${branch}.merge refs/heads/${branch}
    +    
    + -branch=$1 -git br ${branch} -git co ${branch} -git config branch.${branch}.remote origin -git config branch.${branch}.merge refs/heads/${branch} - -
    -
    -

    Bien sûr, je suppose qu’origin est déjà configurée.

    +

    Bien sûr, je suppose qu’origin est déjà configurée.

    diff --git a/output/Scratch/fr/blog/06_How_I_use_git/code/git-create-new-branch b/output/Scratch/fr/blog/06_How_I_use_git/code/git-create-new-branch index add959c52..4ce31d7cd 100644 --- a/output/Scratch/fr/blog/06_How_I_use_git/code/git-create-new-branch +++ b/output/Scratch/fr/blog/06_How_I_use_git/code/git-create-new-branch @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh if (($#<1)); then diff --git a/output/Scratch/fr/blog/06_How_I_use_git/code/git-get-remote-branches b/output/Scratch/fr/blog/06_How_I_use_git/code/git-get-remote-branches index ca1ef8c3b..dd2b3ebc3 100644 --- a/output/Scratch/fr/blog/06_How_I_use_git/code/git-get-remote-branches +++ b/output/Scratch/fr/blog/06_How_I_use_git/code/git-get-remote-branches @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # recup branches not on local diff --git a/output/Scratch/fr/blog/06_How_I_use_git/index.html b/output/Scratch/fr/blog/06_How_I_use_git/index.html index 7850465d7..c69c7cc37 100644 --- a/output/Scratch/fr/blog/06_How_I_use_git/index.html +++ b/output/Scratch/fr/blog/06_How_I_use_git/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,17 +63,17 @@
    -

    Màj : Actuellement j’utilise github avec des repository privés. Je paye une somme très raisonnable pour ce service. Si vous voulez être complètement autonome, je vous conseille d’utiliser gitolite sur votre propre serveur accessible sur le web.

    +

    Màj : Actuellement j’utilise github avec des repository privés. Je paye une somme très raisonnable pour ce service. Si vous voulez être complètement autonome, je vous conseille d’utiliser gitolite sur votre propre serveur accessible sur le web.

    -

    J’utilise Git pour gérer mes projets personnels. -J’ai un repository centralisé et tous mes ordinateurs se synchronisent avec lui. -Cependant, dans la documentation officielle, je n’ai pas trouvé clairement ce que je souhaitais.

    +

    J’utilise Git pour gérer mes projets personnels. +J’ai un repository centralisé et tous mes ordinateurs se synchronisent avec lui. +Cependant, dans la documentation officielle, je n’ai pas trouvé clairement ce que je souhaitais.

    -

    En d’autres termes, si vous souhaitez utiliser le type de workflow que SVN proposait avec Git (et ses avantages), voici comment procéder.

    +

    En d’autres termes, si vous souhaitez utiliser le type de workflow que SVN proposait avec Git (et ses avantages), voici comment procéder.

    @@ -80,38 +83,47 @@ Cependant, dans la documentation officielle, je n’ai pas trouvé clairemen

    Initialisation

    -

    Disons que j’ai déjà un projet et que je veuille en créer un nouveau.

    +

    Disons que j’ai déjà un projet et que je veuille en créer un nouveau.

    -
    -cd to/project/directory/
    +
    +
    +
    cd to/project/directory/
     git init
     git add
     git commit
    -
    +
    + +

    Maintenant tous les fichiers du répertoire to/project/directory/ sont versionnés. Si vous voulez ignorer certains fichiers il suffit de modifier le fichier .gitignore.

    Par exemple voici le mien :

    -
    -*.swp
    +
    +
    +
    *.swp
     .DS_Store
     ikog.py.bak
     output/Scratch/assets
     output/Scratch/en
     output/Scratch/fr
     output/Scratch/multi
    -
    +
    + +

    Ensuite, il faut placer ce projet dans un répertoire accessible via Internet.

    -
    -git clone --bare . /path/to/repository
    -
    + + +
    git clone --bare . /path/to/repository
    +
    + +

    et local_directory contiendra un projet à jour.

    @@ -141,34 +156,43 @@ Je vous conseille de faire la même opération sur l'ordinateur qui à servi à
    -

    L’utilisation courante

    +

    L’utilisation courante

    Pour résumer vous avez maintenant un repository sur Internet et un ou plusieurs ordinateurs lui sont associés. Maintenant il faut que tout soit toujours synchronisé.

    -

    Avant de commencer à travailler, la première chose à faire est de récupérer les modification à partir d’Internet vers votre poste local :

    +

    Avant de commencer à travailler, la première chose à faire est de récupérer les modification à partir d’Internet vers votre poste local :

    -
    -git pull
    -
    + + +
    git pull
    +
    + +

    Ensuit vous pouvez travailler en faisant (plusieurs fois) :

    -
    -hack, hack, hack...
    +
    +
    +
    hack, hack, hack...
     git add some files
     git commit
    -
    +
    + +

    Quang vous voulez envoyez les modifications locales sur Internet, il suffit de faire :

    -
    -git push
    -
    + + +
    git push
    +
    + +

    Tout devrait être bon.

    @@ -176,74 +200,87 @@ git push

    Si vous avez des problèmes avec le push et le pull ; vérifiez votre fichier .git/config. Il devrait contenir les lignes suivantes :

    -
    -...
    -[remote "origin"]
    +
    +
    +
    ...
    +[remote "origin"]
     	url = protocol://url/of/the/repository
     	fetch = +refs/heads/*:refs/remotes/origin/*
    -[branch "master"]
    +[branch "master"]
     	remote = origin
     	merge = refs/heads/master
     ...
    -
    +
    + +

    Synchronisation des branches

    -

    Bien, maintenant que tout semble bon, il faut encore s’occuper de quelques petites choses (sinon, SVN suffirait). -Git est complètement orienté sur la décentralisation et la création de nouvelles branches sur le même poste. Synchroniser des branches sur plusieurs serveurs différent n’est pas une opération naturelle.

    +

    Bien, maintenant que tout semble bon, il faut encore s’occuper de quelques petites choses (sinon, SVN suffirait). +Git est complètement orienté sur la décentralisation et la création de nouvelles branches sur le même poste. Synchroniser des branches sur plusieurs serveurs différent n’est pas une opération naturelle.

    -

    C’est pourquoi j’ai créé deux simples scripts pour automatiser cette opération. Un script pour créer un branche localement et en ligne. Un autre script pour récupérer les branches en lignes qui ne sont pas présente localement.

    +

    C’est pourquoi j’ai créé deux simples scripts pour automatiser cette opération. Un script pour créer un branche localement et en ligne. Un autre script pour récupérer les branches en lignes qui ne sont pas présente localement.

    Ainsi, lorsque je veux créer une nouvelle branche (localement et ligne) ; je lance le script :

    -
    git-create-new-branch branch_name
    -
    +
    -

    et quand je suis sur un autre ordinateur et que je veux récupérer les branches crées sur un autre poste, j’exécute :

    +
    git-create-new-branch branch_name
    -
    git-get-remote-branches
    -
    +
    + +

    et quand je suis sur un autre ordinateur et que je veux récupérer les branches crées sur un autre poste, j’exécute :

    + +
    + +
    git-get-remote-branches
    + +

    Voici le code des deux script (en zsh) :

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -if (($#<1)); then
    -    print -- "usage: $0:t branch_name" >&2
    +
    +
    #!/usr/bin/env zsh
    +
    +if (($#<1)); then
    +    print -- "usage: $0:t branch_name" >&2
         exit 1
    -fi
    +fi
    +
    +branch=$1
    +git br ${branch}
    +git co ${branch}
    +git config branch.${branch}.remote origin
    +git config branch.${branch}.merge refs/heads/${branch}
    +
    + -branch=$1 -git br ${branch} -git co ${branch} -git config branch.${branch}.remote origin -git config branch.${branch}.merge refs/heads/${branch} -
    -
    -
    -
    -#!/usr/bin/env zsh
    +
    +
    +
    +
    #!/usr/bin/env zsh
    +
    +# recup branches not on local
    +localbranches=( $(git br | sed 's/\*/ /') )
    +remoteMissingBranches=( $(git br -r | \
    +    egrep -v "origin/HEAD|(${(j:|:)localbranches})" ) )
    +for br in $remoteMissingBranches; do
    +  branch=${br#origin/}
    +  print "get remote branch $branch"
    +  git br ${branch}
    +  git config branch.${branch}.remote origin
    +  git config branch.${branch}.merge refs/heads/${branch}
    +done
    +
    + -# recup branches not on local -localbranches=( $(git br | sed 's/\*/ /') ) -remoteMissingBranches=( $(git br -r | \ - egrep -v "origin/HEAD|(${(j:|:)localbranches})" ) ) -for br in $remoteMissingBranches; do - branch=${br#origin/} - print "get remote branch $branch" - git br ${branch} - git config branch.${branch}.remote origin - git config branch.${branch}.merge refs/heads/${branch} -done -
    -
    @@ -361,7 +398,7 @@ remoteMissingBranches=( $(git b
    Écrit le : 18/08/2009 - modifié le : 20/04/2011 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html b/output/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html index 683509d65..871352e67 100644 --- a/output/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html +++ b/output/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,18 +57,18 @@
    -

    Comment recompiler un économiseur d’écran sous Snow Leopard©

    +

    Comment recompiler un économiseur d’écran sous Snow Leopard©

    -

    Mon économiseur d’écran ne fonctionnait plus sous Mac OS X 10.6 Snow Leopard©. Après un peu de recherche sous google, le problème semblait pouvoir être réglé avec une recompilation. +

    Mon économiseur d’écran ne fonctionnait plus sous Mac OS X 10.6 Snow Leopard©. Après un peu de recherche sous google, le problème semblait pouvoir être réglé avec une recompilation. Cependant, même en recomilant en 64 bits ça ne fonctionnait toujours pas. Après un peu plus de recherches (merci à ElectricSheep ), -j’ai découvert les bons paramètres.

    +j’ai découvert les bons paramètres.

    XCode configuration

    -

    Pour l’instant je ne l’ai pas compilé pour être compatible Tiger et Leopard. Je ne connais pas assez bien XCode pour savoir comment désactiver le garbage collector sur la version 32 bits et l’activer sur la version 64 bits.

    +

    Pour l’instant je ne l’ai pas compilé pour être compatible Tiger et Leopard. Je ne connais pas assez bien XCode pour savoir comment désactiver le garbage collector sur la version 32 bits et l’activer sur la version 64 bits.

    -

    Il a été assez difficile de découvrir toutes ces informations. J’espère que cet article aura pu aider quelqu’un.

    +

    Il a été assez difficile de découvrir toutes ces informations. J’espère que cet article aura pu aider quelqu’un.

    @@ -182,7 +185,7 @@ j’ai découvert les bons paramètres.

    Écrit le : 06/09/2009 - modifié le : 09/05/2010 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist b/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist index 9cccfce9d..555186218 100644 --- a/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist +++ b/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/code/ssh-443.plist @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html b/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html index 1d1d54cca..d92f9e5c1 100644 --- a/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html +++ b/output/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,80 +59,85 @@

    Surfez partout comme si vous étiez chez vous

    -

    Que ce soit pour surfer en toute sécurité depuis un accès wifi non sécurisé ou pour contourner les parefeux diaboliques des entreprises. J’ai configuré un serveur ssh écoutant sur le port 443 chez moi.

    +

    Que ce soit pour surfer en toute sécurité depuis un accès wifi non sécurisé ou pour contourner les parefeux diaboliques des entreprises. J’ai configuré un serveur ssh écoutant sur le port 443 chez moi.

    Ensuite de mon portable ou de mon ordinateur local, je dois simplement lancé la merveilleuse commande :

    -
    -ssh -p 443 -D 9050 username@host
    -
    + + +
    ssh -p 443 -D 9050 username@host
    +
    + +

    et un proxy socks écoute sur le port 9050. Ce proxy socks transférera toutes les requêtes locales via le tunnel ssh. Ainsi je peux surfer en local comme si je naviguais depuis mon ordinateur à la maison. Je peux écrire mon numéro de carte bleu sans avoir peur que le wifi local soit sniffé. Je dois simplement configurer mon navigateur web pour utiliser le proxy socks sur localhost écoutant le port 9050.

    -

    J’ai eu cette information à partir de cet article.

    +

    J’ai eu cette information à partir de cet article.

    Ssh et Snow Leopard©

    -

    J’ai un Mac avec Snow Leopard© à la maison. -Il ne suffit pas de modifier le fichier /etc/sshd.config pour changer le port d’écoute d’sshd. +

    J’ai un Mac avec Snow Leopard© à la maison. +Il ne suffit pas de modifier le fichier /etc/sshd.config pour changer le port d’écoute d’sshd. Le système utilise launchd pour lancer les démons.

    -

    J’ai posé cette question sur Apple Discussions dans ce fil de discussion. -Merci à tous ceux qui m’ont aidé. Et la solution est :

    +

    J’ai posé cette question sur Apple Discussions dans ce fil de discussion. +Merci à tous ceux qui m’ont aidé. Et la solution est :

    Créer un fichier /Library/LaunchDaemons/ssh-443.plist contenant :

    -
    -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    -<plist version="1.0">
    -<dict>
    -	<key>Disabled</key>
    -	<false/>
    -	<key>Label</key>
    -	<string>local.sshd</string>
    -	<key>Program</key>
    -	<string>/usr/libexec/sshd-keygen-wrapper</string>
    -	<key>ProgramArguments</key>
    -	<array>
    -		<string>/usr/sbin/sshd</string>
    -		<string>-i</string>
    -	</array>
    -	<key>Sockets</key>
    -	<dict>
    -		<key>Listeners</key>
    -		<dict>
    -			<key>SockServiceName</key>
    -			<string>https</string>
    -		</dict>
    -	</dict>
    -	<key>inetdCompatibility</key>
    -	<dict>
    -		<key>Wait</key>
    -		<false/>
    -	</dict>
    -	<key>StandardErrorPath</key>
    -	<string>/dev/null</string>
    -        <key>SHAuthorizationRight</key>
    -        <string>system.preferences</string>
    -</dict>
    -</plist>
    -
    -
    + + + +
    <?xml version="1.0" encoding="UTF-8"?>
    +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    +<plist version="1.0">
    +<dict>
    +	<key>Disabled</key>
    +	<false/>
    +	<key>Label</key>
    +	<string>local.sshd</string>
    +	<key>Program</key>
    +	<string>/usr/libexec/sshd-keygen-wrapper</string>
    +	<key>ProgramArguments</key>
    +	<array>
    +		<string>/usr/sbin/sshd</string>
    +		<string>-i</string>
    +	</array>
    +	<key>Sockets</key>
    +	<dict>
    +		<key>Listeners</key>
    +		<dict>
    +			<key>SockServiceName</key>
    +			<string>https</string>
    +		</dict>
    +	</dict>
    +	<key>inetdCompatibility</key>
    +	<dict>
    +		<key>Wait</key>
    +		<false/>
    +	</dict>
    +	<key>StandardErrorPath</key>
    +	<string>/dev/null</string>
    +        <key>SHAuthorizationRight</key>
    +        <string>system.preferences</string>
    +</dict>
    +</plist>
    +
    + +
    -

    C’est une copie de /System/Library/LaunchDaemons/ssh.plist avec quelques modifications :

    +

    C’est une copie de /System/Library/LaunchDaemons/ssh.plist avec quelques modifications :

    • le SockServiceName est devenu https au lieu de ssh
    • -
    • le Label est passé de com.openssh.sshd à quelque chose qui n’existait pas comme local.sshd
    • +
    • le Label est passé de com.openssh.sshd à quelque chose qui n’existait pas comme local.sshd
    -

    Encore une fois j’espère que ça a pu être utile.

    +

    Encore une fois j’espère que ça a pu être utile.

    diff --git a/output/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/index.html b/output/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/index.html index a7b43f215..13295979d 100644 --- a/output/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/index.html +++ b/output/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,13 +57,13 @@
    -

    J’ai arrété d’utiliser whos.amung.us en faveur de Google Analytics.

    +

    J’ai arrété d’utiliser whos.amung.us en faveur de Google Analytics.

    -

    La plupart du temps je préfère ne pas utiliser le même produit que tout le monde. J’aime bien essayer des choses un peu nouvelles. Mais whosamung.us avait trop de publicités. Je devais affichier une de leur image sur mon site qui n’écrivait que le nombre de personne actuellement présentes. Pas les nombres de visites.

    +

    La plupart du temps je préfère ne pas utiliser le même produit que tout le monde. J’aime bien essayer des choses un peu nouvelles. Mais whosamung.us avait trop de publicités. Je devais affichier une de leur image sur mon site qui n’écrivait que le nombre de personne actuellement présentes. Pas les nombres de visites.

    -

    C’est pourquoi j’utilise maintenant google analytics. Le problème reste entier pour les navigateurs sans javascript.

    +

    C’est pourquoi j’utilise maintenant google analytics. Le problème reste entier pour les navigateurs sans javascript.

    -

    Donc pour l’instant

    +

    Donc pour l’instant

    Théorème :
    diff --git a/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish b/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish index da6ce6b0c..0ea3558bf 100644 --- a/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish +++ b/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/code/publish @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Author: Yann Esposito diff --git a/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html b/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html index ad07ea9d5..b55dd1593 100644 --- a/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html +++ b/output/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,8 +63,8 @@

    mise à jour du 28/10/2009

    -

    J’ai mis à jour mon script avec une version incrémentale bien plus pratique. -En plus depuis l’écriture de cet article Apple© semble avoir nettement amélioré la vitesse de ses serveurs en Europe.

    +

    J’ai mis à jour mon script avec une version incrémentale bien plus pratique. +En plus depuis l’écriture de cet article Apple© semble avoir nettement amélioré la vitesse de ses serveurs en Europe.

    @@ -71,245 +74,249 @@ En plus depuis l’écriture de cet article Apple© sembl

    WebDav terror

    -

    En France l’iDisk d’Apple© est très lent. La vitesse d’upload me rapelle l’époque des modem 56k, c’est dire. La plupart du temps les opérations telles que lister le contenu d’un répertoire prennent au moins 30 secondes (pour 15 éléments). Renommer un répertoire échoue presque systématiquement.

    +

    En France l’iDisk d’Apple© est très lent. La vitesse d’upload me rapelle l’époque des modem 56k, c’est dire. La plupart du temps les opérations telles que lister le contenu d’un répertoire prennent au moins 30 secondes (pour 15 éléments). Renommer un répertoire échoue presque systématiquement.

    -

    Apple© utilise des serveurs WebDav pour héberger les fichiers. Le protocole fonctionne sur le port 80 (comme http). Je me suis rendu compte qu’utiliser WebDav via https fontionne bien mieux (2 à 3 fois plus rapide avec moins d’erreurs). Mais, ça reste quand même très lent et insuffisant.

    +

    Apple© utilise des serveurs WebDav pour héberger les fichiers. Le protocole fonctionne sur le port 80 (comme http). Je me suis rendu compte qu’utiliser WebDav via https fontionne bien mieux (2 à 3 fois plus rapide avec moins d’erreurs). Mais, ça reste quand même très lent et insuffisant.

    -

    J’uploade mes fichiers à partir de mon Mac et de temps en temps à partir d’un PC sous Ubuntu (iDisk monté avec webdavfs).

    +

    J’uploade mes fichiers à partir de mon Mac et de temps en temps à partir d’un PC sous Ubuntu (iDisk monté avec webdavfs).

    Synchroniser de façon sûre

    -

    Voici le script que j’utilise pour synchroniser mon site web (non créé avec iWeb©) avec le maximum de sécurité. Chaque opération est répétée jusqu’à ce qu’elle fonctionne.

    +

    Voici le script que j’utilise pour synchroniser mon site web (non créé avec iWeb©) avec le maximum de sécurité. Chaque opération est répétée jusqu’à ce qu’elle fonctionne.

    Les idées sont :

      -
    • Synchroniser vers un répertoire temporaire sur le serveur distant, puis “swapper” les noms des répertoires. Ainsi le site ne reste indisponible que le temps du “swap” du nom des deux répertoires.
    • -
    • Réitérer toutes les opérations jusqu’à ce qu’elle aient réussi (par exemple pour le renommage)
    • +
    • Synchroniser vers un répertoire temporaire sur le serveur distant, puis “swapper” les noms des répertoires. Ainsi le site ne reste indisponible que le temps du “swap” du nom des deux répertoires.
    • +
    • Réitérer toutes les opérations jusqu’à ce qu’elle aient réussi (par exemple pour le renommage)
    -

    Jusqu’ici j’utilise rsync qui n’est en fait pas plus efficace qu’une simple copie cp avec WebDav. Je devrais utiliser une méthode pour mémoriser les changements entre chaque publication.

    +

    Jusqu’ici j’utilise rsync qui n’est en fait pas plus efficace qu’une simple copie cp avec WebDav. Je devrais utiliser une méthode pour mémoriser les changements entre chaque publication.

    -

    En réalité quand je suis sur mon Mac j’utilise Transmit qui est vraiment très bien et surtout beaucoup plus efficace que le finder pour synchroniser des fichiers. Ensuite, je ne fait que le “swap” des répertoires.

    +

    En réalité quand je suis sur mon Mac j’utilise Transmit qui est vraiment très bien et surtout beaucoup plus efficace que le finder pour synchroniser des fichiers. Ensuite, je ne fait que le “swap” des répertoires.

    -

    Mon script prend un paramètre -s pour ne faire que le “swap”. Il prend aussi une option -a pour envoyer le fichier index.html qui va rediriger vers ma nouvelle page principale (iWeb© à la fâcheuse habitude de le remplacer).

    +

    Mon script prend un paramètre -s pour ne faire que le “swap”. Il prend aussi une option -a pour envoyer le fichier index.html qui va rediriger vers ma nouvelle page principale (iWeb© à la fâcheuse habitude de le remplacer).

    -

    Pour utiliser le script vous devriez remplacer la valeur de la variable mobileMeUser par votre nom d’utilisateur mobileMe©.

    +

    Pour utiliser le script vous devriez remplacer la valeur de la variable mobileMeUser par votre nom d’utilisateur mobileMe©.

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Script synchronisant le site sur me.com
    -# normalement, le site est indisponible le moins de temps possible
    -# le temps de deux renommages de répertoire
     
    -mobileMeUser="yann.esposito"
    -siteName="siteName"
    +
    #!/usr/bin/env zsh
     
    -# Depending of my hostname the 
    -if [[ $(hostname) == 'ubuntu' ]]; then
    -    iDisk='/mnt/iDisk'
    -else
    -    iDisk="/Volumes/$mobileMeUser"
    -fi
    +# Script synchronisant le site sur me.com
    +# normalement, le site est indisponible le moins de temps possible
    +# le temps de deux renommages de répertoire
     
    -root=$HOME/Sites/$siteName
    -destRep=$iDisk/Web/Sites/$siteName
    +mobileMeUser="yann.esposito"
    +siteName="siteName"
     
    -[[ ! -d $root ]] && { 
    -    print -- "$root n'existe pas ; vérifiez la conf" >&2; 
    +# Depending of my hostname the 
    +if [[ $(hostname) == 'ubuntu' ]]; then
    +    iDisk='/mnt/iDisk'
    +else
    +    iDisk="/Volumes/$mobileMeUser"
    +fi
    +
    +root=$HOME/Sites/$siteName
    +destRep=$iDisk/Web/Sites/$siteName
    +
    +[[ ! -d $root ]] && { 
    +    print -- "$root n'existe pas ; vérifiez la conf" >&2; 
         exit 1 
     }
     
    -[[ ! -d $destRep ]] && { 
    -    print -- "$destRep n'existe pas, veuillez remonter le FS" >&2; 
    +[[ ! -d $destRep ]] && { 
    +    print -- "$destRep n'existe pas, veuillez remonter le FS" >&2; 
         exit 1 
     }
     
    -if [[ $1 == '-h' ]]; then
    -    print -- "usage: $0:h [-h|-a|-s]"
    -    print -- "  -a sychronise aussi l'index"
    -    print -- "  -h affiche l'aide"
    -    print -- "  -s swappe simplement les répertoires"
    -fi
    +if [[ $1 == '-h' ]]; then
    +    print -- "usage: $0:h [-h|-a|-s]"
    +    print -- "  -a sychronise aussi l'index"
    +    print -- "  -h affiche l'aide"
    +    print -- "  -s swappe simplement les répertoires"
    +fi
     
    -if [[ $1 == '-a' ]]; then
    -    print -- "Synchronisation de l'index (${destRep:h})"
    -    rsync -av $root/index.html ${destRep:h}/index.html
    -fi
    +if [[ $1 == '-a' ]]; then
    +    print -- "Synchronisation de l'index (${destRep:h})"
    +    rsync -av $root/index.html ${destRep:h}/index.html
    +fi
     
    -print -- "Root = $root"
    -print -- "Dest = $destRep"
    +print -- "Root = $root"
    +print -- "Dest = $destRep"
     
    -if [[ ! $1 = '-s' ]]; then
    -    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    -    print -P -- "%B[Sync => tmp]%b"
    +if [[ ! $1 = '-s' ]]; then
    +    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    +    print -P -- "%B[Sync => tmp]%b"
         result=1
         essai=1
    -    while (( $result > 0 )); do
    -        rsync -arv $root/Scratch/ $destRep.tmp
    -        result=$?
    -        if (( $result > 0 )); then
    -            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
    -        fi
    +    while (( $result > 0 )); do
    +        rsync -arv $root/Scratch/ $destRep.tmp
    +        result=$?
    +        if (( $result > 0 )); then
    +            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
    +        fi
             ((essai++))
    -    done
    -fi
    +    done
    +fi
     
    -# SWAP
    -print -P -- "%B[Swap des Répertoires (tmp <=> target)]%b"
    +# SWAP
    +print -P -- "%B[Swap des Répertoires (tmp <=> target)]%b"
     essai=1
    -while [[ -e $destRep.old ]]; do
    -    print -n -- "suppression de $destRep.old"
    -    if ((essai>1)); then 
    -        print " (essai n°$essai)"
    -    else
    +while [[ -e $destRep.old ]]; do
    +    print -n -- "suppression de $destRep.old"
    +    if ((essai>1)); then 
    +        print " (essai n°$essai)"
    +    else
             print
    -    fi
    +    fi
         ((essai++))
    -    \rm -rf $destRep.old
    -done
    +    \rm -rf $destRep.old
    +done
     
    -print -- "  renommage du repertoire sandard vers le .old"
    +print -- "  renommage du repertoire sandard vers le .old"
     essai=1
    -while [[ -e $destRep ]]; do
    -    mv $destRep $destRep.old 
    -    (($?)) && print -- "Echec du renommage (essai n°$essai)" >&2
    +while [[ -e $destRep ]]; do
    +    mv $destRep $destRep.old 
    +    (($?)) && print -- "Echec du renommage (essai n°$essai)" >&2
         ((essai++))
    -done
    +done
     
    -print -- "  renommage du repertoire tmp (nouveau) vers le standard"
    -print -P -- "  %BSite Indisponible%b $(date)"
    +print -- "  renommage du repertoire tmp (nouveau) vers le standard"
    +print -P -- "  %BSite Indisponible%b $(date)"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.tmp $destRep
    -    (($?)) && print -P -- "%B[Site Indisponible]%b(essai n°$essai) Echec du renommage (mv $destRep.tmp $destRep)" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.tmp $destRep
    +    (($?)) && print -P -- "%B[Site Indisponible]%b(essai n°$essai) Echec du renommage (mv $destRep.tmp $destRep)" >&2
         ((essai++))
    -done
    +done
     
    -print -P -- "\t===\t%BSITE DISPONIBLE%b\t==="
    +print -P -- "\t===\t%BSITE DISPONIBLE%b\t==="
     
    -print -- "  renommage du repertoire old vers le tmp"
    +print -- "  renommage du repertoire old vers le tmp"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.old $destRep.tmp
    -    (($?)) && print -P -- "Echec du renommage n°$essai" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.old $destRep.tmp
    +    (($?)) && print -P -- "Echec du renommage n°$essai" >&2
         ((essai++))
    -done
    +done
    +
    +print -P -- "  publication terminée"
    +
    + -print -P -- " publication terminée" -
    -
    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Author: Yann Esposito
    -#   Mail: yann.esposito@gmail.com
    -# Synchronize with "mobileMe" iDisk account.
     
    -mobileMeUser="firstname.lastname"
    -siteName="siteName"
    +
    #!/usr/bin/env zsh
     
    -# Depending of my hostname the 
    -if [[ $(hostname) == 'ubuntu' ]]; then
    -    iDisk='/mnt/iDisk'
    -else
    -    iDisk="/Volumes/$mobileMeUser"
    -fi
    +# Author: Yann Esposito
    +#   Mail: yann.esposito@gmail.com
    +# Synchronize with "mobileMe" iDisk account.
     
    -root=$HOME/Sites/$siteName
    -destRep=$iDisk/Web/Sites/$siteName
    +mobileMeUser="firstname.lastname"
    +siteName="siteName"
     
    -[[ ! -d $root ]] && { 
    -    print -- "$root does not exist ; please verify the configuration ($0)" >&2; 
    +# Depending of my hostname the 
    +if [[ $(hostname) == 'ubuntu' ]]; then
    +    iDisk='/mnt/iDisk'
    +else
    +    iDisk="/Volumes/$mobileMeUser"
    +fi
    +
    +root=$HOME/Sites/$siteName
    +destRep=$iDisk/Web/Sites/$siteName
    +
    +[[ ! -d $root ]] && { 
    +    print -- "$root does not exist ; please verify the configuration ($0)" >&2; 
         exit 1 
     }
     
    -[[ ! -d $destRep ]] && { 
    -    print -- "$destRep does not exist, please mount the filesystem" >&2; 
    +[[ ! -d $destRep ]] && { 
    +    print -- "$destRep does not exist, please mount the filesystem" >&2; 
         exit 1 
     }
     
    -if [[ $1 == '-h' ]]; then
    -    print -- "usage: $0:h [-h|-a|-s]"
    -    print -- "  -a sychronize primary index"
    -    print -- "  -h show this help"
    -    print -- "  -s only swap directories"
    -fi
    +if [[ $1 == '-h' ]]; then
    +    print -- "usage: $0:h [-h|-a|-s]"
    +    print -- "  -a sychronize primary index"
    +    print -- "  -h show this help"
    +    print -- "  -s only swap directories"
    +fi
     
    -if [[ $1 == '-a' ]]; then
    -    print -- "Index synchronisation (${destRep:h})"
    -    rsync -av $root/index.html ${destRep:h}/index.html
    -fi
    +if [[ $1 == '-a' ]]; then
    +    print -- "Index synchronisation (${destRep:h})"
    +    rsync -av $root/index.html ${destRep:h}/index.html
    +fi
     
    -print -- "Root = $root"
    -print -- "Dest = $destRep"
    +print -- "Root = $root"
    +print -- "Dest = $destRep"
     
    -if [[ ! $1 = '-s' ]]; then
    -    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    -    print -P -- "%B[Sync => tmp]%b"
    +if [[ ! $1 = '-s' ]]; then
    +    [[ ! -d $destRep.tmp ]] && mkdir $destRep.tmp
    +    print -P -- "%B[Sync => tmp]%b"
         result=1
         essai=1
    -    while (( $result > 0 )); do
    -        rsync -arv $root/Scratch/ $destRep.tmp
    -        result=$?
    -        if (( $result > 0 )); then
    -            print -P -- "%Brsync failed%b (try n°$essai)" >&2
    -        fi
    +    while (( $result > 0 )); do
    +        rsync -arv $root/Scratch/ $destRep.tmp
    +        result=$?
    +        if (( $result > 0 )); then
    +            print -P -- "%Brsync failed%b (try n°$essai)" >&2
    +        fi
             ((essai++))
    -    done
    -fi
    +    done
    +fi
     
    -# SWAP
    -print -P -- "%B[Directory Swap (tmp <=> target)]%b"
    +# SWAP
    +print -P -- "%B[Directory Swap (tmp <=> target)]%b"
     essai=1
    -while [[ -e $destRep.old ]]; do
    -    print -n -- "remove $destRep.old"
    -    if ((essai>1)); then 
    -        print " (try n°$essai)"
    -    else
    +while [[ -e $destRep.old ]]; do
    +    print -n -- "remove $destRep.old"
    +    if ((essai>1)); then 
    +        print " (try n°$essai)"
    +    else
             print
    -    fi
    +    fi
         ((essai++))
    -    \rm -rf $destRep.old
    -done
    +    \rm -rf $destRep.old
    +done
     
    -print -- "  renommage du repertoire sandard vers le .old"
    +print -- "  renommage du repertoire sandard vers le .old"
     essai=1
    -while [[ -e $destRep ]]; do
    -    mv $destRep $destRep.old 
    -    (($?)) && print -- "Failed to rename (try n°$essai)" >&2
    +while [[ -e $destRep ]]; do
    +    mv $destRep $destRep.old 
    +    (($?)) && print -- "Failed to rename (try n°$essai)" >&2
         ((essai++))
    -done
    +done
     
    -print -- "  renaming folder tmp (new) to the standard one"
    -print -P -- "  %BThe WebSite isn't working%b $(date)"
    +print -- "  renaming folder tmp (new) to the standard one"
    +print -P -- "  %BThe WebSite isn't working%b $(date)"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.tmp $destRep
    -    (($?)) && print -P -- "%B[WebSite not working]%b(try n°$essai) Failed to rename (mv $destRep.tmp $destRep)" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.tmp $destRep
    +    (($?)) && print -P -- "%B[WebSite not working]%b(try n°$essai) Failed to rename (mv $destRep.tmp $destRep)" >&2
         ((essai++))
    -done
    +done
     
    -print -P -- "\t===\t%BWEBSITE SHOULD WORK NOW%b\t==="
    +print -P -- "\t===\t%BWEBSITE SHOULD WORK NOW%b\t==="
     
    -print -- "  rename old folder to tmp folder"
    +print -- "  rename old folder to tmp folder"
     essai=1
    -while [[ ! -e $destRep ]]; do
    -    mv $destRep.old $destRep.tmp
    -    (($?)) && print -P -- "Failed to rename n°$essai" >&2
    +while [[ ! -e $destRep ]]; do
    +    mv $destRep.old $destRep.tmp
    +    (($?)) && print -P -- "Failed to rename n°$essai" >&2
         ((essai++))
    -done
    +done
    +
    +print -P -- "  Publish terminated"
    +
    + -print -P -- " Publish terminated" -
    -
    diff --git a/output/Scratch/fr/blog/11_Load_Disqus_Asynchronously/index.html b/output/Scratch/fr/blog/11_Load_Disqus_Asynchronously/index.html index 467f3f57a..4f2ba5f16 100644 --- a/output/Scratch/fr/blog/11_Load_Disqus_Asynchronously/index.html +++ b/output/Scratch/fr/blog/11_Load_Disqus_Asynchronously/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@

    In fact this method works for old threads. But it fails to create new post threads. This is why I tried and be conquered by intensedebate, as you can see in the bottom of this page.

    -

    Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).

    +

    Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).

    @@ -72,7 +75,7 @@

    I also know there is a jQuery plugin to make just that. Unfortunately I had some issue with CSS.

    -

    Now let’s begin.

    +

    Now let’s begin.

    @@ -85,7 +88,7 @@

    Why should I want to load the disqus javascript asynchronously?

      -
    • Efficiency: I don’t want my page to wait the complete execution of disqus script to load.
    • +
    • Efficiency: I don’t want my page to wait the complete execution of disqus script to load.
    • More independance: when disqus is down, my page is blocked!
    @@ -97,27 +100,33 @@

    How?

    -

    I give a solution with jQuery, but I’m certain it will work with many other js library.

    +

    I give a solution with jQuery, but I’m certain it will work with many other js library.

    Javascript

    replace:

    -
    -<script type="text/javascript" src="http://disqus.com/forums/YOUR_DISQUS_ID/embed.js"></script>
    -
    + + +
    <script type="text/javascript" src="http://disqus.com/forums/YOUR_DISQUS_ID/embed.js"></script>
    +
    + +

    by

    -
    -window.disqus_no_style=true;
    -$(document).ready(function(){
    -    $.getScript("http://disqus.com/forums/YOUR_DISQUS_ID/embed.js");
    +
    +
    +
    window.disqus_no_style=true;
    +$(document).ready(function(){
    +    $.getScript("http://disqus.com/forums/YOUR_DISQUS_ID/embed.js");
     });
    -
    +
    + +

    If you forget the window.disqus_no_style=true; then your page will be blank. Simply because without this option, the javascript use a document.write action after the document was closed, which cause a complete erasing of it.

    @@ -127,7 +136,7 @@

    But with this option you still need to provide a CSS. This is why you have to copy the css code from the embed.js file and rewrite it in a CSS file. You can download the CSS I obtained.


    -

    Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.

    +

    Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.

    diff --git a/output/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html b/output/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html index c60672c3c..f723d10cc 100644 --- a/output/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html +++ b/output/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,40 +59,46 @@

    Disqus vs. Intense Debate

    -

    J’ai écrit un article sur la façon dont j’ai essayé d’intégrer Disqus. Mon problème majeur avec Disqus c’était que ma page ne s’affichait pas correctement tant que les commentaire n’avait pas fini de s’afficher. Ça m’est arrivé plusieurs fois d’avoir ma page complètement bloquée parce que les serveurs de Disqus ne répondait pas. -C’est pourquoi j’ai essayer de l’inclure de manière asynchrone. Cependant j’ai eu des difficultés pour le faire fonctionner correctement.

    +

    J’ai écrit un article sur la façon dont j’ai essayé d’intégrer Disqus. Mon problème majeur avec Disqus c’était que ma page ne s’affichait pas correctement tant que les commentaire n’avait pas fini de s’afficher. Ça m’est arrivé plusieurs fois d’avoir ma page complètement bloquée parce que les serveurs de Disqus ne répondait pas. +C’est pourquoi j’ai essayer de l’inclure de manière asynchrone. Cependant j’ai eu des difficultés pour le faire fonctionner correctement.

    -

    De plus il n’a pas été trivial de faire en sorte que les commentaires soient commun à plusieurs pages différentes (chaque page à trois représentations différentes, une par language plus une version multi-langue).

    +

    De plus il n’a pas été trivial de faire en sorte que les commentaires soient commun à plusieurs pages différentes (chaque page à trois représentations différentes, une par language plus une version multi-langue).

    -

    Je dois reconnaître que je suis un peu triste de quitter Disqus parce que pour chacun de mes problèmes giannii m’a aidé du mieux qu’il a pu. Cependant les problèmes que j’ai eu étaient inhérents à des choix de conceptions plus que de simples petits problèmes techniques.

    +

    Je dois reconnaître que je suis un peu triste de quitter Disqus parce que pour chacun de mes problèmes giannii m’a aidé du mieux qu’il a pu. Cependant les problèmes que j’ai eu étaient inhérents à des choix de conceptions plus que de simples petits problèmes techniques.

    -

    Lorsque j’ai commencé à intégrer Disqus je n’ai jamais essayé Intense Debate. Maintenant que j’ai essayé je doit dire que je suis conquis. Il correspond exactement à ce que j’espérais de ce type de service.

    +

    Lorsque j’ai commencé à intégrer Disqus je n’ai jamais essayé Intense Debate. Maintenant que j’ai essayé je doit dire que je suis conquis. Il correspond exactement à ce que j’espérais de ce type de service.

    Pour le rendre complètement asynchrone il suffit de récupérer leur js commun et de remplacer la ligne suivante :

    -
    -document.getElementsByTagName("head")[0].appendChild(commentScript);
    -
    + + +
    document.getElementsByTagName("head")[0].appendChild(commentScript);
    +
    + +

    par (si vous utilisez jQuery) :

    -
    -$(document).ready( function() {
    -    document.getElementsByTagName("head")[0].appendChild(commentScript);
    +
    +
    +
    $(document).ready( function() {
    +    document.getElementsByTagName("head")[0].appendChild(commentScript);
     });
    -
    +
    + +

    And the Winner is: Intense Debate

    -

    Pour conclure les avantages majeurs (pour moi) d’Intense Debate par rapport à Disqus:

    +

    Pour conclure les avantages majeurs (pour moi) d’Intense Debate par rapport à Disqus:

    • Se charge de façon asynchrone ; ne bloque pas mon site web
    • -
    • Permet d’ajouter sans rien de plus des boutons comme “share to any” et les charge eux aussi de façon asynchrone.
    • +
    • Permet d’ajouter sans rien de plus des boutons comme “share to any” et les charge eux aussi de façon asynchrone.

    Voilà.

    diff --git a/output/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/index.html b/output/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/index.html index cb55c7ed5..2aac9b79a 100644 --- a/output/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/index.html +++ b/output/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,7 +61,7 @@ All my site is static and pages are generated with nanoc. It is (in my humble opinion) the modern geek way to make a website.

    -

    This is why I’ll give only a Ruby Generator, not a full javascript generator. +

    This is why I’ll give only a Ruby Generator, not a full javascript generator. But you can easily translate from Ruby to Javascript.

    Here is what you should obtain:

    @@ -78,7 +81,7 @@ But you can easily translate from Ruby to Javascript.

    $('.tag.selected').removeClass('selected'); $('#tag_'+id).addClass('selected'); } -

    analyser

      +

      analyser

      expressions régulières

        +

      expressions régulières

      mathématiques

        +

      mathématiques

      Réalité alternée

        +

      Réalité alternée

      securité

        +

      securité

      théorie

        +

      théorie

      • 24 @@ -2252,15 +2255,18 @@ But you can easily translate from Ruby to Javascript.

        Here is the simple jQuery code:

        -
        -    $(document).ready( function(){$('.list').hide();} );
        -    function tagSelected(id) {
        -        $('.list').hide();
        -        $('#'+id).fadeIn();
        -        $('.tag.selected').removeClass('selected');
        -        $('#tag_'+id).addClass('selected');
        +
        +
        +
            $(document).ready( function(){$('.list').hide();} );
        +    function tagSelected(id) {
        +        $('.list').hide();
        +        $('#'+id).fadeIn();
        +        $('.tag.selected').removeClass('selected');
        +        $('#tag_'+id).addClass('selected');
             }
        -
        +
        + +

        This code will hide all the div containing links to articles containing the tag. And create a function do show the div containing the tag.

        @@ -2268,28 +2274,34 @@ But you can easily translate from Ruby to Javascript.

        For each tag I create a span element:

        -
        -    <span   style="font-size: 1.0em;" 
        -            class="tag" 
        -            onClick="tagSelected('[TAG]')" 
        -            id="tag_[TAG]">
        +
        +
        +
            <span   style="font-size: 1.0em;" 
        +            class="tag" 
        +            onClick="tagSelected('[TAG]')" 
        +            id="tag_[TAG]">
                 [TAG]
        -    </span> 
        -
        + </span> +
        + +

        and a div containing links associtated to this tag:

        -
        -    <div id="[TAG]">
        -        <h4>[TAG]</h4>
        -        <ul>
        -            <li> LINK 1 </li>
        -            <li> LINK 2 </li>
        -        </ul>
        -    </div> 
        -
        + + +
            <div id="[TAG]">
        +        <h4>[TAG]</h4>
        +        <ul>
        +            <li> LINK 1 </li>
        +            <li> LINK 2 </li>
        +        </ul>
        +    </div> 
        +
        + +
        @@ -2302,179 +2314,197 @@ But you can easily translate from Ruby to Javascript.

        Here is how I generate this using nanoc 2.

        -

        If you want to make it fully jQuery one, it shouldn’t be +

        If you want to make it fully jQuery one, it shouldn’t be too difficult, to use my ruby code and translate it into javascript.

        In a first time tags correpond of the list of all tags.

        -
        -def tags
        -    return @page.tags.join(', ')
        -end
        -
        + + +
        def tags
        +    return @page.tags.join(', ')
        +end
        +
        + +

        A function to create a data structure associating to each tag its occurence.

        -
        -# generate an hash tag => number of occurence of tag
        -def tagNumber
        -    tags={}
        -    @pages.each do |p|
        -        if p.tags.nil?
        -            next
        -        end
        -        p.tags.each do |t|
        -            if tags[t]
        -                tags[t]+=1
        -            else
        -                tags[t]=1
        -            end
        -        end
        -    end
        -    return tags
        -end
        -
        + + +
        # generate an hash tag => number of occurence of tag
        +def tagNumber
        +    tags={}
        +    @pages.each do |p|
        +        if p.tags.nil?
        +            next
        +        end
        +        p.tags.each do |t|
        +            if tags[t]
        +                tags[t]+=1
        +            else
        +                tags[t]=1
        +            end
        +        end
        +    end
        +    return tags
        +end
        +
        + +

        I also need a data structure who associate to each tag a list of pages (at least url and title).

        -
        -# generate an hash tag => [ page1, page2 ... ]
        -def tagRefs
        -    tagLinks={}
        -    @pages.each do |p|
        -        if p.tags.nil?
        -            next
        -        end
        -        p.tags.each do |t|
        -            if tagLinks[t].nil?
        -                tagLinks[t]=[ p ]
        -            else
        -                tagLinks[t] <<= p
        -            end
        -        end
        -    end
        -    return tagLinks
        -end
        -
        + + +
        # generate an hash tag => [ page1, page2 ... ]
        +def tagRefs
        +    tagLinks={}
        +    @pages.each do |p|
        +        if p.tags.nil?
        +            next
        +        end
        +        p.tags.each do |t|
        +            if tagLinks[t].nil?
        +                tagLinks[t]=[ p ]
        +            else
        +                tagLinks[t] <<= p
        +            end
        +        end
        +    end
        +    return tagLinks
        +end
        +
        + +

        Calculate the real size of each tag to be displayed.

        I choosen not to use the full range of size for all the tag. Because if no -tag has more than n (here 10) occurences, then it doesn’t deserve to be +tag has more than n (here 10) occurences, then it doesn’t deserve to be of the maximal size.

        -
        -def tagRealSize
        -    tags=tagNumber
        -    max=tags.values.max
        -    min=tags.values.min
        -    # size in CSS em.
        -    minSize=1.0
        -    maxSize=2.5
        -    tagSize={}
        -    tags.each do |t,n|
        -        if ( max == min )
        -            tagSize[t]=minSize
        -        else
        -            # normalized value between 0 and 1
        -            # if not tag appear more than 10 times, 
        -            # then it cannot have the maximal size
        -            tagSize[t]=[ ( n - min + 0.0 ) / ( max - min ) , 
        -                         (n - min) / 10.0 ].min
        -            # from normalized size to real size
        -            tagSize[t]=( tagSize[t] ) * (maxSize - minSize) + minSize
        -        end
        -    end
        -    return tagSize
        -end
        -
        + + +
        def tagRealSize
        +    tags=tagNumber
        +    max=tags.values.max
        +    min=tags.values.min
        +    # size in CSS em.
        +    minSize=1.0
        +    maxSize=2.5
        +    tagSize={}
        +    tags.each do |t,n|
        +        if ( max == min )
        +            tagSize[t]=minSize
        +        else
        +            # normalized value between 0 and 1
        +            # if not tag appear more than 10 times, 
        +            # then it cannot have the maximal size
        +            tagSize[t]=[ ( n - min + 0.0 ) / ( max - min ) , 
        +                         (n - min) / 10.0 ].min
        +            # from normalized size to real size
        +            tagSize[t]=( tagSize[t] ) * (maxSize - minSize) + minSize
        +        end
        +    end
        +    return tagSize
        +end
        +
        + +

        Finaly a function to generate the XHTML/jQuery code

        -
        -# generate an XHTML/jQuery code for tag cloud
        -def tagCloud
        -    tagLinks=tagRefs
        -    tagSize=tagRealSize
         
        -    # begin to write the code
        -    tagCloud=%{<script type="text/javascript">
        -        $(document).ready( function(){$('.list').hide();} );
        -        function tagSelected(id) {
        -            $('.list').hide();
        -            $('#'+id).fadeIn();
        -            $('.tag.selected').removeClass('selected');
        -            $('#tag_'+id).addClass('selected');
        -        }
        -    </script><div id="tagcloud">}
        -    # Creation of the tags <span>
        -    tagSize.sort{|a,b| a[0].downcase <=> b[0].downcase}.each do |t,s|
        -        tag_in_id=t.gsub(/\W/,'_')
        -        # HTML protected version of the tag
        -        # for example, replace ' ' by '&nbsp;'
        -        protected=t.gsub(/&/,'&amp;').gsub(/ /,'&nbsp;').gsub(/</,'&lt;').gsub(/>/,'&gt;')
        -        tagCloud <<= %{
        -            <span style="font-size: #{s}em;" 
        -                  class="tag" 
        -                  onClick="tagSelected('#{tag_in_id}')" 
        -                  id="tag_#{tag_in_id}">
        -                #{protected}
        -            </span> }
        -    end
        -    tagCloud <<= %{</div><div id="hiddenDivs" >}
        -    # Creation of the divs containing links associated to a tag.
        -    tagLinks.each do |t,l|
        -        tag_in_id=t.gsub(/\W/,'_')
        -        tagCloud <<= %{
        -            <div id="#{tag_in_id}" class="list">
        -                <h4>#{t}</h4><ul>}
        -        # generate the link list
        -        l.each do |p|
        -            tagCloud <<= %{<li><a href="#{p.path}">#{p.title}</a></li>}
        -        end
        -        tagCloud <<= %{</ul></div>}
        -    end
        -    tagCloud <<= %{</div>}
        -    return tagCloud # yeah I know it is not necessary
        -end
        -
        + +
        # generate an XHTML/jQuery code for tag cloud
        +def tagCloud
        +    tagLinks=tagRefs
        +    tagSize=tagRealSize
        +
        +    # begin to write the code
        +    tagCloud=%{<script type="text/javascript">
        +        $(document).ready( function(){$('.list').hide();} );
        +        function tagSelected(id) {
        +            $('.list').hide();
        +            $('#'+id).fadeIn();
        +            $('.tag.selected').removeClass('selected');
        +            $('#tag_'+id).addClass('selected');
        +        }
        +    </script><div id="tagcloud">}
        +    # Creation of the tags <span>
        +    tagSize.sort{|a,b| a[0].downcase <=> b[0].downcase}.each do |t,s|
        +        tag_in_id=t.gsub(/\W/,'_')
        +        # HTML protected version of the tag
        +        # for example, replace ' ' by ' '
        +        protected=t.gsub(/&/,'&').gsub(/ /,' ').gsub(/</,'<').gsub(/>/,'>')
        +        tagCloud <<= %{
        +            <span style="font-size: #{s}em;" 
        +                  class="tag" 
        +                  onClick="tagSelected('#{tag_in_id}')" 
        +                  id="tag_#{tag_in_id}">
        +                #{protected}
        +            </span> }
        +    end
        +    tagCloud <<= %{</div><div id="hiddenDivs" >}
        +    # Creation of the divs containing links associated to a tag.
        +    tagLinks.each do |t,l|
        +        tag_in_id=t.gsub(/\W/,'_')
        +        tagCloud <<= %{
        +            <div id="#{tag_in_id}" class="list">
        +                <h4>#{t}</h4><ul>}
        +        # generate the link list
        +        l.each do |p|
        +            tagCloud <<= %{<li><a href="#{p.path}">#{p.title}</a></li>}
        +        end
        +        tagCloud <<= %{</ul></div>}
        +    end
        +    tagCloud <<= %{</div>}
        +    return tagCloud # yeah I know it is not necessary
        +end
        +
        + +
        -

        You can download the complete file to put in your ‘lib’ directory. Beware, it is a nanoc 2 version, you’ll have to make some small changes like replace @pages by @items to be nanoc3 compatible.

        +

        You can download the complete file to put in your ‘lib’ directory. Beware, it is a nanoc 2 version, you’ll have to make some small changes like replace @pages by @items to be nanoc3 compatible.

        Of course to be nice you need the associated CSS

        -
         
        +
        +
        
         // Change the color when mouse over
        -.tag:hover {
        -  color: #cc0000; }
        +.tag:hover {
        +  color: #cc0000; }
         
         // Change the color when tag selected
        -.tag.selected {
        -  color: #6c0000; }
        +.tag.selected {
        +  color: #6c0000; }
         
         // a bit of space and pointer cursor
        -.tag {
        -  cursor: pointer;
        -  margin-left: .5em;
        -  margin-right: .5em; }
        -
        +.tag { + cursor: pointer; + margin-left: .5em; + margin-right: .5em; } +
        + +
        -

        That’s all folks.

        +

        That’s all folks.

      diff --git a/output/Scratch/fr/blog/2009-09-replace-all-except-some-part/index.html b/output/Scratch/fr/blog/2009-09-replace-all-except-some-part/index.html index da9755a60..408248af5 100644 --- a/output/Scratch/fr/blog/2009-09-replace-all-except-some-part/index.html +++ b/output/Scratch/fr/blog/2009-09-replace-all-except-some-part/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,11 +59,12 @@

      My problem is simple:

      -

      I want to filter a text except some part of it. I can match easily the part I don’t want to be filtered. For example

      +

      I want to filter a text except some part of it. I can match easily the part I don’t want to be filtered. For example

      -
      -...
      +
      +
      +
      ...
       text
       ...
       BEGIN not to filter
      @@ -71,66 +75,77 @@ END not to filter
       ...
       text
       ...
      -
      +
      + +

      I searched a better way to do that, but the best I can do is using split and scan.

      -
      -def allExceptCode( f, content )
      -    # Beware the behaviour will change if you add
      -    # parenthesis (groups) to the regexp!
      -    regexp=/<code[^>]*>.*?<\/code>|<pre[^>]*>.*?<\/pre>/m
      -    tmp=""
      -    mem=[]
      -    content.scan(regexp).each do |c|
      -        mem <<= c
      -    end
      -    i=0
      -    content.split(regexp).each do |x|
      -        tmp <<= send(f,x) 
      -        if not mem[i].nil? 
      -            tmp <<= mem[i]
      -            i+=1
      -        end
      -    end
      +
      +
      +
      def allExceptCode( f, content )
      +    # Beware the behaviour will change if you add
      +    # parenthesis (groups) to the regexp!
      +    regexp=/<code[^>]*>.*?<\/code>|<pre[^>]*>.*?<\/pre>/m
      +    tmp=""
      +    mem=[]
      +    content.scan(regexp).each do |c|
      +        mem <<= c
      +    end
      +    i=0
      +    content.split(regexp).each do |x|
      +        tmp <<= send(f,x) 
      +        if not mem[i].nil? 
      +            tmp <<= mem[i]
      +            i+=1
      +        end
      +    end
           tmp
      -end
      -
      +end +
      + +

      An usage is:

      -
      -def filter(content)
      -    content.gsub(/e/,'X')
      -end
      +
      +
      +
      def filter(content)
      +    content.gsub(/e/,'X')
      +end
       ...
      -allExceptCode(:filter, content)
      +allExceptCode(:filter, content)
       ...
      -
      +
      + +

      A better syntax would be:

      -
      -# !!!!!!!!!! THIS SYNTAX DOES NOT WORK !!!!!!! #
      -def allExceptCode( f, content )
      -    regexp=/<code[^>]*>.*?<\/code>/m
      -    tmp=""
      -    content.split(regexp).each do |x|
      -        separator=$&
      -        tmp <<= send(f,x) 
      -        if not separator.nil?
      -            tmp <<= separator
      -        end
      -    end
      +
      +
      +
      # !!!!!!!!!! THIS SYNTAX DOES NOT WORK !!!!!!! #
      +def allExceptCode( f, content )
      +    regexp=/<code[^>]*>.*?<\/code>/m
      +    tmp=""
      +    content.split(regexp).each do |x|
      +        separator=$&
      +        tmp <<= send(f,x) 
      +        if not separator.nil?
      +            tmp <<= separator
      +        end
      +    end
           tmp
      -end
      -
      +end +
      + +

      I would expect the split make a search on a regular expression and then give the matched expression into the $& variable. But it is not the case.

      diff --git a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish index b37f9d12b..679e9caa1 100644 --- a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish +++ b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/publish @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Script synchronisant le site sur me.com diff --git a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework index f83af7601..4ec7888af 100644 --- a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework +++ b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/code/webdav-framework @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh function samelineprint { diff --git a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html index f61153fc4..8c5de0b5d 100644 --- a/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html +++ b/output/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,269 +57,279 @@
      -

      J’ai déjà discuté de la façon dont je synchronise mon site web sur mobileme. J’ai amélioré mon script pour le rendre incrémental.

      +

      J’ai déjà discuté de la façon dont je synchronise mon site web sur mobileme. J’ai amélioré mon script pour le rendre incrémental.

      -

      Voici mon script, il créé tout d’abord un fichier qui contient la liste des fichiers avec leur hash. Afin de les comparer avec ceux qui sont en ligne sans avoir à les parcourir. Ensuite pour chaque fichier qui semble différent, je met à jour le contenu.

      +

      Voici mon script, il créé tout d’abord un fichier qui contient la liste des fichiers avec leur hash. Afin de les comparer avec ceux qui sont en ligne sans avoir à les parcourir. Ensuite pour chaque fichier qui semble différent, je met à jour le contenu.

      -

      Cependant même avec ce script j’ai encore des problèmes. Dû à webdav. En particulier le renommage de répertoire. Par exemple :

      +

      Cependant même avec ce script j’ai encore des problèmes. Dû à webdav. En particulier le renommage de répertoire. Par exemple :

      -
      -mv folder folder2
      -
      +
      + +
      mv folder folder2
      +
      + +

      Retourne OK et pourtant :

      -
      -$ ls 
      +
      + +
      $ ls 
       folder folder2
      -
      +
      -

      Bouuhh…

      +
      -

      Pour résoudre ce type de problèmes j’utilise un framework en zsh. Il résout presque tous les problèmes liés à webdav à l’exception du renommage de répertoire.

      +

      Bouuhh…

      -
      -
      -#!/usr/bin/env zsh
      +

      Pour résoudre ce type de problèmes j’utilise un framework en zsh. Il résout presque tous les problèmes liés à webdav à l’exception du renommage de répertoire.

      + +
      + + +
      #!/usr/bin/env zsh
       
       function samelineprint {
      -    print -n -P -- "\r$*"
      +    print -n -P -- "\r$*"
       }
       
      -# avec 1 essai par seconde: 300 = 5 minutes
      +# avec 1 essai par seconde: 300 = 5 minutes
       maxessais=300
       
      -# try to create a directory until success
      +# try to create a directory until success
       function trymkdir {
      -    target="$1"
      -    print -- mkdir -p $target
      +    target="$1"
      +    print -- mkdir -p $target
           local essai=1
      -    while ! mkdir -p $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    while ! mkdir -p $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to copy until success
      +# try to copy until success
       function trycp {
      -    element="$1"
      -    target="$2"
      -    if [[ ! -d ${target:h} ]]; then
      -        trymkdir ${target:h}
      -    fi
      +    element="$1"
      +    target="$2"
      +    if [[ ! -d ${target:h} ]]; then
      +        trymkdir ${target:h}
      +    fi
           local essai=1
      -    print -- cp $element $target
      -    while ! \cp $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    print -- cp $element $target
      +    while ! \cp $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to remove until success
      +# try to remove until success
       function tryrm {
      -    target="$1"
      +    target="$1"
           local essai=1
      -    local options=''
      -    [[ -d $target ]] && options='-rf'
      -    print -- rm $options $target
      -    while ! rm $options $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    local options=''
      +    [[ -d $target ]] && options='-rf'
      +    print -- rm $options $target
      +    while ! rm $options $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "rm reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "rm reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to rename until success
      +# try to rename until success
       function tryrename {
      -    element="$1"
      -    target="$2"
      +    element="$1"
      +    target="$2"
           local essai=1
      -    while [[ -e $target ]]; do
      -        samelineprint "Echec n°$essai le fichier $target existe déjà"
      +    while [[ -e $target ]]; do
      +        samelineprint "Echec n°$essai le fichier $target existe déjà"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      +        ((essai>maxessais)) && exit 5
               sleep 1
      -    done
      -    print -- mv $element $target
      -    while ! mv $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    done
      +    print -- mv $element $target
      +    while ! mv $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 4
      -    done
      +        ((essai>maxessais)) && exit 4
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "mv reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "mv reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
       
      -# try to move until success
      +# try to move until success
       function trymv {
      -    element="$1"
      -    target="$2"
      +    element="$1"
      +    target="$2"
           local essai=1
      -    print -- mv $element $target
      -    while ! mv $element $target; do
      -        samelineprint "Echec: essai n°$essai"
      +    print -- mv $element $target
      +    while ! mv $element $target; do
      +        samelineprint "Echec: essai n°$essai"
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           essai=1
      -    while [[ -e $element ]]; do
      -        samelineprint "mv reussi mais fichier source non disparu n°$essai"
      +    while [[ -e $element ]]; do
      +        samelineprint "mv reussi mais fichier source non disparu n°$essai"
               sleep 1
               ((essai++))
      -        ((essai>maxessais)) && exit 5
      -    done
      +        ((essai>maxessais)) && exit 5
      +    done
           print
       }
      -
      -
      + -

      Et voici le code qui me permet de synchroniser mon site web. Il y a une partie un peu incompréhensible. C’est pour enlever les mail réencodés par le filtre bluecloth qui est une implémentation de markdown. Mes mails, sont encodés à chaque fois de façon différente à chaque réengendrement de page html. C’est pourquoi je les enlève pour ne pas les uploadés inutilement à chaque fois.

      +
      -
      -
      -#!/usr/bin/env zsh
      +

      Et voici le code qui me permet de synchroniser mon site web. Il y a une partie un peu incompréhensible. C’est pour enlever les mail réencodés par le filtre bluecloth qui est une implémentation de markdown. Mes mails, sont encodés à chaque fois de façon différente à chaque réengendrement de page html. C’est pourquoi je les enlève pour ne pas les uploadés inutilement à chaque fois.

      -# Script synchronisant le site sur me.com -# normalement, le site est indisponible le moins de temps possible -# le temps de deux renommages de répertoire +
      -# get configuration -# mostly directories -source $0:h/config -# get trycp function (copy until success) -source $0:h/webdav-framework +
      #!/usr/bin/env zsh
       
      -if [[ $1 == '-h' ]]; then
      -    print -- "usage : $0:h [-h|-s|-d]"
      -    print -- "  -a sychronise aussi l'index"
      -    print -- "  -h affiche l'aide"
      -    print -- "  -d modification directe (pas de swap)"
      -    print -- "  -s swappe simplement les répertoires"
      -fi
      +# Script synchronisant le site sur me.com
      +# normalement, le site est indisponible le moins de temps possible
      +# le temps de deux renommages de répertoire
       
      -# publication incrementale
      +# get configuration
      +# mostly directories
      +source $0:h/config
      +
      +# get trycp function (copy until success)
      +source $0:h/webdav-framework
      +
      +if [[ $1 == '-h' ]]; then
      +    print -- "usage : $0:h [-h|-s|-d]"
      +    print -- "  -a sychronise aussi l'index"
      +    print -- "  -h affiche l'aide"
      +    print -- "  -d modification directe (pas de swap)"
      +    print -- "  -s swappe simplement les répertoires"
      +fi
      +
      +# publication incrementale
       function incrementalPublish {
      -    local ydestRep=$destRep$suffix
      -    localRef="$srcRep/map.yrf"
      -    print -- "Creation du fichier de references"
      -    create-reference-file.sh > $localRef
      -    remoteRef="/tmp/remoteSiteMapRef.$$.yrf"
      -    if [[ ! -e "$ydestRep/map.yrf" ]]; then
      -        # pas de fichier de reference sur la cible
      -        print -- "pas de fichier de reference sur la cible, passage en mode rsync"
      +    local ydestRep=$destRep$suffix
      +    localRef="$srcRep/map.yrf"
      +    print -- "Creation du fichier de references"
      +    create-reference-file.sh > $localRef
      +    remoteRef="/tmp/remoteSiteMapRef.$$.yrf"
      +    if [[ ! -e "$ydestRep/map.yrf" ]]; then
      +        # pas de fichier de reference sur la cible
      +        print -- "pas de fichier de reference sur la cible, passage en mode rsync"
               rsyncPublish
               swap
      -    else
      -        trycp "$ydestRep/map.yrf" "$remoteRef"
      +    else
      +        trycp "$ydestRep/map.yrf" "$remoteRef"
               typeset -U filesToUpdate
      -        filesToUpdate=( $(diff $localRef $remoteRef | awk '/^[<>]/ {print $2}' ) )
      -        if ((${#filesToUpdate} == 1)); then
      -            print -- "Seul le fichier ${filesToUpdate} sera téléversé"
      -        elif ((${#filesToUpdate}<10)); then
      -            print -- "${#filesToUpdate} fichiers seront téléversés :"
      -            print -- "${filesToUpdate}"
      -        else
      -            print -- "${#filesToUpdate} fichiers seront téléversés"
      -        fi
      -        # copy all file with some differences
      -        # except the map in case of error
      -        for element in $filesToUpdate; do
      -            if [[ $element == "/map.yrf" ]]; then
      -                continue
      -            fi
      -            if [[ -e $srcRep$element ]]; then
      -                trycp $srcRep$element $ydestRep$element
      -            else
      -                tryrm $ydestRep$element
      -            fi
      -        done
      -        # if all went fine, copy the map file
      -        trycp $srcRep/map.yrf $ydestRep/map.yrf
      -        # remove the temporary file
      -        \rm $remoteRef
      -        # if we have used the tmp directory we swap
      -        if [[ "$suffix" != "" ]]; then
      +        filesToUpdate=( $(diff $localRef $remoteRef | awk '/^[<>]/ {print $2}' ) )
      +        if ((${#filesToUpdate} == 1)); then
      +            print -- "Seul le fichier ${filesToUpdate} sera téléversé"
      +        elif ((${#filesToUpdate}<10)); then
      +            print -- "${#filesToUpdate} fichiers seront téléversés :"
      +            print -- "${filesToUpdate}"
      +        else
      +            print -- "${#filesToUpdate} fichiers seront téléversés"
      +        fi
      +        # copy all file with some differences
      +        # except the map in case of error
      +        for element in $filesToUpdate; do
      +            if [[ $element == "/map.yrf" ]]; then
      +                continue
      +            fi
      +            if [[ -e $srcRep$element ]]; then
      +                trycp $srcRep$element $ydestRep$element
      +            else
      +                tryrm $ydestRep$element
      +            fi
      +        done
      +        # if all went fine, copy the map file
      +        trycp $srcRep/map.yrf $ydestRep/map.yrf
      +        # remove the temporary file
      +        \rm $remoteRef
      +        # if we have used the tmp directory we swap
      +        if [[ "$suffix" != "" ]]; then
                   swap
      -        fi
      -    fi
      +        fi
      +    fi
       }
       
      -# publication via rsync
      +# publication via rsync
       function rsyncPublish {
           result=1
           essai=1
      -    while (( $result > 0 )); do
      -        print -- rsync -arv $srcRep/ $destRep.tmp
      -        if ((!testmode)); then
      -            rsync -arv $srcRep/ $destRep.tmp
      -        fi
      -        result=$?
      -        if (( $result > 0 )); then
      -            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
      -        fi
      +    while (( $result > 0 )); do
      +        print -- rsync -arv $srcRep/ $destRep.tmp
      +        if ((!testmode)); then
      +            rsync -arv $srcRep/ $destRep.tmp
      +        fi
      +        result=$?
      +        if (( $result > 0 )); then
      +            print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2
      +        fi
               ((essai++))
      -    done
      +    done
       }
       
      -# swap
      +# swap
       function swap {
      -    print -P -- "%B[Directory Swap (tmp <=> target)]%b"
      -    [[ -e $destRep.old ]] && tryrm $destRep.old
      +    print -P -- "%B[Directory Swap (tmp <=> target)]%b"
      +    [[ -e $destRep.old ]] && tryrm $destRep.old
           
      -    print -- "  renommage du repertoire sandard vers le .old"
      -    tryrename $destRep $destRep.old 
      +    print -- "  renommage du repertoire sandard vers le .old"
      +    tryrename $destRep $destRep.old 
           
      -    print -- "  renommage du repertoire tmp (nouveau) vers le standard"
      -    print -P -- "%B[Site Indisponible]%b $(date)"
      -    tryrename $destRep.tmp $destRep
      -    print -P -- "%B[Site Disponible]%b $(date)"
      +    print -- "  renommage du repertoire tmp (nouveau) vers le standard"
      +    print -P -- "%B[Site Indisponible]%b $(date)"
      +    tryrename $destRep.tmp $destRep
      +    print -P -- "%B[Site Disponible]%b $(date)"
           
      -    print -- "  renommage du repertoire old vers le tmp"
      -    tryrename $destRep.old $destRep.tmp
      +    print -- "  renommage du repertoire old vers le tmp"
      +    tryrename $destRep.old $destRep.tmp
       
      -    print -P -- "  publication terminée"
      +    print -P -- "  publication terminée"
       }
       
      -print -- "Root = $webroot"
      -print -- "Dest = $destRep"
      +print -- "Root = $webroot"
      +print -- "Dest = $destRep"
       
      -if [[ "$1" = "-s" ]]; then
      +if [[ "$1" = "-s" ]]; then
           swap
      -else 
      -    print -P "Copie de l'init"
      -    \cp -f $webroot/Scratch/multi/index.html $webroot/index.html
      +else 
      +    print -P "Copie de l'init"
      +    \cp -f $webroot/Scratch/multi/index.html $webroot/index.html
       
      -    if [[ "$1" = "-d" ]]; then
      -        suffix=""
      -    else
      -        suffix=".tmp"
      -    fi
      -    print -P -- "%BSync%b[${Root:t} => ${destRep:t}$suffix]"
      +    if [[ "$1" = "-d" ]]; then
      +        suffix=""
      +    else
      +        suffix=".tmp"
      +    fi
      +    print -P -- "%BSync%b[${Root:t} => ${destRep:t}$suffix]"
           incrementalPublish
      -fi
      -
      -
      +fi + -

      C’est ma façon de remplacer rsync avec des filesystem qui ne permettent pas de l’utiliser. J’espère que ça pourra vous être utile. Je serai heureux de savoir si quelqu’un à une idée sur comment gérer le problème de renommage de répertoire avec webdav.

      +
      + +

      C’est ma façon de remplacer rsync avec des filesystem qui ne permettent pas de l’utiliser. J’espère que ça pourra vous être utile. Je serai heureux de savoir si quelqu’un à une idée sur comment gérer le problème de renommage de répertoire avec webdav.

      diff --git a/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js b/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js index 661daa2f2..6b047dbb2 100644 --- a/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js +++ b/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js @@ -1,4 +1,3 @@ - // Remove all CSS I don't want to use on IE $('link[rel=stylesheet]').each(function(i) { diff --git a/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/index.html b/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/index.html index 96a2f15bd..42fa83198 100644 --- a/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/index.html +++ b/output/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,38 +57,43 @@
      -

      Pour les développeur de site web Internet Explorer est un cauchemar. C’est pourquoi j’utilise un style complètement différent pour ce navigateur. Avec la librairie jQuery.

      +

      Pour les développeur de site web Internet Explorer est un cauchemar. C’est pourquoi j’utilise un style complètement différent pour ce navigateur. Avec la librairie jQuery.

      -
      -$(document).ready( function() {
      -    if ($.browser["msie"]) {
      -        // include the ie.js file
      -        $('head').append('<script type="text/javascript" src="/js/ie.js"></scr' + 'ipt>');
      +
      + +
      $(document).ready( function() {
      +    if ($.browser["msie"]) {
      +        // include the ie.js file
      +        $('head').append('<script type="text/javascript" src="/js/ie.js"></scr' + 'ipt>');
           }
       });
      -
      +
      -
      -
      -// Remove all CSS I don't want to use on IE
      -$('link[rel=stylesheet]').each(function(i)
      +
      + +
      + + +
      // Remove all CSS I don't want to use on IE
      +$('link[rel=stylesheet]').each(function(i)
       {
      -    if (this.getAttribute('href') == '/css/layout.css') 
      -        this.disabled = true;
      -    if (this.getAttribute('href') == '/css/shadows.css') 
      -        this.disabled = true;
      -    if (this.getAttribute('href') == '/css/gen.css')    
      -        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/layout.css') 
      +        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/shadows.css') 
      +        this.disabled = true;
      +    if (this.getAttribute('href') == '/css/gen.css')    
      +        this.disabled = true;
       }) ;
       
      -// Append the CSS for IE only
      -$('head').append('<link rel="stylesheet" type="text/css" href="/css/ie.css"/>');
      +// Append the CSS for IE only
      +$('head').append('<link rel="stylesheet" type="text/css" href="/css/ie.css"/>');
       
      -// I also add a message on top of the page
      -$('body').prepend('<div id="iemessage"><p><span class="fr"><em>Avec <a href="http://www.firefox.com"> Firefox </a> et <a href="http://www.apple.com/safari">Safari</a> cette page est bien plus jolie !</em></span><span class="en"><em>This page is far nicer with <a href="http://www.firefox.com"> Firefox </a> and <a href="http://www.apple.com/safari">Safari</a>!</em></span></p>.</div>');
      +// I also add a message on top of the page
      +$('body').prepend('<div id="iemessage"><p><span class="fr"><em>Avec <a href="http://www.firefox.com"> Firefox </a> et <a href="http://www.apple.com/safari">Safari</a> cette page est bien plus jolie !</em></span><span class="en"><em>This page is far nicer with <a href="http://www.firefox.com"> Firefox </a> and <a href="http://www.apple.com/safari">Safari</a>!</em></span></p>.</div>');
       
      -
      -
      + + +

      Voilà.

      diff --git a/output/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/index.html b/output/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/index.html index fac8c173a..aa49cce32 100644 --- a/output/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/index.html +++ b/output/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,10 +61,10 @@
      -

      Je crois que le but du minimalisme est de facilité le Focus c’est-à-dire la concentration sur le contenu. Je crois que le minimalisme doit être un moyen et pas une fin. Le Focus devrait être le but, et je pense que le minimalisme n’est pas obligatoire pour l’atteindre.

      +

      Je crois que le but du minimalisme est de facilité le Focus c’est-à-dire la concentration sur le contenu. Je crois que le minimalisme doit être un moyen et pas une fin. Le Focus devrait être le but, et je pense que le minimalisme n’est pas obligatoire pour l’atteindre.

      -

      C’est pourquoi mon design n’est pas minimaliste. Mais j’ai décidé d’enlever la majorité des objets servant à la navigation pour améliorer l’attention sur l’article. Peut-être que plus tard, je préfèrerai laisser le menu dans les pages normales du site pour ne le cacher que dans les articles de blog. -Pour l’instant je le cache partout.

      +

      C’est pourquoi mon design n’est pas minimaliste. Mais j’ai décidé d’enlever la majorité des objets servant à la navigation pour améliorer l’attention sur l’article. Peut-être que plus tard, je préfèrerai laisser le menu dans les pages normales du site pour ne le cacher que dans les articles de blog. +Pour l’instant je le cache partout.

      @@ -73,60 +76,69 @@ Pour l’instant je le cache partout.

      Pour ceux qui souhaitent connaître les détails techniques derrière le menu apparaissant/disparaissant, voici le code utilisant jQuery.

      -

      L’HTML :

      +

      L’HTML :

      -
      -<div id="menuButton"></div>
      -<div id="entete">#content of the menu</div>
      -
      + + +
      <div id="menuButton"></div>
      +<div id="entete">#content of the menu</div>
      +
      + +

      La CSS :

      -
      -#menuButton {
      -  font-size: 2em;
      -  height: 2em;
      -  line-height: 1.8em;
      -  width: 2em;
      -  position: fixed;
      -  left: 0;
      -  top: 0; 
      -  z-index: 9001 }
      +
      + +
      #menuButton {
      +  font-size: 2em;
      +  height: 2em;
      +  line-height: 1.8em;
      +  width: 2em;
      +  position: fixed;
      +  left: 0;
      +  top: 0; 
      +  z-index: 9001 }
      +
      +#menuButton:hover {
      +  cursor: pointer; }
      +
      +#entete {
      +  top: 5em;
      +  left: 0;
      +  position: fixed;
      +  width: 10em;
      +  z-index: 9000; }
      +
      -#menuButton:hover { - cursor: pointer; } -#entete { - top: 5em; - left: 0; - position: fixed; - width: 10em; - z-index: 9000; } -

      Le code javascript (utilisant jQuery)

      -
      -function hideMenu() {
      -    $('#entete').animate({left:"-10em"}, 500 );
      -    $('#menuButton').html('&rarr;');
      +
      +
      +
      function hideMenu() {
      +    $('#entete').animate({left:"-10em"}, 500 );
      +    $('#menuButton').html('→');
       }
      -function showMenu() {
      -    $('#entete').animate({left:"0em"}, 500 );
      -    $('#menuButton').html('&larr;');
      +function showMenu() {
      +    $('#entete').animate({left:"0em"}, 500 );
      +    $('#menuButton').html('←');
       }
      -function toggleMenu() {
      -    if ( $('#entete').css('left')=='-10em' ) {
      +function toggleMenu() {
      +    if ( $('#entete').css('left')=='-10em' ) {
               showMenu();
      -    } else {
      +    } else {
               hideMenu();
           }
       }
      -
      +
      + +

      Le résultat est visible dans le coin en haut à droite de cet article.

      diff --git a/output/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/index.html b/output/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/index.html index 3bd174411..7181cea79 100644 --- a/output/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/index.html +++ b/output/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -79,32 +82,36 @@
      -

      J’ai d’abord essayé d’intégrer queryLoader, mais il ne comblait pas mes besoins.

      +

      J’ai d’abord essayé d’intégrer queryLoader, mais il ne comblait pas mes besoins.

      -

      Ce plugin ajoutait un ‘div’ noir pour cacher le contenu du site. Cependant, comme le script doit être lancé à la fin du code source. Pendant un petit moment, on peut voir mon site en train de se mettre à jour.

      +

      Ce plugin ajoutait un ‘div’ noir pour cacher le contenu du site. Cependant, comme le script doit être lancé à la fin du code source. Pendant un petit moment, on peut voir mon site en train de se mettre à jour.

      -

      Pour cacher ce petit ‘artefact’, voici comment je m’y suis pris.

      +

      Pour cacher ce petit ‘artefact’, voici comment je m’y suis pris.

      Code

      -

      D’abort il faut ajouter tout en haut du body cette fois un div qui va être le voile noir qui va tout cacher.

      +

      D’abort il faut ajouter tout en haut du body cette fois un div qui va être le voile noir qui va tout cacher.

      -
      -...
      -<body>
      -<div id="blackpage">
      +
      +
      +
      ...
      +<body>
      +<div id="blackpage">
           content to display during the loading.
      -</div>
      +</div>
       ...
      -
      +
      + +

      et le CSS correspondant au div #blackpage :

      -
      -#blackpage
      +
      +
      +
      #blackpage
         top: 0 
         left: 0 
         width: 100%
      @@ -117,24 +124,29 @@
         text-align: center
         color: #666
         padding-top: 10em
      -  background-color: #eee
      +  background-color: #eee
         z-index: 9000
      -
      +
      + +

      ainsi que le code jQuery associé :

      -
      -$(document).ready(function(){
      -    $('#blackpage').fadeOut();
      +
      +
      +
      $(document).ready(function(){
      +    $('#blackpage').fadeOut();
       });
      -
      +
      + +
      -

      Oui, c’est aussi simple que ça. Maintenant ajouter le #blackpage tout en haut de ma page me permet d’être certain de tout cacher pendant le chargement de la page.

      +

      Oui, c’est aussi simple que ça. Maintenant ajouter le #blackpage tout en haut de ma page me permet d’être certain de tout cacher pendant le chargement de la page.

      -

      J’espère que ça a pu vous être utile !

      +

      J’espère que ça a pu vous être utile !

      diff --git a/output/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html b/output/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html index d675f5184..ae323a9eb 100644 --- a/output/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html +++ b/output/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,88 +57,97 @@
      -

      J’ai déjà dit pourquoi je préférais que mon menu de navigation soit caché. J’ai finalement décidé d’attendre un peu avant de cacher le menu. Juste le temps que l’utilisateur le voit. Mais voilà. Comment faire pour qu’il ne disparaisse que lorsque l’on ne s’en sert pas pendant un petit moment ?

      +

      J’ai déjà dit pourquoi je préférais que mon menu de navigation soit caché. J’ai finalement décidé d’attendre un peu avant de cacher le menu. Juste le temps que l’utilisateur le voit. Mais voilà. Comment faire pour qu’il ne disparaisse que lorsque l’on ne s’en sert pas pendant un petit moment ?

      -

      Voici la solution que j’utilise avec jQuery

      +

      Voici la solution que j’utilise avec jQuery

      HTML :

      -
      -    <div id="menuButton"></div>
      -    <div id="entete">
      -        <ul>
      -            <li> menu item 1 </li>
      +
      +
      +
          <div id="menuButton"></div>
      +    <div id="entete">
      +        <ul>
      +            <li> menu item 1 </li>
                   ...
      -            <li> menu item n </li>
      -        </ul>
      -    </div>
      -
      + <li> menu item n </li> + </ul> + </div> +
      + +

      CSS :

      -
      -    #entete {
      -      top: 1em;
      -      left: 0;
      -      position: fixed;
      -      width: 10em;
      -      z-index: 2000; }
      +
      + +
          #entete {
      +      top: 1em;
      +      left: 0;
      +      position: fixed;
      +      width: 10em;
      +      z-index: 2000; }
           
      -    #entete {
      -      top: 1em;
      -      height: 22em;
      -      left: 0;
      -      position: fixed;
      -      width: 10em; }
      -
      + #entete { + top: 1em; + height: 22em; + left: 0; + position: fixed; + width: 10em; } +
      + +

      Javascript :

      -
      -var last=0;
      +
      -// will hide the menu in 5 seconds -// if the variable 'last' has not changed its value -function autoHideMenu(value) { - setTimeout(function(){ - if ( last == value ) { hideMenu(); } - },5000); +
      var last=0;
      +
      +// will hide the menu in 5 seconds
      +// if the variable 'last' has not changed its value
      +function autoHideMenu(value) {
      +    setTimeout(function(){
      +        if ( last == value ) { hideMenu(); }
      +    },5000);
       }
       
      -$(document).ready( function() {
      -    // show the menu when the mouse is on
      -    // the good area
      -    $('#menuButton').hover(showMenu);
      +$(document).ready( function() {
      +    // show the menu when the mouse is on
      +    // the good area
      +    $('#menuButton').hover(showMenu);
       
      -    // If the mouse is on the menu change the
      -    // value of 'last'
      -    // try to hide the menu when the mouse 
      -    // go out off the menu.
      -    $('#entete').hover(
      -        function(){last+=1;}, 
      -        function(){autoHideMenu(last);} );
      -    autoHideMenu(0);
      +    // If the mouse is on the menu change the
      +    // value of 'last'
      +    // try to hide the menu when the mouse 
      +    // go out off the menu.
      +    $('#entete').hover(
      +        function(){last+=1;}, 
      +        function(){autoHideMenu(last);} );
      +    autoHideMenu(0);
       });
       
      -// show / hide menu functions details
      +// show / hide menu functions details
       
      -// move to the left
      -function hideMenu() { 
      -    $('#entete').animate({left:"-10em"}, 500 ); 
      +// move to the left
      +function hideMenu() { 
      +    $('#entete').animate({left:"-10em"}, 500 ); 
       }
       
      -// move to right and will try to hide in 5 sec.
      -function showMenu() { 
      -    $('#entete').animate({left:"0em"}, 500 );
      -    last+=1;
      +// move to right and will try to hide in 5 sec.
      +function showMenu() { 
      +    $('#entete').animate({left:"0em"}, 500 );
      +    last+=1;
           autoHideMenu(last);
       }
       
      -
      +
      -

      Simple et peu gourmand en ressources. Pas de timer (ou presque), pas de fuite de mémoire, pas d’utilisation de date…

      +
      + +

      Simple et peu gourmand en ressources. Pas de timer (ou presque), pas de fuite de mémoire, pas d’utilisation de date…

      diff --git a/output/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/index.html b/output/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/index.html index 9cf10c7a3..550b34286 100644 --- a/output/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/index.html +++ b/output/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,15 +59,18 @@

      Une petite astuce dont je ne me souvient jamais (je ne sais pas pourquoi).

      -

      Lorsque que vous souhaitez lancer une commande qui ne soit pas tuée après la fermeture du terminal voici comment s’y prendre :

      +

      Lorsque que vous souhaitez lancer une commande qui ne soit pas tuée après la fermeture du terminal voici comment s’y prendre :

      + +
      + +
      nohup cmd &
      +
      + -
      -nohup cmd &
      -
      cmd est la commande que vous souhaitez lancer.
      -

      Je laisse cette astuce ici pour moi et dans l’espoir que ça pourra aussi être utile à quelqu’un d’autre.

      +

      Je laisse cette astuce ici pour moi et dans l’espoir que ça pourra aussi être utile à quelqu’un d’autre.

      diff --git a/output/Scratch/fr/blog/2009-10-untaught-git-usage/index.html b/output/Scratch/fr/blog/2009-10-untaught-git-usage/index.html index bc17f151c..79d4a55e5 100644 --- a/output/Scratch/fr/blog/2009-10-untaught-git-usage/index.html +++ b/output/Scratch/fr/blog/2009-10-untaught-git-usage/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,9 +57,9 @@
      -

      Je décris pourquoi j’ai eu tant de mal à me faire à Git. Il y a en effet une partie “non dite” qui m’a bloqué pendant un bon moment. Jusqu’à ce que je découvre le bon document.

      +

      Je décris pourquoi j’ai eu tant de mal à me faire à Git. Il y a en effet une partie “non dite” qui m’a bloqué pendant un bon moment. Jusqu’à ce que je découvre le bon document.

      -

      Le fait est que les branches légères ne sont pas destinée à être des branches isolées. Ainsi, il y a un “workflow standard” qui s’il n’est pas suivi rend l’utilisation de Git inappropriée.

      +

      Le fait est que les branches légères ne sont pas destinée à être des branches isolées. Ainsi, il y a un “workflow standard” qui s’il n’est pas suivi rend l’utilisation de Git inappropriée.

      @@ -68,12 +71,12 @@

      De SVN à Bazaar

      -

      J’étais un fervent utilisateur de subversion (svn). Lorsqu’un jour, comme beaucoup de gens je découvris ce qu’en pensais Linus Torvald sur une vidéo. Ventant les mérites d’un système de versions concurrentes décentralisé.

      +

      J’étais un fervent utilisateur de subversion (svn). Lorsqu’un jour, comme beaucoup de gens je découvris ce qu’en pensais Linus Torvald sur une vidéo. Ventant les mérites d’un système de versions concurrentes décentralisé.

      -

      En effet une fois qu’on s’y intéresse un peu, on voit tous les avantages pratiques qu’apporteraient en théorie un tel système.

      +

      En effet une fois qu’on s’y intéresse un peu, on voit tous les avantages pratiques qu’apporteraient en théorie un tel système.

      -

      J’ai alors eu besoin d’un système de version dans mon équipe. -Ils n’étaient pas familier avec les systèmes de versions. À par ceux possédant une GUI, qui sont lourds et administrés par un responsable.

      +

      J’ai alors eu besoin d’un système de version dans mon équipe. +Ils n’étaient pas familier avec les systèmes de versions. À par ceux possédant une GUI, qui sont lourds et administrés par un responsable.

      Après quelques recherches trois choix se dessinent :

      @@ -83,29 +86,29 @@ Ils n’étaient pas familier avec les systèmes de versions. À par ceux po
    • Mercurial
    -

    En me renseignant un peu sur les forums et en essayant les trois, je me suis vite rendu compte que celui possédant l’interface utilisateur la plus simple était Bazaar*. Mon choix était fait.

    +

    En me renseignant un peu sur les forums et en essayant les trois, je me suis vite rendu compte que celui possédant l’interface utilisateur la plus simple était Bazaar*. Mon choix était fait.

    De bazaar à Git

    -

    Je me suis alors familiarisé avec Bazaar. Et je dois dire que c’était vraiment naturel en venant de subversion. La commande pull correspond au update, la commande push correspond au commit. Puis les commandes commit et update existent toujours si on en a besoin et qu’on veut utiliser un workflow identique à celui de subversion.

    +

    Je me suis alors familiarisé avec Bazaar. Et je dois dire que c’était vraiment naturel en venant de subversion. La commande pull correspond au update, la commande push correspond au commit. Puis les commandes commit et update existent toujours si on en a besoin et qu’on veut utiliser un workflow identique à celui de subversion.

    -

    Mais plus le temps passe et plus de partout sur les blog, c’est surtout Git qui a le vent en poupe.

    +

    Mais plus le temps passe et plus de partout sur les blog, c’est surtout Git qui a le vent en poupe.

    -

    Je décide alors d’utiliser Git en particulier pour versionner le site que vous êtes en train de lire. Sauf que je le trouve vraiment difficile d’utilisation et surtout complètement contre intuitif (j’y reviendrai plus tard).

    +

    Je décide alors d’utiliser Git en particulier pour versionner le site que vous êtes en train de lire. Sauf que je le trouve vraiment difficile d’utilisation et surtout complètement contre intuitif (j’y reviendrai plus tard).

    -

    Alors que j’essaye de trouver de l’aide et que je dis qu’il est plus difficile à utiliser que Bazaar, beaucoup me répliquent que c’est :

    +

    Alors que j’essaye de trouver de l’aide et que je dis qu’il est plus difficile à utiliser que Bazaar, beaucoup me répliquent que c’est :

    -

    Super-tellement-trop-simple que même ma fille de 12 ans qui n’y comprend rien en informatique l’utilise pour versionner ses documents. Elle s’en sert très facilement en créant des branches et tout et tout…

    +

    Super-tellement-trop-simple que même ma fille de 12 ans qui n’y comprend rien en informatique l’utilise pour versionner ses documents. Elle s’en sert très facilement en créant des branches et tout et tout…

    -

    Bon alors si une gamine 12 ans trouve ça très naturel et que moi (avec mon Doctorat en informatique) j’ai du mal à faire ce que je veux, c’est un peu frustrant et humiliant. Mais qu’est-ce qui fait que Git est naturel aux uns (comme pour CocoaSamurai ) et très confus pour moi ?

    +

    Bon alors si une gamine 12 ans trouve ça très naturel et que moi (avec mon Doctorat en informatique) j’ai du mal à faire ce que je veux, c’est un peu frustrant et humiliant. Mais qu’est-ce qui fait que Git est naturel aux uns (comme pour CocoaSamurai ) et très confus pour moi ?

    -

    C’est en lisant un article j’ai enfin compris ce qu’il me manquait. C’est la partie non dite de la conception. Celle que tous les développeurs et les concepteurs trouvaient comme aller de soi. Sauf que pour moi, ce n’était pas du tout le cas.

    +

    C’est en lisant un article j’ai enfin compris ce qu’il me manquait. C’est la partie non dite de la conception. Celle que tous les développeurs et les concepteurs trouvaient comme aller de soi. Sauf que pour moi, ce n’était pas du tout le cas.

    -

    - Je parle de ClearCase©. Et oui, je sais qu’il existe des commandes en lignes pour ClearCase©, mais ce n’est pas comme ça qu’ils étaient habitués à travailler avec des systèmes de “versionning”.

    +

    - Je parle de ClearCase©. Et oui, je sais qu’il existe des commandes en lignes pour ClearCase©, mais ce n’est pas comme ça qu’ils étaient habitués à travailler avec des systèmes de “versionning”.

    -

    * - Je n’ai pas vraiment donné sa chance à Mercurial, la terminologie qu’ils utilisaient était trop éloignée de celle de svn à laquelle je m’étais habituée.

    +

    * - Je n’ai pas vraiment donné sa chance à Mercurial, la terminologie qu’ils utilisaient était trop éloignée de celle de svn à laquelle je m’étais habituée.

    @@ -113,132 +116,159 @@ Ils n’étaient pas familier avec les systèmes de versions. À par ceux po
    -

    Lorsqu’on voit les présentations autour de la notion de branche et de dcvs, on s’imagine dans un monde où chaque branche est totalement isolée des autres sauf au moment de “merger” les différences les unes des autres. -Tout est magique. C’est la façon de voir “Mondes Parallèles”. Cette façon de voir est expliquée dans le très bon article sur les branches sur betterexplained.

    +

    Lorsqu’on voit les présentations autour de la notion de branche et de dcvs, on s’imagine dans un monde où chaque branche est totalement isolée des autres sauf au moment de “merger” les différences les unes des autres. +Tout est magique. C’est la façon de voir “Mondes Parallèles”. Cette façon de voir est expliquée dans le très bon article sur les branches sur betterexplained.

    Sauf que les concepteurs de Git (conçu pour le noyau Linux) ont plutôt imaginé un système basé non pas autour des mondes parallèles, mais sur la notion de Patch.

    -

    D’un côté Mondes Parallèles, de l’autre Patchs. Il y a beaucoup de notions équivalentes dans les deux cas, mais aussi quelques différences.

    +

    D’un côté Mondes Parallèles, de l’autre Patchs. Il y a beaucoup de notions équivalentes dans les deux cas, mais aussi quelques différences.

    • Bazaar est complètement basé sur la notion de Mondes Parallèles qui va impliquer un phénomène de Patch.
    • Alors que Git est basé sur la notion de Patch qui va impliquer la création de Mondes Parallèles.
    -

    Je ne vais pas argumenté pour savoir si une façon de voir est meilleure que l’autre. Disons simplement que ma façon d’entrer dans l’explication des DCVS était par le biais des Mondes Parallèles alors que Git est conçut selon l’autre notion.

    +

    Je ne vais pas argumenté pour savoir si une façon de voir est meilleure que l’autre. Disons simplement que ma façon d’entrer dans l’explication des DCVS était par le biais des Mondes Parallèles alors que Git est conçut selon l’autre notion.

    De la théorie à la pratique

    -

    Bien que je pense avoir bien compris les mécanismes conceptuels de Git, la mise en pratique posait problème. Et le point noir, celui qui m’empêchait de comprendre Git comme je le souhaitais était dû à la notion de branche légère.

    +

    Bien que je pense avoir bien compris les mécanismes conceptuels de Git, la mise en pratique posait problème. Et le point noir, celui qui m’empêchait de comprendre Git comme je le souhaitais était dû à la notion de branche légère.

    -

    Une branche légère qu’est-ce que c’est me demanderez-vous ? Si comme moi on vient de Bazaar, c’est une notion complètement nouvelle. Il s’agit simplement de la capacité de créer une nouvelle branche en réutilisant le répertoire dans lequel on se trouve.

    +

    Une branche légère qu’est-ce que c’est me demanderez-vous ? Si comme moi on vient de Bazaar, c’est une notion complètement nouvelle. Il s’agit simplement de la capacité de créer une nouvelle branche en réutilisant le répertoire dans lequel on se trouve.

    En pratique pour changer de branche, il faut lancer une commande. Tous les fichiers locaux non modifiés depuis le dernier commit seront alors modifiés pour correspondre à la version de la branche.

    -

    En théorie, les branches légères sont des branches tout comme avec bazaar. D’ailleurs le mot utilisé n’est pas branche légère mais branche tout court.

    +

    En théorie, les branches légères sont des branches tout comme avec bazaar. D’ailleurs le mot utilisé n’est pas branche légère mais branche tout court.

    -

    Sauf que contrairement à une branche standard résidant dans son propre répertoire, une branche légère est destinée à n’être qu’un patch de la branche principale du répertoire dans lequel elle réside.

    +

    Sauf que contrairement à une branche standard résidant dans son propre répertoire, une branche légère est destinée à n’être qu’un patch de la branche principale du répertoire dans lequel elle réside.

    -

    Bien entendu on pourra m’objecter que l’on peut tout à fait utiliser ces branches légères comme des branches normales. Mais elles n’ont pas été conçues pour ça. Et donc, en pratique, c’est gênant de les utiliser de la sorte.

    +

    Bien entendu on pourra m’objecter que l’on peut tout à fait utiliser ces branches légères comme des branches normales. Mais elles n’ont pas été conçues pour ça. Et donc, en pratique, c’est gênant de les utiliser de la sorte.

    Voici comment Git est censé être utilisé (pour plus de détails vous pouvez lire Git for Designers en anglais) :

      -
    • récupération ou création d’un “repository” central Le Grand Repository
    • -
    • Création d’un branche légère locale qui contient les différences qui vont devoir être “patché” dans LE GRAND REPOSITORY.
    • +
    • récupération ou création d’un “repository” central Le Grand Repository
    • +
    • Création d’un branche légère locale qui contient les différences qui vont devoir être “patché” dans LE GRAND REPOSITORY.
    -

    Voici comment n’est pas censé être utilisé Git :

    +

    Voici comment n’est pas censé être utilisé Git :

      -
    • Récupération ou création d’un “repository” quelconque
    • -
    • Création d’un branche légère locale qui n’a pas pour vocation de mettre à jour le “repository” d’origine, mais de vivre sa vie de façon autonome et de récupérer les mises à jour des autres du “repository” d’origine.
    • +
    • Récupération ou création d’un “repository” quelconque
    • +
    • Création d’un branche légère locale qui n’a pas pour vocation de mettre à jour le “repository” d’origine, mais de vivre sa vie de façon autonome et de récupérer les mises à jour des autres du “repository” d’origine.
    -

    En effet cette petite notion m’a empêché de fonctionner correctement.

    +

    En effet cette petite notion m’a empêché de fonctionner correctement.

    En pratique

    -

    Maintenant que j’ai compris ça, je peux enfin comprendre pourquoi Git a tant de défenseurs qui continue de trouver que Git est meilleur que les autres.

    +

    Maintenant que j’ai compris ça, je peux enfin comprendre pourquoi Git a tant de défenseurs qui continue de trouver que Git est meilleur que les autres.

    -

    La notion de branche légère est essentielle à Git et vraiment utile en pratique. Notamment pour la gestion de ce site. Par contre, elle m’empêche d’utiliser les branches comme je le souhaiterai.

    +

    La notion de branche légère est essentielle à Git et vraiment utile en pratique. Notamment pour la gestion de ce site. Par contre, elle m’empêche d’utiliser les branches comme je le souhaiterai.

    -

    Mais dans ce cas-là, je n’ai qu’à utiliser des clônes et pas des branches légères.

    +

    Mais dans ce cas-là, je n’ai qu’à utiliser des clônes et pas des branches légères.

    Des exemples

    Je trouve toujours que les terminologies de bazaar sont plus claires et plus concises.

    -
    bzr revert
    -
    +
    + +
    bzr revert
    + +

    est quand même plus clair que

    -
    git reset --hard HEAD
    -
    +
    + +
    git reset --hard HEAD
    + +

    De la même façon

    -
    bzr revert -r -3
    -
    +
    + +
    bzr revert -r -3
    + +

    je trouve ça mieux que

    -
    git reset --hard HEAD~3
    -
    +
    -

    Là ça va commencer à se compliquer. Si on veut revenir dans le temps sur toute l’arborescence, avec Git on utilise reset.

    +
    git reset --hard HEAD~3
    + +
    + +

    Là ça va commencer à se compliquer. Si on veut revenir dans le temps sur toute l’arborescence, avec Git on utilise reset.

    OK

    Maintenant si je veux revenir dans le temps sur un seul fichier. Naturellement on se dit :

    -
    git reset --hard FILE
    -
    +
    + +
    git reset --hard FILE
    + +
    **ET BIEN NON !**
    -

    La solution c’est :

    +

    La solution c’est :

    -
    git checkout FILE
    -
    +
    -

    Quoi ? checkout !? Bon, d’accord, j’accepte, pourquoi pas après tout ? -En plus quand on est habitué à Bazaar c’est :

    +
    git checkout FILE
    -
    git revert FILE
    -
    +
    + +

    Quoi ? checkout !? Bon, d’accord, j’accepte, pourquoi pas après tout ? +En plus quand on est habitué à Bazaar c’est :

    + +
    + +
    git revert FILE
    + +

    Ce que je trouve quand même bien plus naturel.

    -

    Mais là où ça devient vraiment difficile de s’y faire c’est pour changer de branche.
    +

    Mais là où ça devient vraiment difficile de s’y faire c’est pour changer de branche.
    Avec Bazaar ça donne :

    -
    cd ../branch
    -
    +
    -

    Bon ok, il faut changer de répertoire, un répertoire par branche. Ça consomme de l’espace disque mais au moins on voit où on est. Avec Git voilà comment on change de branche (branche légère) :

    +
    cd ../branch
    -
    git checkout branch
    -
    +
    -

    Alors là, on se dit “WTF?” ; en français : mais qu’est-ce que c’est que ça ? Je croyais que checkout c’était pour récupérer l’état d’un fichier ?

    +

    Bon ok, il faut changer de répertoire, un répertoire par branche. Ça consomme de l’espace disque mais au moins on voit où on est. Avec Git voilà comment on change de branche (branche légère) :

    + +
    + +
    git checkout branch
    + +
    + +

    Alors là, on se dit “WTF?” ; en français : mais qu’est-ce que c’est que ça ? Je croyais que checkout c’était pour récupérer l’état d’un fichier ?

    En fait le mot checkout sert à la fois à revenir en arrière sur un fichier (MAIS PAS TOUTE UNE ARBORESCENCE où là ça sera reset --hard) et à changer de branche !

    -

    Je trouve ça carrément contre nature. Même si c’est totalement justifié du point de vue théorique voir le très bon article (en anglais) Git for Computer Scientist. Du point de vue interface utilisateur, on peut difficilement faire pire. On dirait que les mots clés sont utilisés pour piéger l’utilisateur.

    +

    Je trouve ça carrément contre nature. Même si c’est totalement justifié du point de vue théorique voir le très bon article (en anglais) Git for Computer Scientist. Du point de vue interface utilisateur, on peut difficilement faire pire. On dirait que les mots clés sont utilisés pour piéger l’utilisateur.

      -
    • — Alors, essaye de deviner ce qu’il va falloir écrire pour faire cette opération ?
    • -
    • — Perdu. Essaye encore, cherche sur Internet (blaireau).
    • -
    • — Non, c’est toujours pas bon, recommence (sale nul).
    • +
    • — Alors, essaye de deviner ce qu’il va falloir écrire pour faire cette opération ?
    • +
    • — Perdu. Essaye encore, cherche sur Internet (blaireau).
    • +
    • — Non, c’est toujours pas bon, recommence (sale nul).
    -

    Bon alors, voilà, les défauts de Git. Mais, il a par contre beaucoup d’avantages. Une fois qu’on a compris le principe des branches légères. Tout devient plus clair. Même si en pratique, l’édition du fichier .git/config peut s’avérer un peu fastidieuse et surtout contre intuitive.

    +

    Bon alors, voilà, les défauts de Git. Mais, il a par contre beaucoup d’avantages. Une fois qu’on a compris le principe des branches légères. Tout devient plus clair. Même si en pratique, l’édition du fichier .git/config peut s’avérer un peu fastidieuse et surtout contre intuitive.

    -

    - Il faut aussi préciser qu’ayant travaillé sur les logiques multi-modales et en particulier sur les logiques temporelles (linéaires ou non), j’étais plus enclin à adhérer à cette vision des choses. “Ah mes premiers amours dans la recherche scientifique !”

    +

    - Il faut aussi préciser qu’ayant travaillé sur les logiques multi-modales et en particulier sur les logiques temporelles (linéaires ou non), j’étais plus enclin à adhérer à cette vision des choses. “Ah mes premiers amours dans la recherche scientifique !”

    @@ -250,18 +280,18 @@ Avec Bazaar ça donne :

    DCVS vs. CVS ?

    -

    Est-ce que ça valait la peine d’utiliser un système de “versionning” décentralisé ? Indéniablement la réponse est oui. On peut très bien vivre avec des systèmes de versions centralisés, mais la souplesse apportée par la facilité de “merger” différentes branches. De travailler de façon autonomes sur différentes parties d’un projets sont vraiment des plus appréciables. J’aurai vraiment du mal à revenir en arrière.

    +

    Est-ce que ça valait la peine d’utiliser un système de “versionning” décentralisé ? Indéniablement la réponse est oui. On peut très bien vivre avec des systèmes de versions centralisés, mais la souplesse apportée par la facilité de “merger” différentes branches. De travailler de façon autonomes sur différentes parties d’un projets sont vraiment des plus appréciables. J’aurai vraiment du mal à revenir en arrière.

    Est-ce que Git est meilleurs que Bazaar ?

    En terme de fonctionnalités je dirai que Git est meilleurs. -Par contre, je dois avouer qu’il s’agit d’un CVS qui s’est mis dans mes pattes. Or c’est exactement ce que je ne souhaitait pas lors de mon premier choix.

    +Par contre, je dois avouer qu’il s’agit d’un CVS qui s’est mis dans mes pattes. Or c’est exactement ce que je ne souhaitait pas lors de mon premier choix.

    -

    Je n’aurai pas dû avoir du mal à comprendre cette notion de branche légère qui doit être un patch sinon tu reçois des messages t’expliquant que tu es en retard. En réalité, Git différencie la notion d’arbre de la notion de branche. Ce qui n’est pas le cas dans Bazaar. Conceptuellement, c’est beaucoup plus simple de comprendre avec Bazaar.

    +

    Je n’aurai pas dû avoir du mal à comprendre cette notion de branche légère qui doit être un patch sinon tu reçois des messages t’expliquant que tu es en retard. En réalité, Git différencie la notion d’arbre de la notion de branche. Ce qui n’est pas le cas dans Bazaar. Conceptuellement, c’est beaucoup plus simple de comprendre avec Bazaar.

    Finalement ?

    -

    Pour conclure, j’utilise plus souvent Git que Bazaar et je dois dire que je préfère utiliser Git. Cependant, les commandes comme revert manquent cruellement avec Git. Pour l’instant je n’ai pas encore fait d’alias pour renommer les commandes Git comme je le souhaite.

    +

    Pour conclure, j’utilise plus souvent Git que Bazaar et je dois dire que je préfère utiliser Git. Cependant, les commandes comme revert manquent cruellement avec Git. Pour l’instant je n’ai pas encore fait d’alias pour renommer les commandes Git comme je le souhaite.

    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html index 3792c7740..42b5e8e06 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -62,45 +65,51 @@
    -

    Si tout ce qui vous intéresse c’est d’utiliser Git tout de suite. Lisez simplement les parties sur fond noir. Je vous conseille aussi de revenir relire tout ça un peu plus tard, pour mieux comprendre les fondements des systèmes de versions et ne pas faire de bêtises quand vous les utilisez.

    +

    Si tout ce qui vous intéresse c’est d’utiliser Git tout de suite. Lisez simplement les parties sur fond noir. Je vous conseille aussi de revenir relire tout ça un peu plus tard, pour mieux comprendre les fondements des systèmes de versions et ne pas faire de bêtises quand vous les utilisez.

    -

    Git est un DCVS, c’est-à-dire un système de versions concurrentes décentralisé. Analysons chaque partie de cette appellation compliquée.

    +

    Git est un DCVS, c’est-à-dire un système de versions concurrentes décentralisé. Analysons chaque partie de cette appellation compliquée.

    Système de versions

    -

    Tout d’abord, les systèmes de versions gèrent des fichiers. +

    Tout d’abord, les systèmes de versions gèrent des fichiers. Quand on travaille avec des fichiers sans système de version voilà ce qui arrive souvent :

    -

    Lorsqu’on modifie un fichier un peu critique et qu’on a pas envie de perdre, on se retrouve souvent à le recopier sous un autre nom. Par exemple

    - -
    $ cp fichier_important.c fichier_important.c.bak
    -
    - -

    Du coups, ce nouveau fichier joue le rôle de backup. Si on casse tout, on peut toujours écraser les modifications que nous avons faites. Évidemment le problème avec cette façon de faire c’est que ce n’est pas très professionnel. Et puis c’est un peu limité. Si on veut faire trois ou quatre modifications on se retrouve avec plein de fichiers. Parfois avec des nom bizarres comme :

    +

    Lorsqu’on modifie un fichier un peu critique et qu’on a pas envie de perdre, on se retrouve souvent à le recopier sous un autre nom. Par exemple

    -
    -fichier_important.c.bak
    +
    +
    $ cp fichier_important.c fichier_important.c.bak
    + +
    + +

    Du coups, ce nouveau fichier joue le rôle de backup. Si on casse tout, on peut toujours écraser les modifications que nous avons faites. Évidemment le problème avec cette façon de faire c’est que ce n’est pas très professionnel. Et puis c’est un peu limité. Si on veut faire trois ou quatre modifications on se retrouve avec plein de fichiers. Parfois avec des nom bizarres comme :

    + +
    + + +
    fichier_important.c.bak
     fichier_important.c.old
     fichier_important.c.Bakcup
     fichier_important.c.BAK.2009-11-14
     fichier_important.c.2009.11.14
     fichier_important.c.12112009
     old.fichier_important.c
    -
    + + +
    -

    Bon alors si on veut que ça marche il faut se fixer des conventions de nommage. Les fichiers prennent beaucoup de place alors que souvent il n’y a que quelques lignes différentes entre le fichier et son backup…

    +

    Bon alors si on veut que ça marche il faut se fixer des conventions de nommage. Les fichiers prennent beaucoup de place alors que souvent il n’y a que quelques lignes différentes entre le fichier et son backup…

    Heureusement les systèmes de version viennent à la rescousse.

    -

    Il suffit de signaler que l’on va faire une nouvelle version d’un fichier et le système de version se débrouille pour l’enregistrer quelque part où on pourra facilement le retrouver. Et en général, le système de version fait les choses bien. C’est-à-dire qu’il n’utilise que très peu d’espace disque pour faire ces backups.

    +

    Il suffit de signaler que l’on va faire une nouvelle version d’un fichier et le système de version se débrouille pour l’enregistrer quelque part où on pourra facilement le retrouver. Et en général, le système de version fait les choses bien. C’est-à-dire qu’il n’utilise que très peu d’espace disque pour faire ces backups.

    -

    Il fut un temps où les versions étaient gérées fichier par fichier. Je pense à CVS. Puis on s’est vite aperçu qu’un projet c’est un ensemble de fichiers cohérents. Et donc il ne suffit pas de pouvoir revenir en arrière par fichier, mais plutôt dans le temps. Les numéros de versions sont donc passé d’un numéro par fichier à un numéro par projet tout entier.

    +

    Il fut un temps où les versions étaient gérées fichier par fichier. Je pense à CVS. Puis on s’est vite aperçu qu’un projet c’est un ensemble de fichiers cohérents. Et donc il ne suffit pas de pouvoir revenir en arrière par fichier, mais plutôt dans le temps. Les numéros de versions sont donc passé d’un numéro par fichier à un numéro par projet tout entier.

    Ainsi on peut dire, «je veux revenir trois jours en arrière», et tous les fichiers se remettent à jour.

    @@ -108,12 +117,12 @@ old.fichier_important.c
    -

    Qu’apportent les systèmes de versions ? (je n’ai pas tout mentionné)

    +

    Qu’apportent les systèmes de versions ? (je n’ai pas tout mentionné)

    • backup automatique de tous les fichiers: Revenir dans le temps. ;
    • donne la possibilité de voir les différences entre chaque version et les différences entre la version en cours et les modifications locales ;
    • -
    • permet de poser un tag sur certaines versions et ainsi pouvoir s’y référer facilement ;
    • +
    • permet de poser un tag sur certaines versions et ainsi pouvoir s’y référer facilement ;
    • permet d’avoir un historique des modifications. Car en général il est demandé aux utilisateurs d’ajouter un petit commentaire à chaque nouvelle version.
    @@ -123,17 +132,20 @@ old.fichier_important.c

    concurrentes

    -

    Les systèmes de versions sont déjà intéressants pour gérer ses projets personnels. Car ils permettent de mieux organiser celui-ci. De ne (presque) plus se poser de questions à propos des backups. Je dis presque parce qu’il faut quand même penser à protéger par backup son repository. Mais là où les systèmes de versions deviennent vraiment intéressants, c’est pour la gestion de projets à plusieurs.

    +

    Les systèmes de versions sont déjà intéressants pour gérer ses projets personnels. Car ils permettent de mieux organiser celui-ci. De ne (presque) plus se poser de questions à propos des backups. Je dis presque parce qu’il faut quand même penser à protéger par backup son repository. Mais là où les systèmes de versions deviennent vraiment intéressants, c’est pour la gestion de projets à plusieurs.

    Commençons par un exemple avec un projet fait par deux personnes ; Alex et Béatrice. Sur un fichier contenant une liste de dieux Lovecraftiens :

    -
    -Cthulhu
    +
    +
    +
    Cthulhu
     Shubniggurath
     Yogsototh
    -
    + + +

    Disons que Alex est chez lui, il modifie le fichier :

    @@ -146,9 +158,9 @@ Yogsototh
    -

    puis il envoi ce fichier sur le serveur du projet. Ainsi sur le serveur, il y a le fichier d’Alex.

    +

    puis il envoi ce fichier sur le serveur du projet. Ainsi sur le serveur, il y a le fichier d’Alex.

    -

    Ensuite c’est Béatrice qui n’a pas récupéré le fichier d’Alex sur le serveur qui fait une modification.

    +

    Ensuite c’est Béatrice qui n’a pas récupéré le fichier d’Alex sur le serveur qui fait une modification.

    @@ -161,7 +173,7 @@ Yogsototh
     
     

    Puis Béatrice envoi son fichier sur le serveur.

    -

    La modification d’Alex est perdue. Encore une fois les systèmes de versions sont là pour résoudre ce type de soucis.

    +

    La modification d’Alex est perdue. Encore une fois les systèmes de versions sont là pour résoudre ce type de soucis.

    Un système de version aurait mergé les deux fichiers au moment où Béatrice voulait envoyer la modification sur le serveur. Et comme par magie, sur le serveur le fichier deviendra :

    @@ -175,18 +187,18 @@ Yogsototh
    -

    En pratique, au moment où Béatrice veut envoyer ses modifications, le système de version la préviens qu’une modification a eu lieu sur le serveur. Elle utilise la commande qui rapatrie les modifications localement et qui va mettre à jour le fichier. Ensuite Béatrice renvoie le nouveau fichier sur le serveur.

    +

    En pratique, au moment où Béatrice veut envoyer ses modifications, le système de version la préviens qu’une modification a eu lieu sur le serveur. Elle utilise la commande qui rapatrie les modifications localement et qui va mettre à jour le fichier. Ensuite Béatrice renvoie le nouveau fichier sur le serveur.

    -

    Qu’apportent les Systèmes de Versions Concurrentes ?

    +

    Qu’apportent les Systèmes de Versions Concurrentes ?

    • récupérer sans problème les modifications des autres ;
    • envoyer sans problème ses modifications aux autres ;
    • -
    • permet de gérer les conflits. Je n’en ai pas parlé, mais quand un conflit arrive (ça peut arriver si deux personnes modifient la même ligne avec deux contenus différents), les SVC proposent leur aide pour les résoudre. J’en dirai un mot plus loin.
    • +
    • permet de gérer les conflits. Je n’en ai pas parlé, mais quand un conflit arrive (ça peut arriver si deux personnes modifient la même ligne avec deux contenus différents), les SVC proposent leur aide pour les résoudre. J’en dirai un mot plus loin.
    • permet de savoir qui a fait quoi et quand
    @@ -196,26 +208,26 @@ Yogsototh

    décentralisé

    -

    Ce mot n’est devenu populaire que très récemment dans le milieu des systèmes de version. Et bien ça veut dire principalement deux choses.

    +

    Ce mot n’est devenu populaire que très récemment dans le milieu des systèmes de version. Et bien ça veut dire principalement deux choses.

    -

    Tout d’abord, jusqu’à très récemment (SVN) il fallait être connecté sur un serveur distant pour avoir des informations sur un projet. Comme avoir l’historique. Les nouveaux systèmes décentralisés permettent de travailler avec un REPOSITORY (le répertoire contenant tous les backups, et les différentes info nécessaires au fonctionnement du système de versions) local au projet. Ainsi on peut avoir l’historique du projet sans avoir à se connecter au serveur.

    +

    Tout d’abord, jusqu’à très récemment (SVN) il fallait être connecté sur un serveur distant pour avoir des informations sur un projet. Comme avoir l’historique. Les nouveaux systèmes décentralisés permettent de travailler avec un REPOSITORY (le répertoire contenant tous les backups, et les différentes info nécessaires au fonctionnement du système de versions) local au projet. Ainsi on peut avoir l’historique du projet sans avoir à se connecter au serveur.

    Toutes les instances de projets peuvent vivre de façon indépendantes.

    Pour préciser, les systèmes de versions concurrentes décentralisés sont basés sur la notion de branche.

    -

    Et la signification pratique est très importante. Ça veut dire que tout les utilisateurs travaillent de façon complètement indépendante les uns des autres. Et c’est l’outil de version qui se charge de mettre tout ça ensemble.

    +

    Et la signification pratique est très importante. Ça veut dire que tout les utilisateurs travaillent de façon complètement indépendante les uns des autres. Et c’est l’outil de version qui se charge de mettre tout ça ensemble.

    -

    Ça va même encore plus loin. Ça permet de développer plusieurs features de manière complètement indépendantes. Sous les autres systèmes c’était plus difficile.

    +

    Ça va même encore plus loin. Ça permet de développer plusieurs features de manière complètement indépendantes. Sous les autres systèmes c’était plus difficile.

    -

    L’exemple type :

    +

    L’exemple type :

    -

    Je développe mon projet. Je suis en train de l’améliorer. Lorsqu’un bug urgent est reporté.

    +

    Je développe mon projet. Je suis en train de l’améliorer. Lorsqu’un bug urgent est reporté.

    Je peux très facilement avec un système décentralisé, revenir sur la version qui pose problème. Résoudre le bug. Renvoyer les modifications. Puis revenir à ma version avec les améliorations en cours. Et même récupérer la correction de bug dans ma nouvelle version avec les améliorations.

    -

    Dans un système non décentralisé, cela est possible, mais fastidieux. Les systèmes décentralisés rendent ce type de comportement très naturels. Ainsi, il devient naturel de tirer des branches pour toutes les features, les bug…

    +

    Dans un système non décentralisé, cela est possible, mais fastidieux. Les systèmes décentralisés rendent ce type de comportement très naturels. Ainsi, il devient naturel de tirer des branches pour toutes les features, les bug…

    @@ -236,15 +248,15 @@ Yogsototh

    Pour résumer

    -

    Résumons l’ensemble des choses que l’on peut faire facilement avec un DCVS :

    +

    Résumons l’ensemble des choses que l’on peut faire facilement avec un DCVS :

    Systèmes de versions

    • revenir dans le temps ;
    • lister les différences entre chaque version ;
    • -
    • nommer certaines versions pour s’y référer facilement ;
    • -
    • afficher l’historique des modifications.
    • +
    • nommer certaines versions pour s’y référer facilement ;
    • +
    • afficher l’historique des modifications.

    Concurrentes

    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html index e6994dc92..c2df09541 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -56,9 +59,9 @@
    -

    Et c’est parti !

    +

    Et c’est parti !

    -

    Voici une parmi de nombreuses autres façon d’utiliser Git. Cette méthode est nécessaire et suffisante pour travailler seul ou en collaboration sur un projet commun. Cependant, on peut faire beaucoup mieux avec Git que ce workflow (en langage anglo-saxon).

    +

    Voici une parmi de nombreuses autres façon d’utiliser Git. Cette méthode est nécessaire et suffisante pour travailler seul ou en collaboration sur un projet commun. Cependant, on peut faire beaucoup mieux avec Git que ce workflow (en langage anglo-saxon).

    Utilisation basique

    @@ -78,20 +81,23 @@ -

    Voilà, avec ces quelques commandes vous pouvez utiliser Git sur un projet avec d’autres personnes. Même si c’est suffisant, il faut quand même connaître une chose avant de se lancer ; la gestion des conflits.

    +

    Voilà, avec ces quelques commandes vous pouvez utiliser Git sur un projet avec d’autres personnes. Même si c’est suffisant, il faut quand même connaître une chose avant de se lancer ; la gestion des conflits.

    Gestion des conflits

    -

    Les conflits peuvent survenir lorsque vous modifiez les même lignes de codes sur le même fichier d’une autre branche que vous mergez. Ça peut sembler un peu intimidant, mais avec Git ce genre de chose est très facile a régler.

    +

    Les conflits peuvent survenir lorsque vous modifiez les même lignes de codes sur le même fichier d’une autre branche que vous mergez. Ça peut sembler un peu intimidant, mais avec Git ce genre de chose est très facile a régler.

    exemple

    Vous partez du fichier suivant :

    -
    -Zoot 
    -
    + + +
    Zoot 
    +
    + +

    et vous modifiez une ligne

    @@ -113,17 +119,20 @@ Zoot, just Zoot

    Maintenant quand vous lancez la commande

    -
    -$ git pull
    -remote: Counting objects: 5, done.
    +
    +
    +
    $ git pull
    +remote: Counting objects: 5, done.
     remote: Total 3 (delta 0), reused 0 (delta 0)
    -Unpacking objects: 100% (3/3), done.
    +Unpacking objects: 100% (3/3), done.
     From /home/e640846/tmp/conflictTest
    -   d3ea395..2dc7ffb  master     -> origin/master
    +   d3ea395..2dc7ffb  master     -> origin/master
     Auto-merging foo
    -CONFLICT (content): Merge conflict in foo
    -Automatic merge failed; fix conflicts and then commit the result.
    -
    +CONFLICT (content): Merge conflict in foo +Automatic merge failed; fix conflicts and then commit the result. +
    + +

    Notre fichier foo contient alors :

    @@ -140,7 +149,7 @@ Zoot the pure

    Résolution du conflit

    -

    Régler le conflit, il suffit d’éditer le fichier, par exemple en écrivant :

    +

    Régler le conflit, il suffit d’éditer le fichier, par exemple en écrivant :

    @@ -148,16 +157,19 @@ Zoot the not so pure
     
    -

    et de ‘commiter’ tout simplement :

    +

    et de ‘commiter’ tout simplement :

    -
    -git commit -a -m "conflict resolved"
    -
    + + +
    git commit -a -m "conflict resolved"
    +
    + +

    Maintenant vous êtes fin prêt pour utiliser Git. -Sauf que Git, c’est un outil qui permet de faire beaucoup plus que juste ça. Nous allons maintenant voir comment utiliser les fonctionnalités de Git qui n’étaient pas disponibles avec CVS et consorts.

    +Sauf que Git, c’est un outil qui permet de faire beaucoup plus que juste ça. Nous allons maintenant voir comment utiliser les fonctionnalités de Git qui n’étaient pas disponibles avec CVS et consorts.

    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html index ea999dfdf..4d6774130 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -69,8 +72,8 @@
  • envoyer ses modifications aux autres ;
  • revenir dans le temps ;
  • lister les différences entre chaque version ;
  • -
  • nommer certaines versions pour s’y référer facilement ;
  • -
  • afficher l’historique des modifications ;
  • +
  • nommer certaines versions pour s’y référer facilement ;
  • +
  • afficher l’historique des modifications ;
  • savoir qui a fait quoi et quand ;
  • gérer des conflits ;
  • manipuler facilement des branches.
  • @@ -78,114 +81,165 @@

    récupérer les modifications des autres

    -
    -$ git pull
    -
    +
    + +
    $ git pull
    +
    + +

    envoyer ses modifications aux autres

    -
    -$ git push
    -
    +
    + +
    $ git push
    +
    + +

    ou plus généralement

    -
    -$ git pull
    +
    + +
    $ git pull
     $ git push
    -
    +
    + +

    revenir dans le temps

    -

    Pour toute l’arborescence

    +

    Pour toute l’arborescence

    -
    -$ git checkout
    -
    +
    -
    -$ git revert
    -
    +
    $ git checkout
    +
    + +
    + +
    + +
    $ git revert
    +
    + +

    revenir trois versions en arrière

    -
    -$ git uncommit 3
    -
    +
    -

    Revenir avant le dernier merge (s’il s’est mal passé).

    +
    $ git uncommit 3
    +
    -
    -$ git revertbeforemerge
    -
    +
    + +

    Revenir avant le dernier merge (s’il s’est mal passé).

    + +
    + +
    $ git revertbeforemerge
    +
    + +

    Pour un seul fichier

    -
    -$ git checkout file
    +
    + +
    $ git checkout file
     $ git checkout VersionHash file
     $ git checkout HEAD~3 file
    -
    +
    + +

    lister les différences entre chaque version

    liste les fichiers en cours de modifications

    -
    -$ git status
    -
    +
    + +
    $ git status
    +
    + +

    différences entre les fichiers de la dernière version et les fichiers locaux.

    -
    -$ git diff
    -
    +
    -

    liste les différences entre les fichier d’une certaine version et les fichiers locaux.

    +
    $ git diff
    +
    -
    -$ git diff VersionHash fichier
    -
    +
    -

    nommer certaines versions pour s’y référer facilement

    +

    liste les différences entre les fichier d’une certaine version et les fichiers locaux.

    -
    -$ git tag 'toto'
    -
    +
    -

    afficher l’historique des modifications

    +
    $ git diff VersionHash fichier
    +
    -
    -$ git log
    +
    + +

    nommer certaines versions pour s’y référer facilement

    + +
    + +
    $ git tag 'toto'
    +
    + +
    + +

    afficher l’historique des modifications

    + +
    + +
    $ git log
     $ git lg
     $ git logfull
    -
    + + +

    savoir qui a fait quoi et quand

    -
    -$ git blame fichier
    -
    +
    + +
    $ git blame fichier
    +
    + +

    gérer des conflits

    -
    -$ git conflict
    -
    +
    + +
    $ git conflict
    +
    + +

    manipuler facilement des branches

    Pour créer une branche :

    -
    -$ git branch branch_name
    -
    +
    + +
    $ git branch branch_name
    +
    + +

    Pour changer de branche courante :

    -
    -$ git checkout branch_name
    -
    +
    + +
    $ git checkout branch_name
    +
    + +
    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/index.html index a71b7afea..53ca76a5f 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,19 +63,19 @@

    Pourquoi Git est cool ?

    -

    Parce que grace à Git vous pouvez travailler sur plusieurs partie du projet de façon complètement isolée les unes des autres. Ça c’est la partie décentralisée de Git.

    +

    Parce que grace à Git vous pouvez travailler sur plusieurs partie du projet de façon complètement isolée les unes des autres. Ça c’est la partie décentralisée de Git.

    -

    Toutes les branches locales utilisent le même répertoire. Ainsi on peu changer de branche très aisément et rapidement. On peut aussi changer de branche alors que certains fichier sont en cours de modifications. On peut même pousser le vice jusqu’à modifier un fichier, changer de branche, commiter une partie seulement des modifications de ce fichier dans la branche courante. Revenir dans l’ancienne branche et commiter à nouveau les modifications restantes. Et merger dans une troisième branche les deux modifications.

    +

    Toutes les branches locales utilisent le même répertoire. Ainsi on peu changer de branche très aisément et rapidement. On peut aussi changer de branche alors que certains fichier sont en cours de modifications. On peut même pousser le vice jusqu’à modifier un fichier, changer de branche, commiter une partie seulement des modifications de ce fichier dans la branche courante. Revenir dans l’ancienne branche et commiter à nouveau les modifications restantes. Et merger dans une troisième branche les deux modifications.

    -

    Avec la command git rebase on peut après coup, décider que certaines modifications devaient aller dans certaines branches, que d’autres ne servaient à rien. C’est une commande vraiment très puissante pour organiser l’historique.

    +

    Avec la command git rebase on peut après coup, décider que certaines modifications devaient aller dans certaines branches, que d’autres ne servaient à rien. C’est une commande vraiment très puissante pour organiser l’historique.

    -

    En pratique, qu’est-ce que ça signifie ? Mieux qu’avec tous les autres systèmes de versions, vous pouvez utiliser Git pour vous concentrer sur votre code. En effet, on peut envoyer les commits après avoir coder. Par exemple, vous pouvez coder sur la résolution du bug b01, du bug b02 et de la feature f03. Puis ensuite, vous pouvez créer une branche par bug et par feature. Puis commiter les modifications pour chaque branche et chaque feature. Puis finalement merger tous les modifications dans la branche principale.

    +

    En pratique, qu’est-ce que ça signifie ? Mieux qu’avec tous les autres systèmes de versions, vous pouvez utiliser Git pour vous concentrer sur votre code. En effet, on peut envoyer les commits après avoir coder. Par exemple, vous pouvez coder sur la résolution du bug b01, du bug b02 et de la feature f03. Puis ensuite, vous pouvez créer une branche par bug et par feature. Puis commiter les modifications pour chaque branche et chaque feature. Puis finalement merger tous les modifications dans la branche principale.

    -

    Tout a été pensé pour vous permettre de coder d’abord, puis de vous occuper du système de version plus tard. Bien entendu, faire des commit atomique au fur et à mesure du code permet de gagner du temps et de ne pas trop s’embêter pour organiser les branches. Mais rien ne vous y oblige. Par contre faire la même chose dans d’autres systèmes de versions n’est absolument pas naturel.

    +

    Tout a été pensé pour vous permettre de coder d’abord, puis de vous occuper du système de version plus tard. Bien entendu, faire des commit atomique au fur et à mesure du code permet de gagner du temps et de ne pas trop s’embêter pour organiser les branches. Mais rien ne vous y oblige. Par contre faire la même chose dans d’autres systèmes de versions n’est absolument pas naturel.

    -

    Avec Git vous pouvez aussi dépendre de plusieurs sources. Ainsi, plutôt que d’avoir un serveur centralisé, vous pouvez avoir plusieurs sources. Vous pouvez définir ce genre de chose très finement.

    +

    Avec Git vous pouvez aussi dépendre de plusieurs sources. Ainsi, plutôt que d’avoir un serveur centralisé, vous pouvez avoir plusieurs sources. Vous pouvez définir ce genre de chose très finement.

    -

    Ce qui change le plus avec Git c’est la vision d’un projet centralisé sur un serveur avec plusieurs personnes qui travaillent dessus. Avec Git plusieurs personnes peuvent travailler sur le même projet, mais sans nécessairement avoir un repository de référence. On peut très facilement résoudre un bug et envoyer le patch à plein d’autres versions du projet.

    +

    Ce qui change le plus avec Git c’est la vision d’un projet centralisé sur un serveur avec plusieurs personnes qui travaillent dessus. Avec Git plusieurs personnes peuvent travailler sur le même projet, mais sans nécessairement avoir un repository de référence. On peut très facilement résoudre un bug et envoyer le patch à plein d’autres versions du projet.

    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig index 25bcf85ce..828ac4d13 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/code/gitconfig @@ -1,4 +1,3 @@ - [color] branch = auto diff = auto diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html index 309819016..447bbe65f 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -56,14 +59,17 @@
    -

    Avant l’utilisation, la configuration

    +

    Avant l’utilisation, la configuration

    installation

    Sous Linux Ubuntu ou Debian :

    -
    $ sudo apt-get install git
    -
    +
    + +
    $ sudo apt-get install git
    + +

    Sous Mac OS X :

    @@ -72,19 +78,23 @@
  • installez Git
  • -
    -$ sudo port selfupdate
    +
    + +
    $ sudo port selfupdate
     
     $ sudo port install git-core
    -
    +
    + +

    Configuration globale

    Enregistrez le fichier suivant comme le fichier ~/.gitconfig.

    -
    -
    -[color]
    +
    + + +
    [color]
         branch = auto
         diff   = auto
         status = auto
    @@ -95,102 +105,130 @@ $ sudo port install git-core
         lg        = log --pretty=oneline --graph
         logfull   = log --pretty=fuller --graph --stat -p
         unstage   = reset HEAD
    -    # there should be an article on what this command do
    -    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    +    # there should be an article on what this command do
    +    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
         undomerge = reset --hard ORIG_HEAD
    -	conflict  = !gitk --left-right HEAD...MERGE_HEAD
    -    # under Mac OS X, you should use gitx instead
    -	# conflict    = !gitx --left-right HEAD...MERGE_HEAD
    +	conflict  = !gitk --left-right HEAD...MERGE_HEAD
    +    # under Mac OS X, you should use gitx instead
    +	# conflict    = !gitx --left-right HEAD...MERGE_HEAD
     [branch]
     	autosetupmerge = true
    -
    -
    + + +

    Vous pouvez obtenir le même résultat en utilisant pour chaque entrée la commande git config --global. Configurez ensuite votre nom et votre email. Par exemple si vous vous appelez John Doe et que votre email est john.doe@email.com. Lancez les commandes suivantes :

    -
    -$ git config --global user.name John Doe
    +
    + +
    $ git config --global user.name John Doe
     
     $ git config --global user.email john.doe@email.com
    -
    +
    -

    Voilà, la configuration de base est terminée. J’ai créé dans le fichier de configuration global des alias qui vont permettre de taper des commandes un peu plus courtes.

    +
    -

    Récupération d’un projet déjà versionné

    +

    Voilà, la configuration de base est terminée. J’ai créé dans le fichier de configuration global des alias qui vont permettre de taper des commandes un peu plus courtes.

    + +

    Récupération d’un projet déjà versionné

    Si un projet est déjà versionné avec Git vous devez avoir une URL pointant vers les sources du projet. La commande a exécuter est alors très simple.

    -
    -$ cd ~/Projets
    +
    + +
    $ cd ~/Projets
     $ git clone git://main.server/path/to/file
    -
    +
    -

    S’il n’y a pas de serveur git sur le serveur distant, mais que vous avez un accès ssh, il suffit de remplacer le git de l’url par ssh. Pour ne pas avoir à entrer votre mot de passe à chaque fois le plus simple est de procéder comme suit :

    +
    -
    -$ ssh-keygen -t rsa
    -
    +

    S’il n’y a pas de serveur git sur le serveur distant, mais que vous avez un accès ssh, il suffit de remplacer le git de l’url par ssh. Pour ne pas avoir à entrer votre mot de passe à chaque fois le plus simple est de procéder comme suit :

    -

    Répondez aux question et n’entrez surtout PAS de mot de passe. Ensuite copiez les clés sur le serveur distant. Ce n’est pas la façon la plus sûre de procéder. L’idéal étant d’écrire quand même un mot de passe et d’utiliser ssh-agent.

    +
    + +
    $ ssh-keygen -t rsa
    +
    + +
    + +

    Répondez aux question et n’entrez surtout PAS de mot de passe. Ensuite copiez les clés sur le serveur distant. Ce n’est pas la façon la plus sûre de procéder. L’idéal étant d’écrire quand même un mot de passe et d’utiliser ssh-agent.

    Ensuite le plus simple, si vous possédez ssh-copy-id (sous Ubuntu par exemple) :

    -
    -me@locahost$ ssh-copy-id -i ~/.ssh/id_rsa.pub me@main.server
    -
    +
    + +
    me@locahost$ ssh-copy-id -i ~/.ssh/id_rsa.pub me@main.server
    +
    + +

    ou manuellement :

    -
    -me@locahost$ scp ~/.ssh/id_rsa.pub me@main.server:
    +
    + +
    me@locahost$ scp ~/.ssh/id_rsa.pub me@main.server:
     me@locahost$ ssh me@main.server
     password:
    -me@main.server$ cat id_rsa.pub >> ~/.ssh/authorized_keys
    +me@main.server$ cat id_rsa.pub >> ~/.ssh/authorized_keys
     me@main.server$ rm id_rsa.pub
     me@main.server$ logout
    -
    +
    -

    Maintenant vous n’avez plus besoin de taper votre mot de passe pour accéder à main.server. Et donc aussi pour les commandes git.

    +
    + +

    Maintenant vous n’avez plus besoin de taper votre mot de passe pour accéder à main.server. Et donc aussi pour les commandes git.

    Créer un nouveau projet

    Supposons que vous avez déjà un projet avec des fichiers. Alors il est très facile de le versionner.

    -
    -$ cd /path/to/project
    +
    + +
    $ cd /path/to/project
     $ git init
     $ git add .
    -$ git commit -m "Initial commit"
    -
    +$ git commit -m "Initial commit" +
    + +

    Une petite précision. Si vous ne souhaitez pas versionner tous les fichiers. Par exemple, les fichiers de compilations intermédiaires. Alors il faut les exclure. Pour cela, avant de lancer la commande git add .. Il faut créer un fichier .gitignore qui va contenir les pattern que git doit ignorer. Par exemple :

    -
    -*.o
    +
    + +
    *.o
     *.bak
     *.swp
     *~
    -
    +
    -

    Maintenant si vous voulez créer un repository sur un serveur distant, il faut absolument qu’il soit en mode bare. C’est-à-dire que le repository ne contiendra que la partie contenant les informations utile à la gestion de git, mais pas les fichiers du projet. Sans rentrer dans les détails, il suffit de lancer :

    +
    -
    -$ cd /path/to/local/project
    +

    Maintenant si vous voulez créer un repository sur un serveur distant, il faut absolument qu’il soit en mode bare. C’est-à-dire que le repository ne contiendra que la partie contenant les informations utile à la gestion de git, mais pas les fichiers du projet. Sans rentrer dans les détails, il suffit de lancer :

    + +
    + +
    $ cd /path/to/local/project
     $ git clone --bare . ssh://server/path/to/project
    -
    +
    + +

    Les autres pourront alors récupérer les modifications via la commande vue précédemment :

    -
    -git clone ssh://server/path/to/project
    -
    +
    + +
    git clone ssh://server/path/to/project
    +
    + +

    Résumé de la seconde étape

    -

    Vous avez maintenant un répertoire sur votre ordinateur local. Il est versionné. Vous pouvez vous en rendre compte parcequ’à la racine (et à la racine seulement), il y a un répertoire .git. Ce répertoire contient tous les fichiers nécessaires au bon fonctionnement de Git.

    +

    Vous avez maintenant un répertoire sur votre ordinateur local. Il est versionné. Vous pouvez vous en rendre compte parcequ’à la racine (et à la racine seulement), il y a un répertoire .git. Ce répertoire contient tous les fichiers nécessaires au bon fonctionnement de Git.

    -

    Il ne reste plus qu’à savoir comment s’en servir maintenant pour obtenir toutes les jolies promesses faites dans la première partie.

    +

    Il ne reste plus qu’à savoir comment s’en servir maintenant pour obtenir toutes les jolies promesses faites dans la première partie.

    diff --git a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/index.html b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/index.html index 331bd6d08..5842489c6 100644 --- a/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/index.html +++ b/output/Scratch/fr/blog/2009-11-12-Git-for-n00b/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,7 +65,7 @@
    -

    Voici un tutoriel Git détaillé pour ceux qui en connaissent très peu sur les systèmes de versions. Vous comprendrez l’utilité de tels systèmes et surtout comment on se sert des systèmes de versions modernes, le tout en restant le plus pragmatique possible.

    +

    Voici un tutoriel Git détaillé pour ceux qui en connaissent très peu sur les systèmes de versions. Vous comprendrez l’utilité de tels systèmes et surtout comment on se sert des systèmes de versions modernes, le tout en restant le plus pragmatique possible.

    @@ -76,41 +79,47 @@

    Pour commencer, la conclusion

    -

    Voici la liste des commandes nécessaires et suffisantes pour utiliser Git. Il y en a très peu. Il est normal de ne pas les comprendre tout de suite mais c’est pour vous donner une idée. Malgré la longueur de l’article, 95% de l’utilisation de Git tiens dans les 7 commandes décrites ci-après.

    +

    Voici la liste des commandes nécessaires et suffisantes pour utiliser Git. Il y en a très peu. Il est normal de ne pas les comprendre tout de suite mais c’est pour vous donner une idée. Malgré la longueur de l’article, 95% de l’utilisation de Git tiens dans les 7 commandes décrites ci-après.

    Récupérer un projet :

    -
    -git clone ssh://server/path/to/project
    -
    +
    + +
    git clone ssh://server/path/to/project
    +
    + +

    Utiliser Git tous les jours :

    -
    -# get modifications from other
    +
    + +
    # get modifications from other
     git pull
    -# read what was done
    +# read what was done
     git log
     
    -# Make local changes to files 
    +# Make local changes to files 
     hack, hack, hack...
    -# list the modified files
    +# list the modified files
     git status
    -# show what I've done
    +# show what I've done
     git diff
     
    -# tell git to version a new file
    +# tell git to version a new file
     git add new/file
     
    -# commit its own modifications 
    -# to its local branch
    -git commit -a -m "Fix bug #321"
    +# commit its own modifications 
    +# to its local branch
    +git commit -a -m "Fix bug #321"
     
    -# send local modifications to other
    +# send local modifications to other
     git push
    -
    +
    -

    Cet article est écrit pour ceux qui en savent très peu sur les systèmes de version. Il est aussi écrit pour ceux qui n’ont pas suivi les progrès accomplis depuis CVS ou subversion (SVN). C’est pourquoi, dans un premier temps, j’explique rapidement quels sont les buts poursuivis par les systèmes de versions. J’explique ensuite comment installer et configurer Git. Puis, pour chaque action que doivent accomplir les DCVS je donne les commandes Git qui y correspondent.

    +
    + +

    Cet article est écrit pour ceux qui en savent très peu sur les systèmes de version. Il est aussi écrit pour ceux qui n’ont pas suivi les progrès accomplis depuis CVS ou subversion (SVN). C’est pourquoi, dans un premier temps, j’explique rapidement quels sont les buts poursuivis par les systèmes de versions. J’explique ensuite comment installer et configurer Git. Puis, pour chaque action que doivent accomplir les DCVS je donne les commandes Git qui y correspondent.

    diff --git a/output/Scratch/fr/blog/2009-12-06-iphone-call-filter/index.html b/output/Scratch/fr/blog/2009-12-06-iphone-call-filter/index.html index eac84da06..3b2dc1a6c 100644 --- a/output/Scratch/fr/blog/2009-12-06-iphone-call-filter/index.html +++ b/output/Scratch/fr/blog/2009-12-06-iphone-call-filter/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,9 +57,9 @@
    -

    Il est vraiment incroyable que le filtrage d’appel soit impossible avec un iPhone ! Le seul intérêt que j’y vois, c’est une négociation avec les opérateurs pour interdire aux utilisateurs de passer à travers la publicité. C’est tout simplement inacceptable.

    +

    Il est vraiment incroyable que le filtrage d’appel soit impossible avec un iPhone ! Le seul intérêt que j’y vois, c’est une négociation avec les opérateurs pour interdire aux utilisateurs de passer à travers la publicité. C’est tout simplement inacceptable.

    -

    Je suis un utilisateur λ de l’iPhone. Le seul moyen de filtrer ses appels, de faire des blacklists ou autre c’est de jailbreaker son iPhone. Et je n’en ai aucune envie. Alors si comme moi, vous trouvez ça inacceptable, envoyez un mot à Apple : http://www.apple.com/feedback/iphone.html

    +

    Je suis un utilisateur λ de l’iPhone. Le seul moyen de filtrer ses appels, de faire des blacklists ou autre c’est de jailbreaker son iPhone. Et je n’en ai aucune envie. Alors si comme moi, vous trouvez ça inacceptable, envoyez un mot à Apple : http://www.apple.com/feedback/iphone.html

    diff --git a/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/code/gitconfig b/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/code/gitconfig index 85a89236f..78a9edd90 100644 --- a/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/code/gitconfig +++ b/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/code/gitconfig @@ -1,3 +1,2 @@ - [alias] uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""' diff --git a/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/index.html b/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/index.html index 3dc08e307..892eee871 100644 --- a/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/index.html +++ b/output/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -62,7 +65,7 @@
    -

    Même si je considère que git a beaucoup de points noirs, je pense qu’il reste le meilleur DCVS à ce jour avec lequel travailler. C’est pourquoi je commencerai par parler des qualité de bazaar qui manquent à git. Ensuite seulement je donnerai le seul avantage de git qui suffit à le rendre préférable à Bazaar.

    +

    Même si je considère que git a beaucoup de points noirs, je pense qu’il reste le meilleur DCVS à ce jour avec lequel travailler. C’est pourquoi je commencerai par parler des qualité de bazaar qui manquent à git. Ensuite seulement je donnerai le seul avantage de git qui suffit à le rendre préférable à Bazaar.

    @@ -70,60 +73,78 @@

    La découverte des DCVS

    -

    À savoir avant de débuter l’article. Je suis, comme beaucoup, un ancien utilisateur de subversion. Je trouve subversion très bien, mais j’ai été conquis par les capacités supplémentaires apportées par les systèmes de versions concurrentes décentralisés.

    +

    À savoir avant de débuter l’article. Je suis, comme beaucoup, un ancien utilisateur de subversion. Je trouve subversion très bien, mais j’ai été conquis par les capacités supplémentaires apportées par les systèmes de versions concurrentes décentralisés.

    -

    Il y a deux façon de percevoir les systèmes de versions. Soit on voit un système de branches (voir le très bon article sur betterexplained). Soit on peut voir les systèmes de versions comme des moyens d’appliquer des patches. C’est-à-dire que l’on peut soit se concentrer sur les nœuds soit sur les transitions du graphe induit par les différentes versions d’un projet.

    +

    Il y a deux façon de percevoir les systèmes de versions. Soit on voit un système de branches (voir le très bon article sur betterexplained). Soit on peut voir les systèmes de versions comme des moyens d’appliquer des patches. C’est-à-dire que l’on peut soit se concentrer sur les nœuds soit sur les transitions du graphe induit par les différentes versions d’un projet.

    -

    Pour git, c’est plutôt ce deuxième point de vue qui a été adopté. C’est un peu normal, étant donné que c’est Linus Torvald qui l’a inventé pour combler les problèmes inhérent aux problèmes de création de code dans le noyau Linux. Et comme historiquement, la communauté Linux se base beaucoup sur les patches, il semblait logique que ce soit ce second point de vue qui soit adopté.

    +

    Pour git, c’est plutôt ce deuxième point de vue qui a été adopté. C’est un peu normal, étant donné que c’est Linus Torvald qui l’a inventé pour combler les problèmes inhérent aux problèmes de création de code dans le noyau Linux. Et comme historiquement, la communauté Linux se base beaucoup sur les patches, il semblait logique que ce soit ce second point de vue qui soit adopté.

    -

    J’ai d’abord été convaincu par Bazaar. Pourquoi ? Les arguments en faveur de bazaar étaient : facilité d’utilisation en particulier, facilité d’adaptation pour tous ceux qui étaient habitués à subversion. Comme c’était mon cas, et que lorsque j’avais essayé de suivre la doc Git à l’époque c’était un peu épique. -Puis avec le temps, je me suis dit que je n’allais quand même pas mourir idiot et que j’allais me mettre sérieusement à git histoire de voir si ses défenseurs avaient vraiment raison.

    +

    J’ai d’abord été convaincu par Bazaar. Pourquoi ? Les arguments en faveur de bazaar étaient : facilité d’utilisation en particulier, facilité d’adaptation pour tous ceux qui étaient habitués à subversion. Comme c’était mon cas, et que lorsque j’avais essayé de suivre la doc Git à l’époque c’était un peu épique. +Puis avec le temps, je me suis dit que je n’allais quand même pas mourir idiot et que j’allais me mettre sérieusement à git histoire de voir si ses défenseurs avaient vraiment raison.

    -

    Mon dieu, que ce fut fastidieux. La terminologie est affreuse ! Et ce n’est rien de le dire.

    +

    Mon dieu, que ce fut fastidieux. La terminologie est affreuse ! Et ce n’est rien de le dire.

    Là où Bazaar est meilleur que git

    -

    Par exemple, checkout qui sert certainement à la même chose du point de vue technique, est dans l’usage un terme employé pour faire des actions qui semblent très différentes à un utilisateur λ. Exemple :

    +

    Par exemple, checkout qui sert certainement à la même chose du point de vue technique, est dans l’usage un terme employé pour faire des actions qui semblent très différentes à un utilisateur λ. Exemple :

    -
    -git checkout pipo
    -
    +
    + +
    git checkout pipo
    +
    + +

    annule une modification courante du fichier pipo

    -
    -git checkout pipo
    -
    +
    + +
    git checkout pipo
    +
    + +

    change de la branche courante vers la branche pipo

    -

    Et là, comme moi, vous remarquez que la même commande à deux sens complètement différents. Comment ça se passe alors, quand il y a une branche pipo et un fichier pipo alors ? Et bien par défaut, ça change de branche. Pour lever l’ambigüité il faut utiliser la syntaxe

    +

    Et là, comme moi, vous remarquez que la même commande à deux sens complètement différents. Comment ça se passe alors, quand il y a une branche pipo et un fichier pipo alors ? Et bien par défaut, ça change de branche. Pour lever l’ambigüité il faut utiliser la syntaxe

    -
    -git checkout ./pipo
    -
    +
    -

    Oui, bon… Voilà, voilà, voilà….

    +
    git checkout ./pipo
    +
    -

    Ça marche, mais ce n’est pas très convivial. D’autant plus que le mot clé checkout signifiait sous CVS et SVN l’opération pour récupérer un projet distant.

    +
    -

    Là où la différence se creuse c’est avec la terminologie Bazaar qui est bien plus naturelle. Car il n’y a pas de commande pour changer de branche, puisqu’il y a une branche par répertoire. Ainsi, pour changer de branche, il suffit de faire cd path/to/branch. Et pour revenir en arrière :

    +

    Oui, bon… Voilà, voilà, voilà….

    -
    -bzr revert pipo
    -
    +

    Ça marche, mais ce n’est pas très convivial. D’autant plus que le mot clé checkout signifiait sous CVS et SVN l’opération pour récupérer un projet distant.

    -

    De plus, la plupart des commandes bazaar prennent en paramètre un numéro de révision, par exemple pour revenir 3 versions précédentes il suffit d’écrire :

    +

    Là où la différence se creuse c’est avec la terminologie Bazaar qui est bien plus naturelle. Car il n’y a pas de commande pour changer de branche, puisqu’il y a une branche par répertoire. Ainsi, pour changer de branche, il suffit de faire cd path/to/branch. Et pour revenir en arrière :

    -
    -bzr revert -r -3 pipo
    -
    +
    -

    L’équivalent sous git est beaucoup plus cryptique :

    +
    bzr revert pipo
    +
    -
    -bzr checkout HEAD~3 pipo
    -
    +
    + +

    De plus, la plupart des commandes bazaar prennent en paramètre un numéro de révision, par exemple pour revenir 3 versions précédentes il suffit d’écrire :

    + +
    + +
    bzr revert -r -3 pipo
    +
    + +
    + +

    L’équivalent sous git est beaucoup plus cryptique :

    + +
    + +
    bzr checkout HEAD~3 pipo
    +
    + +

    Encore un fois, Bazaar est bien plus lisible.

    @@ -131,71 +152,88 @@ bzr checkout HEAD~3 pipo

    avec Bazaar :

    -
    -bzr revert -r -3 pipo
    -
    +
    -

    et avec git ? git checkout ? Bien sûr que non voyons ! Ce serait bien trop simple. Ce que l’on trouve dans les forums c’est :

    +
    bzr revert -r -3 pipo
    +
    -
    -git reset --hard HEAD~3
    -
    +
    -

    Sauf que cette syntaxe est horrible. Elle oublie ‘réellement’ les révisions. Il faut donc l’utiliser avec prudence. Mais en effet, je conseillerai plutôt :

    +

    et avec git ? git checkout ? Bien sûr que non voyons ! Ce serait bien trop simple. Ce que l’on trouve dans les forums c’est :

    -
    -git checkout HEAD~3 -- . && git commit -m 'back in time'
    -
    +
    -

    Histoire d’avoir la branche backup sous la main, car sinon, on risque de perdre définitivement la version courante de HEAD. Qui ramène la branche locale à ce point. Mais il reste des erreur s’il y a eu des ajouts de fichier entre temps. Le seul et l’unique vraiment propre de revenir en arrière dans git c’est de lancer la commande suivante :

    +
    git reset --hard HEAD~3
    +
    -
    -for i in $(seq 0 2); do 
    -    git revert -n --no-edit head~$i; 
    -done
    -git commit -m "reverted 3 versions back"
    -
    +
    -

    ce qui signifie sur un système UNIX en zsh (ou bash) faire git revert de toutes les dernières versions. Même si quelqu’un d’autre à fait un pull de vos modification intermédiaire il ne sera pas embêté et il sera au courant de ce qu’il s’est passé.

    +

    Sauf que cette syntaxe est horrible. Elle oublie ‘réellement’ les révisions. Il faut donc l’utiliser avec prudence. Mais en effet, je conseillerai plutôt :

    -

    La règle est simple : Ne JAMAIS utiliser la commande git reset avec une version que d’autres personnes auraient pu avoir fetcher.

    +
    -

    Voilà, c’est dit. Découvrir ça m’a pris pas mal de temps, avec plein d’essai de tous les cotés. Le plus sûr reste toujours la méthode vue plus haut. Si vous souhaitez automatiser cela, le plus simple est d’ajouter l’alias suivant à votre fichier ~/.gitconfig. Bien sûr l’alias ne fonctionnera que sur les environnement possédant zsh, ce qui est le cas de la plupart des environnements UNIX (Ubuntu, Mac OS X…).

    +
    git checkout HEAD~3 -- . && git commit -m 'back in time'
    +
    -
    -
    -[alias]
    -    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    -
    -
    +
    -

    Ce qui fait que git est le meilleur DCVS jusqu’à aujourd’hui

    +

    Histoire d’avoir la branche backup sous la main, car sinon, on risque de perdre définitivement la version courante de HEAD. Qui ramène la branche locale à ce point. Mais il reste des erreur s’il y a eu des ajouts de fichier entre temps. Le seul et l’unique vraiment propre de revenir en arrière dans git c’est de lancer la commande suivante :

    + +
    + +
    for i in $(seq 0 2); do 
    +    git revert -n --no-edit head~$i; 
    +done
    +git commit -m "reverted 3 versions back"
    +
    + +
    + +

    ce qui signifie sur un système UNIX en zsh (ou bash) faire git revert de toutes les dernières versions. Même si quelqu’un d’autre à fait un pull de vos modification intermédiaire il ne sera pas embêté et il sera au courant de ce qu’il s’est passé.

    + +

    La règle est simple : Ne JAMAIS utiliser la commande git reset avec une version que d’autres personnes auraient pu avoir fetcher.

    + +

    Voilà, c’est dit. Découvrir ça m’a pris pas mal de temps, avec plein d’essai de tous les cotés. Le plus sûr reste toujours la méthode vue plus haut. Si vous souhaitez automatiser cela, le plus simple est d’ajouter l’alias suivant à votre fichier ~/.gitconfig. Bien sûr l’alias ne fonctionnera que sur les environnement possédant zsh, ce qui est le cas de la plupart des environnements UNIX (Ubuntu, Mac OS X…).

    + +
    + + +
    [alias]
    +    uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
    +
    + +
    + +

    Ce qui fait que git est le meilleur DCVS jusqu’à aujourd’hui

    Après avoir énoncé les cotés négatifs (et je les trouve nombreux) de git. Voici les cotés positifs qui a eux seul valent la peine de se coltiner tous les problèmes inhérent à git.

    Cheap branching

    -

    Vous travaillez toujours dans le même répertoire principal. Par exemple, vous pouvez travailler sur deux corrections de bug. Disons fix1 et fix2 nécessitant la modification respective de file1 et file2. Vous pouvez travailler dans n’importe quel ordre sur vos deux fichiers dans la branche master. Puis, une fois votre travail fini. Aller dans la branche fix1 pour commiter file1. Puis aller dans la branche fix2 pour commiter file2. Et enfin, merger les deux branches dans master.

    +

    Vous travaillez toujours dans le même répertoire principal. Par exemple, vous pouvez travailler sur deux corrections de bug. Disons fix1 et fix2 nécessitant la modification respective de file1 et file2. Vous pouvez travailler dans n’importe quel ordre sur vos deux fichiers dans la branche master. Puis, une fois votre travail fini. Aller dans la branche fix1 pour commiter file1. Puis aller dans la branche fix2 pour commiter file2. Et enfin, merger les deux branches dans master.

    -
    -> vim file1
    -> vim file2
    -> git br fix1
    -> git add file1 
    -> git commit -m 'fix1'
    -> git br fix2
    -> git add file2
    -> git commit -m 'fix2'
    -> git commit master
    -> git merge fix1
    -> git merge fix2
    -
    +
    -

    Et il est vraiment très agréable de ne pas se soucier d’être dans la bonne branche. Vous n’avez à vous occuper que de votre code et seulement ensuite vous occuper du système de version.

    +
    > vim file1
    +> vim file2
    +> git br fix1
    +> git add file1 
    +> git commit -m 'fix1'
    +> git br fix2
    +> git add file2
    +> git commit -m 'fix2'
    +> git commit master
    +> git merge fix1
    +> git merge fix2
    +
    -

    Sous Bazaar, il m’est souvent arriver de coder dans la mauvaise branche. Pour récupérer le coup, on doit copier les modifications du fichier dans la bonne branche et faire un revert sur le fichier en question, puis aller dans la bonne branche pour commiter les modifications. Enfin, la plupart du temps, je me trompe de branche et puis tant pis, je merge les deux tout en sachant que c’est sale.

    +
    -

    C’est pourquoi je préfère utiliser git. Si Bazaar venait à implémenter ce système de cheap branching, je le replacerai certainement en tête.

    +

    Et il est vraiment très agréable de ne pas se soucier d’être dans la bonne branche. Vous n’avez à vous occuper que de votre code et seulement ensuite vous occuper du système de version.

    + +

    Sous Bazaar, il m’est souvent arriver de coder dans la mauvaise branche. Pour récupérer le coup, on doit copier les modifications du fichier dans la bonne branche et faire un revert sur le fichier en question, puis aller dans la bonne branche pour commiter les modifications. Enfin, la plupart du temps, je me trompe de branche et puis tant pis, je merge les deux tout en sachant que c’est sale.

    + +

    C’est pourquoi je préfère utiliser git. Si Bazaar venait à implémenter ce système de cheap branching, je le replacerai certainement en tête.

    diff --git a/output/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html b/output/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html index e19333ee2..fb9833aaf 100644 --- a/output/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html +++ b/output/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,11 +57,14 @@
    -

    Je viens de trouver le moyen de changer son shell par défaut sous Mac OS X. Cette note est plus pour moi. Mais elle peut aussi servir à quelqu’un d’autre. Il suffit de lancer la commande :

    +

    Je viens de trouver le moyen de changer son shell par défaut sous Mac OS X. Cette note est plus pour moi. Mais elle peut aussi servir à quelqu’un d’autre. Il suffit de lancer la commande :

    -
    -> chsh
    -
    +
    + +
    > chsh
    +
    + +
    diff --git a/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf b/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf index cbbcb2c5e..f1a5dfc6d 100644 --- a/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf +++ b/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/code/local.conf @@ -1,5 +1,4 @@ - diff --git a/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html b/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html index 2d562ee14..3e8c54f01 100644 --- a/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html +++ b/output/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,67 +62,69 @@

    modifiez le fichier /etc/fonts/local.conf en y incluant le contenu suivant :

    -
    -
    +
     
    -<?xml version="1.0"?>
    -<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
    -<fontconfig>
     
    -<!-- Miscellaneous settings -->
    +
    
    +<?xml version="1.0"?>
    +<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
    +<fontconfig>
     
    -<include ignore_missing="yes">misc.conf</include>
    +<!-- Miscellaneous settings -->
     
    -<!-- Define alias -->
    +<include ignore_missing="yes">misc.conf</include>
     
    -<include ignore_missing="yes">alias.conf</include>
    +<!-- Define alias -->
     
    -<!-- Rules for Microsoft fonts -->
    +<include ignore_missing="yes">alias.conf</include>
     
    -<include ignore_missing="yes">msfonts-rules.conf</include>
    +<!-- Rules for Microsoft fonts -->
     
    -  <match target="pattern" name="family" >
    -      <test name="family" qual="any" >
    -          <string>Tahoma</string>
    -      </test>
    -      <edit mode="assign" name="family" >
    -          <string>Verdana</string>
    -      </edit>
    -  </match>
    -  <selectfont>
    -      <acceptfont>
    -          <pattern>
    -              <patelt name="family"> 
    -                <string>Lucida Grande</string> 
    -              </patelt>
    -          </pattern>
    -      </acceptfont>
    -  </selectfont>
    +<include ignore_missing="yes">msfonts-rules.conf</include>
    +
    +  <match target="pattern" name="family" >
    +      <test name="family" qual="any" >
    +          <string>Tahoma</string>
    +      </test>
    +      <edit mode="assign" name="family" >
    +          <string>Verdana</string>
    +      </edit>
    +  </match>
    +  <selectfont>
    +      <acceptfont>
    +          <pattern>
    +              <patelt name="family"> 
    +                <string>Lucida Grande</string> 
    +              </patelt>
    +          </pattern>
    +      </acceptfont>
    +  </selectfont>
    +
    +  <match target="pattern" name="family" >
    +      <test name="family" qual="any" >
    +          <string>Georgia</string>
    +      </test>
    +      <edit mode="assign" name="family" >
    +          <string>Georgia</string>
    +      </edit>
    +  </match>
    +  <selectfont>
    +      <acceptfont>
    +          <pattern>
    +              <patelt name="family"> 
    +                <string>Century Schoolbook L</string> 
    +              </patelt>
    +          </pattern>
    +      </acceptfont>
    +  </selectfont>
    +
    +</fontconfig>
    +
    - <match target="pattern" name="family" > - <test name="family" qual="any" > - <string>Georgia</string> - </test> - <edit mode="assign" name="family" > - <string>Georgia</string> - </edit> - </match> - <selectfont> - <acceptfont> - <pattern> - <patelt name="family"> - <string>Century Schoolbook L</string> - </patelt> - </pattern> - </acceptfont> - </selectfont> -</fontconfig> -
    -
    -

    J’espère que ça a pu aider quelqu’un qui comme moi pleurait en regardant des fontes aussi laides.

    +

    J’espère que ça a pu aider quelqu’un qui comme moi pleurait en regardant des fontes aussi laides.

    diff --git a/output/Scratch/fr/blog/2010-02-15-All-but-something-regexp/index.html b/output/Scratch/fr/blog/2010-02-15-All-but-something-regexp/index.html index 08332c34c..18d45e598 100644 --- a/output/Scratch/fr/blog/2010-02-15-All-but-something-regexp/index.html +++ b/output/Scratch/fr/blog/2010-02-15-All-but-something-regexp/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,72 +59,84 @@

    Parfois vous ne pouvez simplement pas écrire :

    -
    -if str.match(regexp) and 
    -    not str.match(other_regexp)
    +
    + +
    if str.match(regexp) and 
    +    not str.match(other_regexp)
             do_something
    -
    +
    -

    et vous devez obtenir le même comportement avec seulement une expression régulière. Le problème c’est que le complémentaire des régulier n’est pas régulier. Celà peut s’avérer impossible.

    +
    -

    Cependant, pour certaines expressions ce peut être possible. Disons que vous souhaitez matcher toutes les lignes contenant le mot bull, mais que vous ne souhaitez pas matcher bullshit. Voici une façon sympa d’y arriver :

    +

    et vous devez obtenir le même comportement avec seulement une expression régulière. Le problème c’est que le complémentaire des régulier n’est pas régulier. Celà peut s’avérer impossible.

    -
    -# matcher toute les chaines qui 
    -# matchent 'bull' (bullshit compris)
    -/bull/
    +

    Cependant, pour certaines expressions ce peut être possible. Disons que vous souhaitez matcher toutes les lignes contenant le mot bull, mais que vous ne souhaitez pas matcher bullshit. Voici une façon sympa d’y arriver :

    -# matcher toutes les chaines qui -# contiennent 'bull' sauf 'bullshit' -/bull([^s]|$)| -bulls([^h]|$)| -bullsh([^i]|$)| -bullshi([^t]|$)/ +
    -# une autre façon de l'écrire serait -/bull([^s]|$|s([^h]|$)|sh([^i]|$)|shi([^t]|$))/ -
    +
    # matcher toute les chaines qui 
    +# matchent 'bull' (bullshit compris)
    +/bull/
     
    -

    Regardons de plus près. Dans la première ligne, l’expression est : +# matcher toutes les chaines qui +# contiennent 'bull' sauf 'bullshit' +/bull([^s]|$)| +bulls([^h]|$)| +bullsh([^i]|$)| +bullshi([^t]|$)/ + +# une autre façon de l'écrire serait +/bull([^s]|$|s([^h]|$)|sh([^i]|$)|shi([^t]|$))/ +

    + +
    + +

    Regardons de plus près. Dans la première ligne, l’expression est : bull([^s]|$), pourquoi avons nous besoin du $ ? Parce que sans lui, le mot bull ne serait pas matché. Cette expression signifie :

    La chaine finie par bull
    ou,
    -contient bull suivi d’une lettre différente de s.

    +contient bull suivi d’une lettre différente de s.

    -

    Et voilà. J’espère que ça a pu vous aider.

    +

    Et voilà. J’espère que ça a pu vous aider.

    -

    Notez que cette méthode n’est pas toujours la meilleure. Par exemple essayons d’écrire une expression régulière équivalente à l’expression conditionnelle suivante :

    -
    -# Commence avec 'a': ^a
    -# Se finit par 'a': c$
    -# Contient 'b': .*b.*
    -# Mais n'est pas 'axbxc'
    -if str.match(/^a.*b.*c$/) and 
    -        not str.match(/^axbxc$/)
    +

    Notez que cette méthode n’est pas toujours la meilleure. Par exemple essayons d’écrire une expression régulière équivalente à l’expression conditionnelle suivante :

    +
    + +
    # Commence avec 'a': ^a
    +# Se finit par 'a': c$
    +# Contient 'b': .*b.*
    +# Mais n'est pas 'axbxc'
    +if str.match(/^a.*b.*c$/) and 
    +        not str.match(/^axbxc$/)
         do_something
    -end
    -
    +end +
    + +

    Une solution est :

    -
    -/abc|           # longueur 3
    -a.bc|           # longueur 4
    -ab.c|
    -a[^x]b[^x]c|    # longueur 5
    -a...*b.*c|      # longueur >5
    -a.*b...*c/
    -
    +
    -

    Cette solution utilise la longueur maximale de la chaine qui ne doit pas être matchée. Il existe certainement d’autres méthodes. Mais la leçon importante c’est qu’il n’est pas naturel d’exclure des solutions d’un expression régulière.

    +
    /abc|           # longueur 3
    +a.bc|           # longueur 4
    +ab.c|
    +a[^x]b[^x]c|    # longueur 5
    +a...*b.*c|      # longueur >5
    +a.*b...*c/
    +
    + +
    + +

    Cette solution utilise la longueur maximale de la chaine qui ne doit pas être matchée. Il existe certainement d’autres méthodes. Mais la leçon importante c’est qu’il n’est pas naturel d’exclure des solutions d’un expression régulière.


    -

    -Il peut être démontré que tout ensemble régulier privé d’un ensemble fini est aussi régulier. +

    +Il peut être démontré que tout ensemble régulier privé d’un ensemble fini est aussi régulier.

    diff --git a/output/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/index.html b/output/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/index.html index a239ac3cc..12f298c55 100644 --- a/output/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/index.html +++ b/output/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,8 +57,8 @@
    -

    Dans mon précédent article j’ai donné certaines astuces pour matcher ‘tout sauf quelque chose’. De la même manière, un truc pour matcher la chaine de caractère la plus petite possible. -Disons que vous voulez matcher la chaine de caractère entre ‘a’ et ‘b’. Par exemple, vous voulez matcher :

    +

    Dans mon précédent article j’ai donné certaines astuces pour matcher ‘tout sauf quelque chose’. De la même manière, un truc pour matcher la chaine de caractère la plus petite possible. +Disons que vous voulez matcher la chaine de caractère entre ‘a’ et ‘b’. Par exemple, vous voulez matcher :

     a.....a......b..b..a....a....b...
    @@ -68,84 +71,102 @@ a.....a......b..b..a....a.....a......b..b..a....a....b...
     
    -

    La première erreur vient de l’utilisation du terrible .*. Parce que vous allez matcher la chaîne de caractère la plus longue possible.

    +

    La première erreur vient de l’utilisation du terrible .*. Parce que vous allez matcher la chaîne de caractère la plus longue possible.

     /a.*?b/
     a.....a......b..b..a....a....b...
     
    -

    L’autre manière naturelle de répondre à ce problème est de changer la greediness. Mais ce n’est pas assez parce que vous allez matcher du premier a au premier b après celui-ci. On peut alors constater que votre chaine de caractère ne devrait comprendre ni la lettre a ni la lettre b. Ce qui emène à la dernière solution élégante.

    +

    L’autre manière naturelle de répondre à ce problème est de changer la greediness. Mais ce n’est pas assez parce que vous allez matcher du premier a au premier b après celui-ci. On peut alors constater que votre chaine de caractère ne devrait comprendre ni la lettre a ni la lettre b. Ce qui emène à la dernière solution élégante.

     /a[^ab]*b/
     a.....a......b..b..a....a....b...
     
    -

    Jusqu’ici, c’était facile. Maintenant comment fait vous quand au lieu de a vous avez une chaine de caractère ?

    +

    Jusqu’ici, c’était facile. Maintenant comment fait vous quand au lieu de a vous avez une chaine de caractère ?

    Par exemple, vous voulez matcher:

    -
    -<li>...<li>
    -
    +
    -

    C’est un peu difficile. Vous devez matcher

    -
    -<li>[anything not containing <li>]</li>
    -
    +
    <li>...<li>
    +
    -

    La première méthode serait d’utiliser le même rainsonnement que dans mon article précédent. Ici un premier essai :

    +
    -
    -<li>([^<]|<[^l]|<l[^i]|<li[^>])*</li>
    -
    +

    C’est un peu difficile. Vous devez matcher

    +
    + +
    <li>[anything not containing <li>]</li>
    +
    + +
    + +

    La première méthode serait d’utiliser le même rainsonnement que dans mon article précédent. Ici un premier essai :

    + +
    + +
    <li>([^<]|<[^l]|<l[^i]|<li[^>])*</li>
    +
    + +

    Mais il y a encore une erreur. Pensez à la chaine de caractère suivante :

    -
    -<li>...<li</li>
    -
    +
    -

    Cette chaine ne matchera pas. C’est pourquoi si on veut vraiment la matcher correctement nous devons ajouter :

    -
    -<li>([^<]|<[^l]|<l[^i]|<li[^>])*(|<|<l|<li)</li>
    -
    +
    <li>...<li</li>
    +
    -

    Oui, c’est un peu compliqué. Mais que se passe t’il lorsque la chaine de caractère que vous voulez matcher est encore plus longue que <li> ?

    +
    -

    Voici un algorithme qui permet de résoudre ce problème aisément. Vous devez réduire ce problème au premier. C’est-à-dire celui avec une seule lettre :

    +

    Cette chaine ne matchera pas. C’est pourquoi si on veut vraiment la matcher correctement nous devons ajouter :

    +
    -
    -# transforme un simple caractère choisi aléatoirement
    -# en un identifiant unique
    -# (vous devez vérifier que l'identifier est VRAIMENT unique)
    -# attention l'identifiant unique ne doit pas 
    -# contenir le caractère choisi.
    -s/X/_was_x_/g
    -s/Y/_was_y_/g
    +
    <li>([^<]|<[^l]|<l[^i]|<li[^>])*(|<|<l|<li)</li>
    +
    -# transforme la longue chaine de caractère -# en un seul caractère -s/<li>/X/g -s/<\/li>/Y/g +
    -# Utilisation de la première méthode -s/X([^X]*)Y//g +

    Oui, c’est un peu compliqué. Mais que se passe t’il lorsque la chaine de caractère que vous voulez matcher est encore plus longue que <li> ?

    -# Retransformation des lettres en chaines -# de caractères -s/X/<li>/g -s/Y/<\/li>/g +

    Voici un algorithme qui permet de résoudre ce problème aisément. Vous devez réduire ce problème au premier. C’est-à-dire celui avec une seule lettre :

    -# retour des anciens caractères. -s/_was_x_/X/g -s/_was_y_/Y/g -
    +
    + +
    # transforme un simple caractère choisi aléatoirement
    +# en un identifiant unique
    +# (vous devez vérifier que l'identifier est VRAIMENT unique)
    +# attention l'identifiant unique ne doit pas 
    +# contenir le caractère choisi.
    +s/X/_was_x_/g
    +s/Y/_was_y_/g
    +
    +# transforme la longue chaine de caractère
    +# en un seul caractère
    +s/<li>/X/g
    +s/<\/li>/Y/g
    +
    +# Utilisation de la première méthode
    +s/X([^X]*)Y//g
    +
    +# Retransformation des lettres en chaines
    +# de caractères
    +s/X/<li>/g
    +s/Y/<\/li>/g
    +
    +# retour des anciens caractères.
    +s/_was_x_/X/g
    +s/_was_y_/Y/g
    +
    + +

    Et ça fonctionne en seulement 9 lignes pour toute chaine de début et de fin. -Cette solution fait un peu moins I AM THE GREAT REGEXP M45T3R, URAN00B, mais elle est mieux adaptée à mon avis. De plus, utiliser cette dernière solution prouve que vous maitrisez les expressions régulières. Simplement parce que vous savez qu’il est difficile de résoudre des problèmes de cette forme en utilisant seulement des expressions régulières.

    +Cette solution fait un peu moins I AM THE GREAT REGEXP M45T3R, URAN00B, mais elle est mieux adaptée à mon avis. De plus, utiliser cette dernière solution prouve que vous maitrisez les expressions régulières. Simplement parce que vous savez qu’il est difficile de résoudre des problèmes de cette forme en utilisant seulement des expressions régulières.


    -

    Je sais que j’ai utilisé une syntaxe HTML dans mon exemple. Mais dans l’utilisation réelle que j’en ai faite, je devais matcher entre en: et ::, sachant que parfois les chaines pouvaient se terminer par e::.

    +

    Je sais que j’ai utilisé une syntaxe HTML dans mon exemple. Mais dans l’utilisation réelle que j’en ai faite, je devais matcher entre en: et ::, sachant que parfois les chaines pouvaient se terminer par e::.

    diff --git a/output/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/index.html b/output/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/index.html index 8f1939054..29753c2c9 100644 --- a/output/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/index.html +++ b/output/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,41 +57,47 @@
    -

    Assez bizarrement, je n’ai trouvé aucun outil UNIX pour découper un fichier par mot clé. -Alors j’en ai fait un en awk. Je le met ici principalement pour moi, mais ça peut toujours servir à quelqu’un d’autre. +

    Assez bizarrement, je n’ai trouvé aucun outil UNIX pour découper un fichier par mot clé. +Alors j’en ai fait un en awk. Je le met ici principalement pour moi, mais ça peut toujours servir à quelqu’un d’autre. Le code suivant découpe un fichier pour chacune de ses ligne contenant le mot UTC.

    -
    -#!/usr/bin/env awk
    -BEGIN{i=0;}
    -/UTC/ { 
    -    i+=1;
    -    FIC=sprintf("fic.%03d",i); 
    -} 
    -{print $0>>FIC}
    -
    +
    -

    En réalité, j’avais besoin de cet outils pour avoir un fichier par jour. Chaque ligne contenant UTC ayant le format suivant :

    +
    #!/usr/bin/env awk
    +BEGIN{i=0;}
    +/UTC/ { 
    +    i+=1;
    +    FIC=sprintf("fic.%03d",i); 
    +} 
    +{print $0>>FIC}
    +
    + +
    + +

    En réalité, j’avais besoin de cet outils pour avoir un fichier par jour. Chaque ligne contenant UTC ayant le format suivant :

     Mon Dec  7 10:32:30 UTC 2009
     
    -

    J’en suis finallement arrivé au code suivant :

    +

    J’en suis finallement arrivé au code suivant :

    -
    -#!/usr/bin/env awk
    -BEGIN{i=0;}
    -/UTC/ {
    -    date=$1$2$3; 
    -    if ( date != olddate ) {
    +
    + +
    #!/usr/bin/env awk
    +BEGIN{i=0;}
    +/UTC/ {
    +    date=$1$2$3; 
    +    if ( date != olddate ) {
             olddate=date;
             i+=1;
    -        FIC=sprintf("fic.%03d",i); 
    +        FIC=sprintf("fic.%03d",i); 
         }
     } 
    -{print $0>>FIC}
    -
    +{print $0>>FIC} +
    + +
    diff --git a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb index 33e9e5580..1770c6aa5 100644 --- a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb +++ b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_ext.rb @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby require 'benchmark' n=80000 diff --git a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb index f99b07a97..e41a21361 100644 --- a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb +++ b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/code/regex_benchmark_strip.rb @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby require 'benchmark' n=80000 diff --git a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html index 231733225..62ce14766 100644 --- a/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html +++ b/output/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,55 +57,60 @@
    -

    Les expressions régulières sont très utiles. Cependant, elles ne sont pas toujours la meilleure manière d’aborder certain problème autour des chaines de caractères. +

    Les expressions régulières sont très utiles. Cependant, elles ne sont pas toujours la meilleure manière d’aborder certain problème autour des chaines de caractères. Et surtout quand les transformations que vous voulez accomplir sont simples.

    -

    Je voulais savoir comment récupérer le plus vite possible l’extension d’un nom de fichier. Il y a trois manière naturelle d’accomplir celà :

    +

    Je voulais savoir comment récupérer le plus vite possible l’extension d’un nom de fichier. Il y a trois manière naturelle d’accomplir celà :

    -
    -# regexp
    -str.match(/[^.]*$/); 
    -ext=$&
    +
    -# split -ext=str.split('.')[-1] +
    # regexp
    +str.match(/[^.]*$/); 
    +ext=$&
     
    -# File module
    -ext=File.extname(str)
    -
    +# split +ext=str.split('.')[-1] -

    A première vue, je pensais que l’expression régulière serait plus rapide que le split parce qu’il pouvait y avoir plusieurs de . dans un nom de fichier. Mais la majorité du temps il n’y a qu’un seul point par nom de fichier. C’est pourquoi j’ai réalisé que le split serait plus rapide. Mais pas le plus rapide possible. Il y a une fonction qui est dédiée à faire ce travail dans un module standard de ruby ; le module File.

    +# File module +ext=File.extname(str) +
    + +
    + +

    A première vue, je pensais que l’expression régulière serait plus rapide que le split parce qu’il pouvait y avoir plusieurs de . dans un nom de fichier. Mais la majorité du temps il n’y a qu’un seul point par nom de fichier. C’est pourquoi j’ai réalisé que le split serait plus rapide. Mais pas le plus rapide possible. Il y a une fonction qui est dédiée à faire ce travail dans un module standard de ruby ; le module File.

    Voici le code pour faire un benchmark :

    -
    -
    -#!/usr/bin/env ruby
    -require 'benchmark'
    -n=80000
    -tab=[ '/accounts/user.json',
    -      '/accounts/user.xml',
    -      '/user/titi/blog/toto.json',
    -      '/user/titi/blog/toto.xml' ]
    +
    -puts "Get extname" -Benchmark.bm do |x| - x.report("regexp:") { n.times do - str=tab[rand(4)]; - str.match(/[^.]*$/); - ext=$&; - end } - x.report(" split:") { n.times do - str=tab[rand(4)]; - ext=str.split('.')[-1] ; - end } - x.report(" File:") { n.times do - str=tab[rand(4)]; - ext=File.extname(str); - end } -end -
    -
    + +
    #!/usr/bin/env ruby
    +require 'benchmark'
    +n=80000
    +tab=[ '/accounts/user.json',
    +      '/accounts/user.xml',
    +      '/user/titi/blog/toto.json',
    +      '/user/titi/blog/toto.xml' ]
    +
    +puts "Get extname"
    +Benchmark.bm do |x|
    +    x.report("regexp:") { n.times do 
    +        str=tab[rand(4)]; 
    +        str.match(/[^.]*$/); 
    +        ext=$&; 
    +    end  }
    +    x.report(" split:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=str.split('.')[-1] ; 
    +    end }
    +    x.report("  File:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=File.extname(str); 
    +    end  }
    +end
    +
    + +

    Et voici les résultats :

    @@ -116,32 +124,34 @@ regexp: 2.550000 0.020000 2.570000 ( 2.693407)

    En conclusion, les fonction dédiées sont meilleures que votre façon de faire (la plupart du temps).

    -

    Chemin complet d’un fichier sans l’extension

    +

    Chemin complet d’un fichier sans l’extension

    -
    -
    -#!/usr/bin/env ruby
    -require 'benchmark'
    -n=80000
    -tab=[ '/accounts/user.json',
    -      '/accounts/user.xml',
    -      '/user/titi/blog/toto.json',
    -      '/user/titi/blog/toto.xml' ]
    +
    -puts "remove extension" -Benchmark.bm do |x| - x.report(" File:") { n.times do - str=tab[rand(4)]; - path=File.expand_path(str,File.basename(str,File.extname(str))); - end } - x.report("chomp:") { n.times do - str=tab[rand(4)]; - ext=File.extname(str); - path=str.chomp(ext); - end } -end -
    -
    + +
    #!/usr/bin/env ruby
    +require 'benchmark'
    +n=80000
    +tab=[ '/accounts/user.json',
    +      '/accounts/user.xml',
    +      '/user/titi/blog/toto.json',
    +      '/user/titi/blog/toto.xml' ]
    +
    +puts "remove extension"
    +Benchmark.bm do |x|
    +    x.report(" File:") { n.times do 
    +        str=tab[rand(4)]; 
    +        path=File.expand_path(str,File.basename(str,File.extname(str))); 
    +    end }
    +    x.report("chomp:") { n.times do 
    +        str=tab[rand(4)]; 
    +        ext=File.extname(str); 
    +        path=str.chomp(ext); 
    +    end }
    +end
    +
    + +

    et voici les résultats :

    @@ -152,7 +162,7 @@ remove extension chomp: 0.820000 0.040000 0.860000 ( 0.947432) -

    En conclusion du ce second benchmark. Un fonction simple est meilleure que trois fonctions dédiées. Pas de surprise, mais c’est toujours bien de savoir.

    +

    En conclusion du ce second benchmark. Un fonction simple est meilleure que trois fonctions dédiées. Pas de surprise, mais c’est toujours bien de savoir.

    diff --git a/output/Scratch/fr/blog/2010-03-22-Git-Tips/index.html b/output/Scratch/fr/blog/2010-03-22-Git-Tips/index.html index d9c42a379..0a17d6776 100644 --- a/output/Scratch/fr/blog/2010-03-22-Git-Tips/index.html +++ b/output/Scratch/fr/blog/2010-03-22-Git-Tips/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,60 +61,75 @@

    La façon standard:

    -
    -git clone git@github.com:yogsototh/project.git
    -
    +
    + +
    git clone git@github.com:yogsototh/project.git
    +
    + +

    En utilisant le port HTTPS :

    -
    -git clone git+ssh://git@github.com:443/yogsototh/project.git
    -
    +
    + +
    git clone git+ssh://git@github.com:443/yogsototh/project.git
    +
    + +

    Cloner toutes les branches

    git clone peut seulement récuper la branche master.

    -

    Si vous n’avez pas beaucoup de branches, vous pouvez simplement les clone le project et ensuite pour chacune d’entre elle lancer la commande suivante :

    +

    Si vous n’avez pas beaucoup de branches, vous pouvez simplement les clone le project et ensuite pour chacune d’entre elle lancer la commande suivante :

    -
    -git branch --track local_branch remote_branch
    -
    +
    + +
    git branch --track local_branch remote_branch
    +
    + +

    par exemple :

    -
    -$ git clone git@github:yogsototh/example.git
    +
    + +
    $ git clone git@github:yogsototh/example.git
     $ git branch
     master *
     $ git branch -a
     master *
    -remotes/origin/HEAD -> origin/master
    +remotes/origin/HEAD -> origin/master
     remotes/origin/experimental
     $ git branch --track experimental remotes/origin/experimental
     $ git branch
     master *
     experimental
    -
    +
    -

    Si vous avez beaucoup de branches il peut être utile d’utiliser le script/la longue ligne de commande suivant(e) :

    +
    -
    -# first clone your project
    +

    Si vous avez beaucoup de branches il peut être utile d’utiliser le script/la longue ligne de commande suivant(e) :

    + +
    + +
    # first clone your project
     $ git clone git@github.com:yogsototh/project.git
     
    -# copy all branches
    +# copy all branches
     $ zsh
     $ cd project
    -$ for br in $( git br -a ); do
    -    case $br in
    -    remotes/*) print $br ; 
    -        case ${br:t} in
    -            master|HEAD) continue ;;
    -            *) git branch --track ${br:t} $br ;;
    -        esac ;;
    -    esac
    -done
    -
    +$ for br in $( git br -a ); do + case $br in + remotes/*) print $br ; + case ${br:t} in + master|HEAD) continue ;; + *) git branch --track ${br:t} $br ;; + esac ;; + esac +done +
    + +

    Et toutes les branches seront récupérées en local.

    diff --git a/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/code/eng b/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/code/eng index 3cb5eded6..192aae632 100644 --- a/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/code/eng +++ b/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/code/eng @@ -1,4 +1,3 @@ - #!/usr/bin/env ruby # encoding: utf-8 diff --git a/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/index.html b/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/index.html index 33b3027a9..8bd810934 100644 --- a/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/index.html +++ b/output/Scratch/fr/blog/2010-03-23-Encapsulate-git/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,16 +59,16 @@

    Voici une solution pour conserver des branches divergentes avec git. -Parce qu’il est facile de merger par erreur, je propose un script qui encapsule le comportement de git pour interdire certains merges dangereux. Mais qui permet aussi de faire des merges en cascades de la racines vers les autres branches. +Parce qu’il est facile de merger par erreur, je propose un script qui encapsule le comportement de git pour interdire certains merges dangereux. Mais qui permet aussi de faire des merges en cascades de la racines vers les autres branches.

    Se prémunir contre les erreurs

    -

    Je travaille sur un projet dans lequel certaines de mes branches git doivent rester divergentes. Et les divergences devraient aller en s’accentuant.

    +

    Je travaille sur un projet dans lequel certaines de mes branches git doivent rester divergentes. Et les divergences devraient aller en s’accentuant.

    -

    J’utilise aussi certaines branches qui contiennent la partie commune de ces projets.

    +

    J’utilise aussi certaines branches qui contiennent la partie commune de ces projets.

    -

    Disons que j’ai les branches :

    +

    Disons que j’ai les branches :

    • master: commune à toutes les branches
    • @@ -83,142 +86,150 @@ Parce qu’il est facile de merger par erreur, je propose un script

      Branch hierarchy

      -

      Une flèche de A vers B signifie que l’on peut merger A dans B. S’il n’y a pas de flèche de A vers B cela signifie qu’il est interdit de merger A dans B. Voici le code ruby correspondant :

      +

      Une flèche de A vers B signifie que l’on peut merger A dans B. S’il n’y a pas de flèche de A vers B cela signifie qu’il est interdit de merger A dans B. Voici le code ruby correspondant :

      -
      -$architecture={ 
      -    :master => [ :dev, :client ],
      -    :dev => [ :master ],
      -    :client => [ :clientA, :clientB ] }
      -
      +
      -

      :master => [ :dev, :client ] signifie que l’on peut merger la branche master dans la branche dev et la branche client.

      +
      $architecture={ 
      +    :master => [ :dev, :client ],
      +    :dev => [ :master ],
      +    :client => [ :clientA, :clientB ] }
      +
      + +
      + +

      :master => [ :dev, :client ] signifie que l’on peut merger la branche master dans la branche dev et la branche client.

      Je fait une erreur si je tape git checkout master && git merge clientA. -C’est pour éviter ça que j’ai fait un script pour encapsuler le comportement de git.

      +C’est pour éviter ça que j’ai fait un script pour encapsuler le comportement de git.

      -

      Mais ce script fait bien plus que ça. Il fait des merges en cascade de la racine vers les feuilles avec l’acion allmerges.

      +

      Mais ce script fait bien plus que ça. Il fait des merges en cascade de la racine vers les feuilles avec l’acion allmerges.

      -
      -git co dev && git merge master
      -git co client && git merge master
      -git co clientA && git merge client
      -git co clientB && git merge client
      -
      +
      -

      Je peux ainsi mettre à jour toutes les branches. L’algorithme ne boucle pas même s’il y a des cycles dans la structure des branches.
      +

      git co dev && git merge master
      +git co client && git merge master
      +git co clientA && git merge client
      +git co clientB && git merge client
      +
      + +
      + +

      Je peux ainsi mettre à jour toutes les branches. L’algorithme ne boucle pas même s’il y a des cycles dans la structure des branches.
      Le voici :

      -
      -
      -#!/usr/bin/env ruby
      -# encoding: utf-8
      +
      -# architecture -# -# master <-> dev -# master -> client -# clien -> clientA | clientB -# -# merge using two of these branches should be -# restricted to these rules -# merge to one of these branch and an unknown one should -# raise a warning, and may the option to add this new branch -# to the hierarchy -$architecture={ - :master => [ :dev, :client ], - :dev => [ :master ], - :client => [ :clientA, :clientB ] } +
      #!/usr/bin/env ruby
      +# encoding: utf-8
       
      -def get_current_branch()
      -    (`git branch --no-color | awk '$1 == "*" {print $2}'`).chop.intern
      -end
      +# architecture
      +#
      +# master <-> dev
      +# master -> client
      +# clien -> clientA | clientB
      +#
      +# merge using two of these branches should be 
      +#   restricted to these rules
      +# merge to one of these branch and an unknown one should
      +#   raise a warning, and may the option to add this new branch
      +#   to the hierarchy
       
      -if ARGV.length == 0
      -    puts %{usage: $0:t [git_command or local_command]
      -    
      -local commands:
      -    allmerges: merge from top to down}
      -    exit 0
      -end
      +$architecture={ 
      +    :master => [ :dev, :client ],
      +    :dev => [ :master ],
      +    :client => [ :clientA, :clientB ] }
       
      -require 'set'
      -$known_branches=Set.new
      -$architecture.each do |k,v| 
      -    $known_branches.add(k)
      -    v.each { |b| $known_branches.add(b) }
      -end
      +def get_current_branch()
      +    (`git branch --no-color | awk '$1 == "*" {print $2}'`).chop.intern
      +end
       
      -def rec_merge(branch)
      -    if $architecture[branch].nil?
      -        return
      -    end
      -    $architecture[branch].each do |b|
      -        if $flag.has_key?(b.to_s + branch.to_s)
      -            next
      -        end
      -        flagname=branch.to_s + b.to_s
      -        if $flag.has_key?(flagname)
      -            next
      -        end
      -        if system %{eng checkout #{b}}
      -            if get_current_branch != b
      -                puts "Can't checkout to #{b}"
      -                exit 2
      -            end
      -            if system %{eng merge #{branch}}
      -                $flag[flagname]=true
      -                rec_merge(b)
      -            else
      -                exit 1
      -            end
      -        else
      -            exit 1
      -        end
      -    end
      -end
      +if ARGV.length == 0
      +    puts %{usage: $0:t [git_command or local_command]
      +    
      +local commands:
      +    allmerges: merge from top to down}
      +    exit 0
      +end
       
      -def do_all_merges
      -    puts 'Will merge from father to sons'
      -    current_branch=get_current_branch
      -    $flag={}
      -    rec_merge(:master)
      -    system %{git co #{current_branch}}
      -end
      +require 'set'
      +$known_branches=Set.new
      +$architecture.each do |k,v| 
      +    $known_branches.add(k)
      +    v.each { |b| $known_branches.add(b) }
      +end
       
      -def do_merge
      -    current_branch=get_current_branch
      -    src_branch=ARGV[1].intern
      -    puts %{do_merge: #{src_branch} => #{current_branch}}
      -    if $known_branches.include?(current_branch)
      -        if $known_branches.include?(src_branch)
      -            if $architecture.has_key?(src_branch) and 
      -                $architecture[src_branch].include?(current_branch)
      -                system %{git merge #{src_branch}}
      -            else
      -                puts %{Forbidden merge: #{src_branch} => #{current_branch}}
      -            end
      -        else
      -            puts %{Warning! #{src_branch} not mentionned in rb configuration}
      -            sleep 2
      -            f system %{git merge #{src_branch}}
      -            puts %{Warning! #{src_branch} not mentionned in rb configuration}
      -        end
      -    end
      -end
      +def rec_merge(branch)
      +    if $architecture[branch].nil?
      +        return
      +    end
      +    $architecture[branch].each do |b|
      +        if $flag.has_key?(b.to_s + branch.to_s)
      +            next
      +        end
      +        flagname=branch.to_s + b.to_s
      +        if $flag.has_key?(flagname)
      +            next
      +        end
      +        if system %{eng checkout #{b}}
      +            if get_current_branch != b
      +                puts "Can't checkout to #{b}"
      +                exit 2
      +            end
      +            if system %{eng merge #{branch}}
      +                $flag[flagname]=true
      +                rec_merge(b)
      +            else
      +                exit 1
      +            end
      +        else
      +            exit 1
      +        end
      +    end
      +end
       
      -case ARGV[0] 
      -    when 'allmerges' then do_all_merges
      -    when 'merge' then do_merge
      -    else system %{git #{ARGV.join(' ')}}
      -end
      -
      -
      +def do_all_merges + puts 'Will merge from father to sons' + current_branch=get_current_branch + $flag={} + rec_merge(:master) + system %{git co #{current_branch}} +end + +def do_merge + current_branch=get_current_branch + src_branch=ARGV[1].intern + puts %{do_merge: #{src_branch} => #{current_branch}} + if $known_branches.include?(current_branch) + if $known_branches.include?(src_branch) + if $architecture.has_key?(src_branch) and + $architecture[src_branch].include?(current_branch) + system %{git merge #{src_branch}} + else + puts %{Forbidden merge: #{src_branch} => #{current_branch}} + end + else + puts %{Warning! #{src_branch} not mentionned in rb configuration} + sleep 2 + f system %{git merge #{src_branch}} + puts %{Warning! #{src_branch} not mentionned in rb configuration} + end + end +end + +case ARGV[0] + when 'allmerges' then do_all_merges + when 'merge' then do_merge + else system %{git #{ARGV.join(' ')}} +end + + +

      Pour que ça fonctionne il vous suffit de copier eng dans un répertoire présent dans votre PATH.

      -

      Bien entendu, il faut essayer de faire aussi peu que possible des cherry-pick et des rebase. Ce script a pour but de s’intégrer avec les workflow basé sur les pull et merge.

      +

      Bien entendu, il faut essayer de faire aussi peu que possible des cherry-pick et des rebase. Ce script a pour but de s’intégrer avec les workflow basé sur les pull et merge.

    @@ -335,7 +346,7 @@ Le voici :

    Écrit le : 23/03/2010 - modifié le : 30/05/2011 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/index.html b/output/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/index.html index 01835c7fa..702359e30 100644 --- a/output/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/index.html +++ b/output/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -57,39 +60,39 @@

    Bonjour à tous !

    -

    …plus on retarde quelque chose, plus il devient difficile de s’y mettre…

    +

    …plus on retarde quelque chose, plus il devient difficile de s’y mettre…

    -

    Je devais écrire d’autres articles pour ce blog. J’ai noté plein d’idées dans mes todolist. Mais j’avais pas mal d’autres choses à faire. Et jusqu’ici, j’ai toujours dit «je le ferai plus tard». Ce qui m’a fait agir, c’est la petite réflexion que j’avais lu une fois. -> Arrétez d’écrire des TODO dans votre code est faites le maintenant !
    -> Vous serez surpris de l’efficacité de cette mesure.

    +

    Je devais écrire d’autres articles pour ce blog. J’ai noté plein d’idées dans mes todolist. Mais j’avais pas mal d’autres choses à faire. Et jusqu’ici, j’ai toujours dit «je le ferai plus tard». Ce qui m’a fait agir, c’est la petite réflexion que j’avais lu une fois. +> Arrétez d’écrire des TODO dans votre code est faites le maintenant !
    +> Vous serez surpris de l’efficacité de cette mesure.

    En résumé : > Just do it! ou Juste fait le comme auraient dit les nuls.

    -

    Finallement j’écrirais plus souvent ces derniers temps.

    +

    Finallement j’écrirais plus souvent ces derniers temps.

    -

    Qu’ai-je fait ?

    +

    Qu’ai-je fait ?

    -

    J’ai fini une application web pour gridpocket©.

    +

    J’ai fini une application web pour gridpocket©.

    -

    J’ai aussi fini d’adapter mon site à la dernière version de nanoc. La partie difficile étant d’avoir un système qui permet de gérer plusieurs langues. Je pense que j’écrirai les détails dans un article.

    +

    J’ai aussi fini d’adapter mon site à la dernière version de nanoc. La partie difficile étant d’avoir un système qui permet de gérer plusieurs langues. Je pense que j’écrirai les détails dans un article.

    -

    J’ai aussi une vrai vie. J’ai passé quelques moments agréables avec ma famille et des amis.

    +

    J’ai aussi une vrai vie. J’ai passé quelques moments agréables avec ma famille et des amis.

    -

    J’ai travaillé avec luc sur un framework REST/JSON orienté API écrit en ruby. Il fonctionne assez bien, avec très peu de bug jusqu’ici. Nous espérons pouvoir écrire un tutoriel de todolist. Peut-être en deux ou trois posts. Ce framework n’est pas encore public. Il le deviendra certainement lorsque nous aurons fini d’écrire une application web et que nous aurons fait un joli site pour lui.

    +

    J’ai travaillé avec luc sur un framework REST/JSON orienté API écrit en ruby. Il fonctionne assez bien, avec très peu de bug jusqu’ici. Nous espérons pouvoir écrire un tutoriel de todolist. Peut-être en deux ou trois posts. Ce framework n’est pas encore public. Il le deviendra certainement lorsque nous aurons fini d’écrire une application web et que nous aurons fait un joli site pour lui.

    Finallement voilà un partie des choses que je dois faire :

    • finir de faire un service web public (je pense que ça peut être populaire) ;
    • -
    • finir d’écrire l’application iPhone associée ;
    • +
    • finir d’écrire l’application iPhone associée ;
    • finir de publier notre framework pour créer des services web ;
    • publier des articles sur ce blog ;
    • publier le code source de ce blog sur github.
    -

    Certaines choses ne seront peut-être jamais finie. Mais simplement parce qu’elles ne dépendent pas complètement de moi.

    +

    Certaines choses ne seront peut-être jamais finie. Mais simplement parce qu’elles ne dépendent pas complètement de moi.

    diff --git a/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb b/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb index 1121d4ef1..bc18f6ccd 100644 --- a/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb +++ b/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb @@ -1,4 +1,3 @@ - # repair cutted XML code by closing the tags # work even if the XML is cut into a tag. # example: diff --git a/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html b/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html index 3491bc0fc..450dcd6e4 100644 --- a/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html +++ b/output/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,108 +61,104 @@
    -

    Sur ma page d’accueil vous pouvez voir la liste des mes derniers articles avec le début de ceux-ci. Pour arriver à faire ça, j’ai besoin de couper le code XHTML de mes pages en plein milieu. Il m’a donc fallu trouver un moyen de les réparer.

    +

    Sur ma page d’accueil vous pouvez voir la liste des mes derniers articles avec le début de ceux-ci. Pour arriver à faire ça, j’ai besoin de couper le code XHTML de mes pages en plein milieu. Il m’a donc fallu trouver un moyen de les réparer.

    Prenons un exemple :

    -
    -<div class="corps">
    -    <div class="intro">
    -        <p>Introduction</p>
    -    </div>
    -    <p>The first paragraph</p>
    -    <img src="/img/img.png" alt="an image"/>
    -    <p>Another long paragraph</p>
    -</div>
    -
    +
    <div class="corps">
    +    <div class="intro">
    +        <p>Introduction</p>
    +    </div>
    +    <p>The first paragraph</p>
    +    <img src="/img/img.png" alt="an image"/>
    +    <p>Another long paragraph</p>
    +</div>
    +
    -

    Après avoir coupé, j’obtiens :

    +

    Après avoir coupé, j’obtiens :

    -
    -<div class="corps">
    -    <div class="intro">
    -        <p>Introduction</p>
    -    </div>
    -    <p>The first paragraph</p>
    -    <img src="/img/im
    -
    +
    <div class="corps">
    +    <div class="intro">
    +        <p>Introduction</p>
    +    </div>
    +    <p>The first paragraph</p>
    +    <img src="/img/im
    +
    -

    En plein milieu d’un tag <img> !

    +

    En plein milieu d’un tag <img> !

    -

    En réalité, ce n’est pas si difficile que celà peut paraître au premier abord. Le secret réside dans le fait de comprendre que l’on n’a pas besoin de conserver la structure complète de l’arbre pour le réparer, mais seulement la liste des parents non fermés.

    +

    En réalité, ce n’est pas si difficile que celà peut paraître au premier abord. Le secret réside dans le fait de comprendre que l’on n’a pas besoin de conserver la structure complète de l’arbre pour le réparer, mais seulement la liste des parents non fermés.

    -

    Pour notre exemple, juste après le paragraphe first paragraph nous n’avons qu’à fermer un div pour la classe corps et le XML est réparé. Bien entendu, quand on est dans le cas où un tag est coupé au milieu, on a qu’à remonté juste avant le début de ce tag corrompu.

    +

    Pour notre exemple, juste après le paragraphe first paragraph nous n’avons qu’à fermer un div pour la classe corps et le XML est réparé. Bien entendu, quand on est dans le cas où un tag est coupé au milieu, on a qu’à remonté juste avant le début de ce tag corrompu.

    -

    Donc, tout ce que nous avons à faire, c’est d’enregistrer la liste des parents dans une pile. Supposons que nous traitions le premier exemple complètement. La pile passera par les états suivants :

    +

    Donc, tout ce que nous avons à faire, c’est d’enregistrer la liste des parents dans une pile. Supposons que nous traitions le premier exemple complètement. La pile passera par les états suivants :

    -
    -[]           
    -[div]           <div class="corps">
    -[div, div]          <div class="intro">
    -[div, div, p]           <p>
    +
    []           
    +[div]           <div class="corps">
    +[div, div]          <div class="intro">
    +[div, div, p]           <p>
                                 Introduction
    -[div, div]              </p>
    -[div]               </div>
    -[div, p]            <p>
    +[div, div]              </p>
    +[div]               </div>
    +[div, p]            <p>
                             The first paragraph
    -[div]               </p>
    -[div]               <img src="/img/img.png" alt="an image"/>
    -[div, p]            <p>
    +[div]               </p>
    +[div]               <img src="/img/img.png" alt="an image"/>
    +[div, p]            <p>
                             Another long paragraph
    -[div]               </p>
    -[]              </div>
    -
    +[div] </p> +[] </div> +
    -

    L’algorithme est alors très simple : -<pre class="twilight"> -let res be the XML as a string ; +

    L’algorithme est alors très simple :

    + +
    let res be the XML as a string ; 
     read res and each time you encouter a tag: 
         if it is an opening one: 
             push it to the stack
         else if it is a closing one: 
    -        pop the stack.

    + pop the stack. -

    remove any malformed/cutted tag in the end of res +remove any malformed/cutted tag in the end of res for each tag in the stack, pop it, and write: - res = res + closed tag

    + res = res + closed tag -

    return res -</pre>

    +return res +

    Et res contiend le XML réparé.

    -

    Finallement, voici le code en ruby que j’utilise. La variable xml contient le XML coupé.

    +

    Finallement, voici le code en ruby que j’utilise. La variable xml contient le XML coupé.

    -
    -
    -# repair cutted XML code by closing the tags
    -# work even if the XML is cut into a tag.
    -# example:
    -#    transform '<div> <span> toto </span> <p> hello <a href="http://tur'
    -#    into      '<div> <span> toto </span> <p> hello </p></div>'
    -def repair_xml( xml )
    -    parents=[]
    -    depth=0
    -    xml.scan( %r{<(/?)(\w*)[^>]*(/?)>} ).each do |m|
    -        if m[2] == "/"
    -            next
    -        end
    -        if m[0] == "" 
    -            parents[depth]=m[1]
    -            depth+=1
    -        else
    -            depth-=1
    -        end
    -    end
    -    res=xml.sub(/<[^>]*$/m,'')
    -    depth-=1
    -    depth.downto(0).each { |x| res<<= %{</#{parents[x]}>} }
    +
    +
    +
    # repair cutted XML code by closing the tags
    +# work even if the XML is cut into a tag.
    +# example:
    +#    transform '<div> <span> toto </span> <p> hello <a href="http://tur'
    +#    into      '<div> <span> toto </span> <p> hello </p></div>'
    +def repair_xml( xml )
    +    parents=[]
    +    depth=0
    +    xml.scan( %r{<(/?)(\w*)[^>]*(/?)>} ).each do |m|
    +        if m[2] == "/"
    +            next
    +        end
    +        if m[0] == "" 
    +            parents[depth]=m[1]
    +            depth+=1
    +        else
    +            depth-=1
    +        end
    +    end
    +    res=xml.sub(/<[^>]*$/m,'')
    +    depth-=1
    +    depth.downto(0).each { |x| res<<= %{</#{parents[x]}>} }
         res
    -end
    -
    -
    +end + -

    Je ne sais pas si ce code pourra vous être utile. Par contre le raisonnement pour y parvenir mérite d’être connu.

    +

    Je ne sais pas si ce code pourra vous être utile. Par contre le raisonnement pour y parvenir mérite d’être connu.

    diff --git a/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_destination_tree.png b/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_destination_tree.png index a39289809ee6cbf29f3e2d6df992dfd039caec3b..9bcef924eb488af7685c2de50c9112ff1d23f4b4 100644 GIT binary patch literal 24490 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 X?^|CYk4pl+PwX_^Ws+`u`27C>t0#us literal 24274 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

  • WyRXY97GpBae?U2&| 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 DPqkQr diff --git a/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_source_tree.png b/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/graph/The_source_tree.png index ae8c96b84f9c82adf383ab49126dc54476ad5874..73a4a5dbb546d5fd7493ec725c6a9a4b9c41e12f 100644 GIT binary patch literal 19344 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 EFRlA3^#A|> literal 17763 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 MrlCeD*7nZ-2XKb9S^xk5 diff --git a/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html b/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html index 412470ed6..034516e26 100644 --- a/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html +++ b/output/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -65,13 +68,13 @@

    tlàl :

      -
    • J’ai essayé de programmer un simple filtre ;
    • -
    • J’ai été bloqué pendant deux jours ;
    • -
    • J’ai arrêté de penser comme un robot ;
    • -
    • J’ai utilisé un papier et un stylo ;
    • -
    • J’ai fait un peu de maths ;
    • -
    • J’ai résolu le problème en 10 minutes ;
    • -
    • Conclusion: Pragmatisme n’est pas : «n’utilisez jamais la théorie». +
    • J’ai essayé de programmer un simple filtre ;
    • +
    • J’ai été bloqué pendant deux jours ;
    • +
    • J’ai arrêté de penser comme un robot ;
    • +
    • J’ai utilisé un papier et un stylo ;
    • +
    • J’ai fait un peu de maths ;
    • +
    • J’ai résolu le problème en 10 minutes ;
    • +
    • Conclusion: Pragmatisme n’est pas : «n’utilisez jamais la théorie».
  • @@ -80,25 +83,25 @@

    Résumé (plus long que le tlàl)

    Je devais résoudre un problème à mon travail. Au début cela -semblait assez facile. J’ai donc commencé à programmer -tout de suite. Je suis alors entré dans un cercle infernal d’essais +semblait assez facile. J’ai donc commencé à programmer +tout de suite. Je suis alors entré dans un cercle infernal d’essais et de réparations. Voilà à quoi ressemblait cet étrange état de boucle infini :

    -

    – Plus que ça a réparer et ça devrait être bon.
    - – Très bien, maintenant ça doit marcher.
    - – Oui !!
    - – Ah mince! J’ai oublié ce détail…
    +

    – Plus que ça a réparer et ça devrait être bon.
    + – Très bien, maintenant ça doit marcher.
    + – Oui !!
    + – Ah mince! J’ai oublié ce détail…
    répéter jusqu'à la mort

    Après deux jours à me prendre pour Sisyphe, je me suis arrêté pour repenser le problème. -J’ai pris un stylo et une feuille de papier. Je me suis souvenu de de ce que j’avais appris sur les arbres pendant mon doctorat. +J’ai pris un stylo et une feuille de papier. Je me suis souvenu de de ce que j’avais appris sur les arbres pendant mon doctorat. Finalement, le problème fut résolu en moins de 20 minutes.

    Je pense que la leçon à retenir de cette expérience est de se souvenir que la méthodologie la plus efficace pour résoudre ce problème pragamtique était la méthode théorique. -Ça ne signifie pas que la méthode théorique est toujours la meilleure, mais en tout cas, il ne faut pas l’écarter.

    +Ça ne signifie pas que la méthode théorique est toujours la meilleure, mais en tout cas, il ne faut pas l’écarter.

    @@ -106,67 +109,65 @@ Finalement, le problème fut résolu en moins de 20 minutes.

    -

    L’anecdote

    +

    L’anecdote

    Apparemment 90% des programmeurs sont incapable de programmer une recherche binaire sans faire de bug. -L’algorithme est pourtant connu et facile à comprendre. +L’algorithme est pourtant connu et facile à comprendre. Cependant, il est difficile à programmer sans bug. -J’ai participé à ce concours. +J’ai participé à ce concours. Vous pouvez voir les résultats ici1. -J’ai dû faire face à un problème similaire à mon travail. +J’ai dû faire face à un problème similaire à mon travail. Il paraissait simple au départ. -Transformer un xml d’un format à un autre.

    +Transformer un xml d’un format à un autre.

    Voici le format général du xml source :

    -
    -<rubrique>
    -    <contenu>
    -        <tag1>value1</tag1>
    -        <tag2>value2</tag2>
    +
    <rubrique>
    +    <contenu>
    +        <tag1>value1</tag1>
    +        <tag2>value2</tag2>
             ...
    -    </contenu>
    -    <enfant>
    -        <rubrique>
    +    </contenu>
    +    <enfant>
    +        <rubrique>
                 ...
    -        </rubrique>
    +        </rubrique>
             ...
    -        <rubrique>
    +        <rubrique>
                 ...
    -        </rubrique>
    -    </enfant>
    -</menu>
    -
    + </rubrique> + </enfant> +</menu> +
    -

    et le format d’arrivé est celui-ci :

    +

    et le format d’arrivé est celui-ci :

    -
    -<item name="Menu0">
    -    <value>
    -        <item name="menu">
    -            <value>
    -                <item name="tag1">
    -                    <value>value1</value>
    -                </item>
    -                <item name="tag2">
    -                    <value>value2</value>
    -                </item>
    +
    <item name="Menu0">
    +    <value>
    +        <item name="menu">
    +            <value>
    +                <item name="tag1">
    +                    <value>value1</value>
    +                </item>
    +                <item name="tag2">
    +                    <value>value2</value>
    +                </item>
                     ...
    -                <item name="menu">
    -                    <value>
    +                <item name="menu">
    +                    <value>
                             ...
    -                    </value>
    -                    <value>
    +                    </value>
    +                    <value>
                             ...
    -                    </value>
    -                </item>
    -            </value>
    -        </item>
    -    </value>
    -</item>
    -
    + </value> + </item> + </value> + </item> + </value> +</item> +
    -

    À première vue, cela m’a paru simple. J’étais certain de pouvoir y arriver en me fixant les règles suivantes :

    +

    À première vue, cela m’a paru simple. J’étais certain de pouvoir y arriver en me fixant les règles suivantes :

    1. ne pas utiliser xslt ;
    2. @@ -175,13 +176,13 @@ Transformer un xml d’un format à un autre.

    Vous pouvez essayer si vous le souhaitez. Si vous attaquez ce problème directement en écrivant le programme, ce ne sera certainement pas si simple. -Je peux le dire, parce que c’est ce que j’ai fait. -Et je dois dire que j’ai perdu une journée de travail complète en m’y prenant de la sorte. -En réalité, il y avait pas mal de petits détails dont je ne parle pas qui m’ont induis en erreur et qui m’ont fait perdre encore plus de temps.

    +Je peux le dire, parce que c’est ce que j’ai fait. +Et je dois dire que j’ai perdu une journée de travail complète en m’y prenant de la sorte. +En réalité, il y avait pas mal de petits détails dont je ne parle pas qui m’ont induis en erreur et qui m’ont fait perdre encore plus de temps.

    Pourquoi étais-je incapable de résoudre ce problème si simple en aparence ?

    -

    Voici comment je m’y suis pris :

    +

    Voici comment je m’y suis pris :

    1. Réfléchir
    2. @@ -190,27 +191,27 @@ En réalité, il y avait pas mal de petits détails dont je ne parle pas qui m&r
    3. Vérifier les résultats
    4. Trouver un bug
    5. Résoudre le bug
    6. -
    7. Reprendre à l’étape 3
    8. +
    9. Reprendre à l’étape 3
    -

    Il s’agissait d’une méthode de travail standard pour un ingénieur en informatique. L’erreur venait de la première étape. -J’ai d’abord pensé à comment résoudre le problème mais avec des yeux d’ingéinieur pragmatique. Je me suis simplement dit :

    +

    Il s’agissait d’une méthode de travail standard pour un ingénieur en informatique. L’erreur venait de la première étape. +J’ai d’abord pensé à comment résoudre le problème mais avec des yeux d’ingéinieur pragmatique. Je me suis simplement dit :

    -

    Ça à l’air de pouvoir se résouvre avec un petit script de search&replace en perl +

    Ça à l’air de pouvoir se résouvre avec un petit script de search&replace en perl Commençons à écrire le code maintenant.

    -

    C’est la deuxième phrase qui est complètement fausse. Parce que j’avais mal commencé et que cette méthodologie de travail ne fonctionne pas lorsque l’on part vraiment mal.

    +

    C’est la deuxième phrase qui est complètement fausse. Parce que j’avais mal commencé et que cette méthodologie de travail ne fonctionne pas lorsque l’on part vraiment mal.

    Réfléchir

    -

    Après un certain temps, j’ai arrêté de programmer et je me suis dit : «Maintenant, ça suffit !». -J’ai pris une feuille et un stylo et j’ai commencé à dessiner des arbres.

    +

    Après un certain temps, j’ai arrêté de programmer et je me suis dit : «Maintenant, ça suffit !». +J’ai pris une feuille et un stylo et j’ai commencé à dessiner des arbres.

    -

    J’ai commencer par simplifier un peu en enlevant le maximum de verbiage. -Tout d’abord en renommant <item name="Menu"> par un simple M par exemple. -J’ai obtenu quelque chose comme :

    +

    J’ai commencer par simplifier un peu en enlevant le maximum de verbiage. +Tout d’abord en renommant <item name="Menu"> par un simple M par exemple. +J’ai obtenu quelque chose comme :

    The source tree

    @@ -220,7 +221,7 @@ J’ai obtenu quelque chose comme :

    Puis, je me suis fait la réflexion suivante :

    -

    Dans les distances d’éditions sur les arbres, chaque opération atomique correspond à un simple search and replace sur mon fichier xml source2. +

    Dans les distances d’éditions sur les arbres, chaque opération atomique correspond à un simple search and replace sur mon fichier xml source2. On considère trois opérations atomiques sur les arbres :

      @@ -251,28 +252,26 @@ r - b y - c -

      Et regardez ce que ça implique quand on l’écrit en xml :

      +

      Et regardez ce que ça implique quand on l’écrit en xml :

      -
      -<r>
      -  <x>
      -    <a>value for a</a>
      -    <b>value for b</b>
      -  </x>
      -  <y>
      -    <c>value for c</c>
      -  </y>
      -</r>
      -
      +
      <r>
      +  <x>
      +    <a>value for a</a>
      +    <b>value for b</b>
      +  </x>
      +  <y>
      +    <c>value for c</c>
      +  </y>
      +</r>
      +

      Alors supprimer tous les nœuds x revient à faire passer le xml à travers le filtre suivant :

      -
      -s/<\/?x>//g
      -
      +
      s/<\/?x>//g
      +
      -

      Par conséquent, s’il existe un transducteur déterministe à un état qui permet de transformer mes arbres ; -je suis capable de transformer le xml d’un format à l’autre en utilisant une simple liste de search and replace.

      +

      Par conséquent, s’il existe un transducteur déterministe à un état qui permet de transformer mes arbres ; +je suis capable de transformer le xml d’un format à l’autre en utilisant une simple liste de search and replace.

      Solution

      @@ -312,41 +311,39 @@ M - V - M - V - tag2 tag1

      peut-être fait en utilisant le transducteur déterministe à un état suivant:

      -

      C → ε
      +

      C → ε
      E → M
      R → V

      Ce qui peut-être traduit par les simples directives Perl suivantes :

      -
      -s/C//g
      -s/E/M/g
      -s/R/V/g
      -
      +
      s/C//g
      +s/E/M/g
      +s/R/V/g
      +

      Une fois adapté au xml cela devient :

      -
      -s%</?contenu>%%g
      -s%<enfant>%<item name="menu">%g
      -s%</enfant>%</item>%g
      -s%<rubrique>%<value>%g
      -s%</rubrique>%</value>%g
      -
      +
      s%</?contenu>%%g
      +s%<enfant>%<item name="menu">%g
      +s%</enfant>%</item>%g
      +s%<rubrique>%<value>%g
      +s%</rubrique>%</value>%g
      +
      -

      Et c’est tout.

      +

      Et c’est tout.

      Conclusion

      -

      Même si cela peut sembler paradoxal, parfois la solution la plus efficace à un problème pragmatique est d’utiliser une méthodologie théorique.

      +

      Même si cela peut sembler paradoxal, parfois la solution la plus efficace à un problème pragmatique est d’utiliser une méthodologie théorique.


      1. Normalement, je fais parti des 10% qui ont fourni une implémentation sans bug.

      2. -

        J’ai programmé un outil qui calcule automatiquement le poids de chaque élément des matrices d’édition à partir de données.

        +

        J’ai programmé un outil qui calcule automatiquement le poids de chaque élément des matrices d’édition à partir de données.

      diff --git a/output/Scratch/fr/blog/2010-06-14-multi-language-choices/index.html b/output/Scratch/fr/blog/2010-06-14-multi-language-choices/index.html index adcc1e658..8e4455a4a 100644 --- a/output/Scratch/fr/blog/2010-06-14-multi-language-choices/index.html +++ b/output/Scratch/fr/blog/2010-06-14-multi-language-choices/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,8 +57,8 @@
      -

      Je traduis la plupart de mes articles pour qu’ils soient disponibles en français et en anglais. -La façon que l’on m’a conseillé était d’avoir un fichier par langue. En général ça donne ça.

      +

      Je traduis la plupart de mes articles pour qu’ils soient disponibles en français et en anglais. +La façon que l’on m’a conseillé était d’avoir un fichier par langue. En général ça donne ça.

       Bonjour, 
      @@ -72,15 +75,15 @@ here is an example of english text.
       

      Cette façon de traduire vous impose une certaine façon de traduire. -D’abord écrire entièrement le texte dans une langue, +D’abord écrire entièrement le texte dans une langue, puis copier le fichier et enfin retraduire dans une nouvelle langue.

      -

      Le problème, c’est que très souvent, les articles ont des parties communes non négligeables. Par exemple, les images, les codes sources, etc… -Lorsque je m’aperçoit que j’ai fait une erreur dans ces parties communes -ça m’oblige à refaire deux fois la même manipulation. Sauf que comme il m’arrive d’être distrait, il peut y avoir pas mal d’aller-retours.

      +

      Le problème, c’est que très souvent, les articles ont des parties communes non négligeables. Par exemple, les images, les codes sources, etc… +Lorsque je m’aperçoit que j’ai fait une erreur dans ces parties communes +ça m’oblige à refaire deux fois la même manipulation. Sauf que comme il m’arrive d’être distrait, il peut y avoir pas mal d’aller-retours.

      -

      C’est pourquoi, j’ai plutôt opté pour une autre solution. -J’utilise des tags sur un seul fichier. +

      C’est pourquoi, j’ai plutôt opté pour une autre solution. +J’utilise des tags sur un seul fichier. En fin de compte, mes fichiers ressemblent à :

      @@ -92,10 +95,10 @@ En fin de compte, mes fichiers ressemblent à :

      [image](url)
      -

      Comme j’édite mes fichier avec vim, il m’est très facile d’ajouter ces fr: ou en: en début de ligne à l’aide du très utile C-v. -Par contre nanoc a été conçu pour être utilisé par une seule langue. Précédemment, j’avais utilisé les capacité de nanoc pour séparer les langues. Mais finalement, il s’avère bien plus simple de faire un pré-traitement qui nettoie mes fichiers et en fait deux copie qui seront ensuite gérées par nanoc.

      +

      Comme j’édite mes fichier avec vim, il m’est très facile d’ajouter ces fr: ou en: en début de ligne à l’aide du très utile C-v. +Par contre nanoc a été conçu pour être utilisé par une seule langue. Précédemment, j’avais utilisé les capacité de nanoc pour séparer les langues. Mais finalement, il s’avère bien plus simple de faire un pré-traitement qui nettoie mes fichiers et en fait deux copie qui seront ensuite gérées par nanoc.

      -

      Vous pouvez récupérer les sources de mon blog (sans tous les articles) à l’adresse suivante github.com/yogsototh/Scratch. J’écrirais un article pour savoir comment l’utiliser facilement. J’ai en effet ajouté beaucoup de scripts et de librairies.

      +

      Vous pouvez récupérer les sources de mon blog (sans tous les articles) à l’adresse suivante github.com/yogsototh/Scratch. J’écrirais un article pour savoir comment l’utiliser facilement. J’ai en effet ajouté beaucoup de scripts et de librairies.

      diff --git a/output/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/index.html b/output/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/index.html index 109964473..d5be1ac12 100644 --- a/output/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/index.html +++ b/output/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,7 +57,7 @@
      -

      J’ai publié une version light de mon système de blog hier soir. Par light il faut comprendre avec un CSS plus épuré et plus portable (sans les bords ronds). +

      J’ai publié une version light de mon système de blog hier soir. Par light il faut comprendre avec un CSS plus épuré et plus portable (sans les bords ronds). Vous pouvez le récupérer sur github.com.

      Que pouvez-vous attendre de ce système de blog ?

      @@ -66,11 +69,11 @@ Vous pouvez le récupérer sur commentaires gérés avec intenseDebate de façon asynchrone ;
    • très portable avec ou sans javascript, XHTML Strict 1.0 / CSS3 ;
    • écrivez vos entrées au format Markdown (pas de HTML) ;
    • -
    • des améliorations typographiques (pas de ‘:’ en début de ligne en Français par exemple),
    • -
    • entrez directement le code de graphes qui se génèreront automatiquement en image à l’aide de Graphviz.
    • +
    • des améliorations typographiques (pas de ‘:’ en début de ligne en Français par exemple),
    • +
    • entrez directement le code de graphes qui se génèreront automatiquement en image à l’aide de Graphviz.
    -

    Pour vous donner une idée plus précise, voici la documentation que j’ai faite (en anglais) pour accompagner le code.

    +

    Pour vous donner une idée plus précise, voici la documentation que j’ai faite (en anglais) pour accompagner le code.

    @@ -86,12 +89,11 @@ Vous pouvez le récupérer sur Once installed (follow the README.md instructions).

    -
    -$ cd /root/of/nanoc3_blog
    +
    $ cd /root/of/nanoc3_blog
     $ ./task/new_blog_entry Title of the blog
     $ vi latest.md
     $ ./task/recompile
    -
    +

    Now your website reside into the output directory.

    @@ -108,9 +110,9 @@ $ ./task/recompile

    Multi-language

    All files in multi are processed and copied in the content directory. -For each file in multi, each line starting by ‘fr: ’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.

    +For each file in multi, each line starting by ‘fr: ’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.

    -

    If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.

    +

    If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.

    Edition & Rendering

    @@ -124,7 +126,7 @@ For each file in multi, each line starting by ‘fr: ’ ar

    Typography

    -

    In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by &nbsp. This enable not to have a line starting by a single special character.

    +

    In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by &nbsp. This enable not to have a line starting by a single special character.

    You can use small caps using <sc> tags.

    @@ -139,11 +141,10 @@ For each file in multi, each line starting by ‘fr: ’ ar

    To write source code you should use the following format:

    -
    -<code class="ruby" file="filename.rb">
    +
    <code class="ruby" file="filename.rb">
     The code
    -</cOde>
    -
    +</cOde> +

    The file attribute is not required.

    diff --git a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html index e3f00f255..946d8732a 100644 --- a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html +++ b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_hidden.html @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html index 3593c9bfc..0f79658ee 100644 --- a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html +++ b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/code/become_visible.html @@ -1,4 +1,3 @@ - diff --git a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/index.html b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/index.html index 9add86011..87d5bd4aa 100644 --- a/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/index.html +++ b/output/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -55,77 +58,74 @@

    Voici un moyen très simple de ne plus être comptabilisé dans les visites de son propre site. -Tout d’abord, vous devriez jeter un coup d’œil sur comment je gère les systèmes de récupération de statistiques. +Tout d’abord, vous devriez jeter un coup d’œil sur comment je gère les systèmes de récupération de statistiques. Je centralise tout dans un seul fichier javascript ce qui facilite le travail.

    -

    Cette méthode nécessite l’utilisation de jquery-cookie.

    +

    Cette méthode nécessite l’utilisation de jquery-cookie.

    -

    Avant de comptabiliser les visites, je vérifie que la clé admin n’est pas utilisée dans mes cookies.

    +

    Avant de comptabiliser les visites, je vérifie que la clé admin n’est pas utilisée dans mes cookies.

    -
    -    var admin = $.cookie('admin');
    -    if (! admin) {
    -        // put your analytics code here
    -    } else {
    -        console.log("[WARNING] you're HIDDEN to analytics");
    +
        var admin = $.cookie('admin');
    +    if (! admin) {
    +        // put your analytics code here
    +    } else {
    +        console.log("[WARNING] you're HIDDEN to analytics");
         }
    -
    +

    et il suffit de créer deux fichier html. Un pour se cacher :

    -
    -
    -<?xml version="1.0" encoding="utf-8"?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    -        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    -    <head>
    -        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -        <script type="text/javascript" src="jquery.js"></script>
    -        <script type="text/javascript" src="jquery.cookie.js"></script>
    -        <script>
    -            $(document).ready(function(){
    -                $.cookie('admin',1);
    -                $('#info').html('Analytics can no more see you.')
    -            });
    -        </script>
    -        <title>Hide to analytics</title>
    -    </head>
    -    <body>
    -        <div id="info"></div> 
    -    </body>
    -</html>
    -
    -
    + + +
    <?xml version="1.0" encoding="utf-8"?>
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    +        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    +    <head>
    +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    +        <script type="text/javascript" src="jquery.js"></script>
    +        <script type="text/javascript" src="jquery.cookie.js"></script>
    +        <script>
    +            $(document).ready(function(){
    +                $.cookie('admin',1);
    +                $('#info').html('Analytics can no more see you.')
    +            });
    +        </script>
    +        <title>Hide to analytics</title>
    +    </head>
    +    <body>
    +        <div id="info"></div> 
    +    </body>
    +</html>
    +

    et un autre pour redevenir visible (ça peut être utile) :

    -
    -
    -<?xml version="1.0" encoding="utf-8"?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    -        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    -    <head>
    -        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -        <script type="text/javascript" src="jquery.js"></script>
    -        <script type="text/javascript" src="jquery.cookie.js"></script>
    -        <script>
    -            $(document).ready(function(){
    -                $.cookie('admin',null);
    -                $('#info').html('Analytics can see you.')
    -            });
    -        </script>
    -        <title>Hide to analytics</title>
    -    </head>
    -    <body>
    -        <div id="info"></div> 
    -    </body>
    -</html>
    -
    -
    + -

    Maintenant en accédant à ces fichiers depuis votre navigateur vous pouvez disparaître des systèmes d’analyses ou bien être considéré comme tous les autres individus. +

    <?xml version="1.0" encoding="utf-8"?>
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    +        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
    +    <head>
    +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    +        <script type="text/javascript" src="jquery.js"></script>
    +        <script type="text/javascript" src="jquery.cookie.js"></script>
    +        <script>
    +            $(document).ready(function(){
    +                $.cookie('admin',null);
    +                $('#info').html('Analytics can see you.')
    +            });
    +        </script>
    +        <title>Hide to analytics</title>
    +    </head>
    +    <body>
    +        <div id="info"></div> 
    +    </body>
    +</html>
    +
    + +

    Maintenant en accédant à ces fichiers depuis votre navigateur vous pouvez disparaître des systèmes d’analyses ou bien être considéré comme tous les autres individus. Pensez à accéder à ces fichiers depuis tous les navigateurs que vous utilisez et vos visites ne seront plus comptabilisées.

    diff --git a/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/code/yga.js b/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/code/yga.js index 31369ad5c..99f4a4661 100644 --- a/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/code/yga.js +++ b/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/code/yga.js @@ -1,4 +1,3 @@ - $(document).ready( function() { // add an event to all link for google analytics $('a').click(function () { diff --git a/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/index.html b/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/index.html index 051964d65..582c1b5e2 100644 --- a/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/index.html +++ b/output/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,68 +63,66 @@

    Voici comment analyser tous les clics que font vos utilisateurs sur votre blog en incluant google analytics de façon asynchrone.

    -

    Dans le html, il faut utiliser jQuery et un fichier que j’ai appelé yga.js :

    +

    Dans le html, il faut utiliser jQuery et un fichier que j’ai appelé yga.js :

    -
    -    <script type="text/javascript" src="jquery.js"></script>
    -    <script type="text/javascript" src="yga.js"></script>
    -
    +
        <script type="text/javascript" src="jquery.js"></script>
    +    <script type="text/javascript" src="yga.js"></script>
    +

    Voici le contenu du fichier yga.js :

    -
    -
    -$(document).ready( function() {
    -    // add an event to all link for google analytics
    -    $('a').click(function () {
    -        // tell analytics to save event
    -        try {
    -            var identifier=$(this).attr('id') ;
    -            var href=$(this).attr('href')
    -            var label="";
    -            if ( typeof( identifier ) != 'undefined' ) {
    -                label=label+'[id]:'+identifier
    -                category='JSLink'
    +
    +
    +
    $(document).ready( function() {
    +    // add an event to all link for google analytics
    +    $('a').click(function () {
    +        // tell analytics to save event
    +        try {
    +            var identifier=$(this).attr('id') ;
    +            var href=$(this).attr('href')
    +            var label="";
    +            if ( typeof( identifier ) != 'undefined' ) {
    +                label=label+'[id]:'+identifier
    +                category='JSLink'
                 }
    -            if ( typeof( href ) != 'undefined' ) {
    -                label=label+' [href]:'+href
    -                if ( href[0] == '#' ) {
    -                    category='Anchor';
    -                } else {
    -                    category='Link';
    +            if ( typeof( href ) != 'undefined' ) {
    +                label=label+' [href]:'+href
    +                if ( href[0] == '#' ) {
    +                    category='Anchor';
    +                } else {
    +                    category='Link';
                     }
                 }
    -            _gaq.push(['_trackEvent', category, 'clicked', label]);
    -            // console.log('[tracked]: ' + category + ' ; clicked ; ' + label );
    +            _gaq.push(['_trackEvent', category, 'clicked', label]);
    +            // console.log('[tracked]: ' + category + ' ; clicked ; ' + label );
             }
    -        catch (err) {
    -            console.log(err);
    +        catch (err) {
    +            console.log(err);
             }
     
    -        // pause to allow google script to run
    -        var date = new Date();
    -        var curDate = null;
    -        do {
    -            curDate = new Date();
    -        } while(curDate-date < 300);
    +        // pause to allow google script to run
    +        var date = new Date();
    +        var curDate = null;
    +        do {
    +            curDate = new Date();
    +        } while(curDate-date < 300);
         });
     });
     
    -var _gaq = _gaq || [];
    -_gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
    -_gaq.push(['_trackPageview']);
    +var _gaq = _gaq || [];
    +_gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
    +_gaq.push(['_trackPageview']);
     
    -(function() {
    - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    +(function() {
    + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
      })();
    -
    -
    + -

    Remplacez le : UA-XXXXXXXX-1 par votre code google analytics. Maintenant l’installation est finie.

    +

    Remplacez le : UA-XXXXXXXX-1 par votre code google analytics. Maintenant l’installation est finie.

    -

    Pour l’utiliser il suffit de se rendre dans google analytics rubrique Content puis Event Tracking comme sur la capture d’écran suivante :

    +

    Pour l’utiliser il suffit de se rendre dans google analytics rubrique Content puis Event Tracking comme sur la capture d’écran suivante :

    Where to find events tracking in google analytics interface

    @@ -242,7 +243,7 @@ _gaq.push([
    Écrit le : 17/06/2010 - modifié le : 17/06/2010 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js b/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js index 751a56c5c..d0c83610a 100644 --- a/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js +++ b/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js @@ -1,4 +1,3 @@ - // --- code popup --- function openPopup() { $(this).clone(false).appendTo($("#_code")); diff --git a/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/index.html b/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/index.html index 7e1a25f3b..12f0cd367 100644 --- a/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/index.html +++ b/output/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -56,42 +59,41 @@

    Voici une façon simple et rapide pour faire des popups avec jQuery.

    -
    -
    -// --- code popup ---
    -function openPopup() {
    -    $(this).clone(false).appendTo($("#_code"));
    -    $("#_code").show();
    +
    +
    +
    // --- code popup ---
    +function openPopup() {
    +    $(this).clone(false).appendTo($("#_code"));
    +    $("#_code").show();
     }
     
    -function closePopup() {
    -    $("#_code").html("");
    -    $("#_code").hide();
    +function closePopup() {
    +    $("#_code").html("");
    +    $("#_code").hide();
     }
     
    -function initCode() {
    -    $(".code").click(openPopup);
    -    $(".code").css({cursor: "pointer"});
    -    $('body').append('<div id="_code"></div>');
    -    $('#_code').css( { 'text-align': "justify", position: "fixed", 
    -                        left:0, top:0, width: "100%", height: "100%", 
    -                        "background-color": "rgba(0, 0, 0, 0.8)", 'z-index':2000, 'padding':'3px'} );
    -    $('#_code').hide();
    -    $('#_code').click(closePopup);
    +function initCode() {
    +    $(".code").click(openPopup);
    +    $(".code").css({cursor: "pointer"});
    +    $('body').append('<div id="_code"></div>');
    +    $('#_code').css( { 'text-align': "justify", position: "fixed", 
    +                        left:0, top:0, width: "100%", height: "100%", 
    +                        "background-color": "rgba(0, 0, 0, 0.8)", 'z-index':2000, 'padding':'3px'} );
    +    $('#_code').hide();
    +    $('#_code').click(closePopup);
     }
    -// --- end of code popup section ---
    -
    -
    +// --- end of code popup section --- +

    Que fait ce code ?

    Au chargement de la page je crée un div grand comme toute la page avec un fond légèrement transparent que je cache. -Je fais bien attention à son z-index pour qu’il soit devant tout le reste.

    +Je fais bien attention à son z-index pour qu’il soit devant tout le reste.

    -

    Puis lorsque l’on clique sur un div de class code, +

    Puis lorsque l’on clique sur un div de class code, je recopie le contenu de celui-ci dans le grand div que je rend visible. Très simple mais très efficace. -Pas besoin d’utiliser un plugin jQuery.

    +Pas besoin d’utiliser un plugin jQuery.

    diff --git a/output/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/index.html b/output/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/index.html index c1c7ff9ee..f139ee107 100644 --- a/output/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/index.html +++ b/output/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -61,10 +64,10 @@

    tlàl:

      -
    • J’ai essayé de faire une version de YPassword en jQuery et avec Cappuccino.
    • -
    • Cappuccino est très bien sur les navigateurs non mobile mais l’application pèse 1.4Mo et n’est pas compatible avec l’iPhone.
    • -
    • la version jQuery n’est pas aussi jolie que la version réalisée avec Cappuccino mais elle pèse seulement 106Ko et est compatible avec l’iPhone.
    • -
    • J’essayerai Dashcode 3
    • +
    • J’ai essayé de faire une version de YPassword en jQuery et avec Cappuccino.
    • +
    • Cappuccino est très bien sur les navigateurs non mobile mais l’application pèse 1.4Mo et n’est pas compatible avec l’iPhone.
    • +
    • la version jQuery n’est pas aussi jolie que la version réalisée avec Cappuccino mais elle pèse seulement 106Ko et est compatible avec l’iPhone.
    • +
    • J’essayerai Dashcode 3
    @@ -76,21 +79,21 @@
    -

    Avant de commencer, je dois dire que je sais que Cappuccino et jQuery ne sont pas plus comparable que Cocoa et la standard library en C++. L’un est fait pour créer des interfaces utilisateurs tandis que l’autre est plus une librairie qui aide aux tâches de bas niveaux. -Par contre je les ai utilisé tous les deux pour faire la même application. C’est pourquoi je compare l’expérience que j’ai retenu de chacun pour cette tâche.

    +

    Avant de commencer, je dois dire que je sais que Cappuccino et jQuery ne sont pas plus comparable que Cocoa et la standard library en C++. L’un est fait pour créer des interfaces utilisateurs tandis que l’autre est plus une librairie qui aide aux tâches de bas niveaux. +Par contre je les ai utilisé tous les deux pour faire la même application. C’est pourquoi je compare l’expérience que j’ai retenu de chacun pour cette tâche.

    -

    J’ai fait une version web de mon widget YPassword. -C’est un simple widget qui permet d’organiser ses mots de passes simplement avec une grande sécurité et de façon portable. -Ce n’est pas un widget créé pour remplacer le trousseau d’accès, mais +

    J’ai fait une version web de mon widget YPassword. +C’est un simple widget qui permet d’organiser ses mots de passes simplement avec une grande sécurité et de façon portable. +Ce n’est pas un widget créé pour remplacer le trousseau d’accès, mais plus un générateur de mots de passe.

    Le premier a été élaboré à partir du code de mon widget Mac. -Vous pouvez l’essayer ici. -J’ai ensuite fait une version avec Cappuccino, que vous pouvez essayer ici.

    +Vous pouvez l’essayer ici. +J’ai ensuite fait une version avec Cappuccino, que vous pouvez essayer ici.

    Que fait ce widget ?

    @@ -104,12 +107,12 @@ J’ai ensuite fait une version avec Cappucc
    -

    J’organise mes mots de passe avec une méthode simple. -Je mémorise un mot de passe maître. Et mon mot de passe est alors (principalement) : -<pre class="twilight">hash(motDePasseMaitre+NomDeDomaine) -</pre>

    +

    J’organise mes mots de passe avec une méthode simple. +Je mémorise un mot de passe maître. Et mon mot de passe est alors (principalement) :

    -

    En réalité j’ai besoin d’un plus d’informations pour créer mon mot de passe :

    +
    hash(motDePasseMaitre+NomDeDomaine)
    + +

    En réalité j’ai besoin d’un plus d’informations pour créer mon mot de passe :

    • Un mot de passe maître ;
    • @@ -121,14 +124,13 @@ Je mémorise un mot de passe maître. Et mon mot de passe est alors (pr

      Le mot de passe résultant est calculé comme suit :

      -
      -domainName=domaine_Name_Of_URL(url)
      -hash=sha1( masterPassword + leakedTimes + domainName )
      -if ( kind == 'base64' )
      -    hash=base64(hash)
      -end
      -return hash[0..maxlength]
      -
      +
      domainName=domaine_Name_Of_URL(url)
      +hash=sha1( masterPassword + leakedTimes + domainName )
      +if ( kind == 'base64' )
      +    hash=base64(hash)
      +end
      +return hash[0..maxlength]
      +

      En fait, selon le site web, on peut avoir des contraintes très différentes :

      @@ -137,11 +139,11 @@ hash=sha1( masterPasswo
    • longueur maximale ;
    • ne doit pas contenir de caractères spéciaux ;
    • doit contenir des caractères spéciaux ;
    • -
    • etc…
    • +
    • etc…

    Et si vous souhaitez changer votre mot de passe, le nombre de changement sert à ça. -Toutes les informations peuvent rester publiques sans trop de danger à l’exception du mot de passe principal.

    +Toutes les informations peuvent rester publiques sans trop de danger à l’exception du mot de passe principal.

    Si vous souhaitez avoir encore plus de détails vous pouvez toujours lire certaines de mes anciens articles de blog (en anglais) :

    @@ -153,64 +155,64 @@ Toutes les informations peuvent rester publiques sans trop de danger à l’

    Cappuccino

    -

    Tout d’abord je voudrais dire que les applications réalisées avec Cappuccino sont tout simplement incroyables. -C’est comme avoir une application Mac dans son navigateur.

    +

    Tout d’abord je voudrais dire que les applications réalisées avec Cappuccino sont tout simplement incroyables. +C’est comme avoir une application Mac dans son navigateur.

    -

    Je dois aussi admettre que j’ai pris du plaisir a écrire mon application avec Cappuccino. -C’est comme programmer une application Mac ou iPhone. +

    Je dois aussi admettre que j’ai pris du plaisir a écrire mon application avec Cappuccino. +C’est comme programmer une application Mac ou iPhone. Si vous connaissez bien Cocoa, vous vous sentirez comme à la maison. Si vous ne connaissez pas Cocoa, je vous conseille de vous y intéresser. -Il s’agit vraiment d’un framework excellent pour faire des interfaces utilisateur. +Il s’agit vraiment d’un framework excellent pour faire des interfaces utilisateur. Je ne suis pas un spécialiste de tous les frameworks. -Mais j’ai réalisé des Interfaces Utilisateurs avec les MFC, Java Swing1 et WXWindows il y a quelques années. +Mais j’ai réalisé des Interfaces Utilisateurs avec les MFC, Java Swing1 et WXWindows il y a quelques années. Et je dois dire que Cocoa est bien meilleurs que tous ces framework.

    -

    Cappuccino est un framework spécialisé dans le développement d’application web vraiment exceptionnel. Mais il a aussi quelques défauts qui ont surgit lors de l’écriture de mon widget.

    +

    Cappuccino est un framework spécialisé dans le développement d’application web vraiment exceptionnel. Mais il a aussi quelques défauts qui ont surgit lors de l’écriture de mon widget.

    -

    Les choses qui m’ont plu :

    +

    Les choses qui m’ont plu :

    • Le résultat est vraiment très beau
    • -
    • C’était très agréable de programmer
    • +
    • C’était très agréable de programmer
    • Comme programmer une application Mac
    • -
    • J’aurai pu utiliser Interface Builder pour créer l’interface.
    • +
    • J’aurai pu utiliser Interface Builder pour créer l’interface.
    -

    Les choses qui ne m’ont pas plu :

    +

    Les choses qui ne m’ont pas plu :

      -
    • J’ai mis un bon moment avant de comprendre comment récupérer le onChange des champs textuels.
    • -
    • La documentation manquait d’organisation.
    • +
    • J’ai mis un bon moment avant de comprendre comment récupérer le onChange des champs textuels.
    • +
    • La documentation manquait d’organisation.
    • Ça ne marche pas sous iPhone.
    • Il a fallu déployer 11Mo.
    • -
    • Il faut télécharger 1,3Mo pour que l’application se charge dans le navigateur.
    • +
    • Il faut télécharger 1,3Mo pour que l’application se charge dans le navigateur.
    -

    Je n’ai pas utilisé les bindings parce qu’il me semble qu’ils ne sont pas prêts.

    +

    Je n’ai pas utilisé les bindings parce qu’il me semble qu’ils ne sont pas prêts.

    jQuery

    -

    La version jQuery de YPassword n’est pas aussi bien finie que celle de Cappuccino. Simplement parce qu’il n’y a pas de slider directement avec jQuery. Il faudrait que j’utilise jQueryUI. Et je pense que l’application deviendrait beaucoup plus lourde pour le coups. En tout cas largement au dessus des 106Ko actuels.

    +

    La version jQuery de YPassword n’est pas aussi bien finie que celle de Cappuccino. Simplement parce qu’il n’y a pas de slider directement avec jQuery. Il faudrait que j’utilise jQueryUI. Et je pense que l’application deviendrait beaucoup plus lourde pour le coups. En tout cas largement au dessus des 106Ko actuels.

    -

    J’ai utilisé le code de mon widget mac en l’adaptant un peu pour faire cette version. C’était relativement facile. Mais jQuery n’est pas un framework orienté application. Il s’agit plus d’un framework pour faire des animations qui la pète.

    +

    J’ai utilisé le code de mon widget mac en l’adaptant un peu pour faire cette version. C’était relativement facile. Mais jQuery n’est pas un framework orienté application. Il s’agit plus d’un framework pour faire des animations qui la pète.

    -

    Je n’ai pas beaucoup plus à dire sur la version jQuery, sinon que programmer avec jQuery était de la programmation de niveau beaucoup plus bas qu’avec Cappuccino.

    +

    Je n’ai pas beaucoup plus à dire sur la version jQuery, sinon que programmer avec jQuery était de la programmation de niveau beaucoup plus bas qu’avec Cappuccino.

    En conclusion

    -

    Si vous voulez faire une application compatible iPhone n’utilisez pas Cappuccino. Du moins pas encore. +

    Si vous voulez faire une application compatible iPhone n’utilisez pas Cappuccino. Du moins pas encore. Si vous souhaitez faire un application très simple (comme la mienne), je pense que Cappuccino est un peu trop lourd pour ça.

    Si vous souhaitez faire des applications web complexes qui ressemblent à des applications de bureau alors clairement Cappuccino est un très bon choix. -Notez cependant qu’il peut être un peu difficile de débuter.

    +Notez cependant qu’il peut être un peu difficile de débuter.

    -

    Finallement, pour terminer la version web de mon widget, j’essayerai Dashcode 3. +

    Finallement, pour terminer la version web de mon widget, j’essayerai Dashcode 3. Il semblerai que ce soit une bonne alternative pour créer des widget sur le web compatible iPhone. -Je ne sais pas si les applications réalisées avec Dashcode 3 sont compatibles pour les browser n’utilisant pas webkit. Mais si c’est le cas, alors ça pourrait sonner le glas des projets comme Cappuccino et Sproutcore.

    +Je ne sais pas si les applications réalisées avec Dashcode 3 sont compatibles pour les browser n’utilisant pas webkit. Mais si c’est le cas, alors ça pourrait sonner le glas des projets comme Cappuccino et Sproutcore.


    1. -

      Si ça vous intéresse vous pouvez jeter un coup d’œil à SEDiL. Je suis assez fier de la vue automatique des arbres que j’ai programmé sans librairie de départ.

      +

      Si ça vous intéresse vous pouvez jeter un coup d’œil à SEDiL. Je suis assez fier de la vue automatique des arbres que j’ai programmé sans librairie de départ.

    diff --git a/output/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html b/output/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html index 3b6ae63e8..78cb70b88 100644 --- a/output/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html +++ b/output/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,10 +57,10 @@
    -

    Beaucoup d’utilisateurs de Reddit m’ont rapporté que mon site était très long à charger et à scroller. -Ils pensaient qu’il s’agissait d’un problème dû aux ombres que j’applique sur le texte. -J’étais un peu surpris puisque je fais mes tests sur une machine vraiment très lente et je n’avais jamais détecté ces problèmes. -En réalité, ce qui ralenti le rendu de ce site est par ordre d’importance :

    +

    Beaucoup d’utilisateurs de Reddit m’ont rapporté que mon site était très long à charger et à scroller. +Ils pensaient qu’il s’agissait d’un problème dû aux ombres que j’applique sur le texte. +J’étais un peu surpris puisque je fais mes tests sur une machine vraiment très lente et je n’avais jamais détecté ces problèmes. +En réalité, ce qui ralenti le rendu de ce site est par ordre d’importance :

    1. Les dégradés sur Chrome (pas dans Safari sur Mac)
    2. @@ -66,24 +69,24 @@ En réalité, ce qui ralenti le rendu de ce site est par ordre d’importanc

      les dégradés

      -

      Sur Safari il n’y a absolument aucun problème avec les dégradés. Par contre sur Chrome sous Linux le site devient quasiment inutilisable.

      +

      Sur Safari il n’y a absolument aucun problème avec les dégradés. Par contre sur Chrome sous Linux le site devient quasiment inutilisable.

      -

      Safari et Chrome utilisent webkit tous les deux. Lorsque vous accéder à ce blog avec javascript activé, un CSS spécifique à votre navigateur est ajouté. Jusqu’à maintenant je faisais un tri entre : IE, Mozilla et Webkit. Maintenant j’ai rajouté un cas particulier pour Chrome. -Maintenant j’ai supprimé les gradients lorsque vous naviguer sur ce site en utilisant Chrome.

      +

      Safari et Chrome utilisent webkit tous les deux. Lorsque vous accéder à ce blog avec javascript activé, un CSS spécifique à votre navigateur est ajouté. Jusqu’à maintenant je faisais un tri entre : IE, Mozilla et Webkit. Maintenant j’ai rajouté un cas particulier pour Chrome. +Maintenant j’ai supprimé les gradients lorsque vous naviguer sur ce site en utilisant Chrome.

      -

      Je n’ai pas vérifier la vitesse de rendu de toutes les propriétés de CSS 3. Mais je vous conseille de ne pas utiliser -webkit-gradient avec Chrome. Au moins sous Linux.

      +

      Je n’ai pas vérifier la vitesse de rendu de toutes les propriétés de CSS 3. Mais je vous conseille de ne pas utiliser -webkit-gradient avec Chrome. Au moins sous Linux.

      Les ombres (box-shadow)

      -

      J’ai aussi remarqué que -moz-box-shadow ralenti le rendu sous Firefox (et sous Linux). Alors que l’équivalent webkit ne pose aucun problème à Safari sous Mac.

      +

      J’ai aussi remarqué que -moz-box-shadow ralenti le rendu sous Firefox (et sous Linux). Alors que l’équivalent webkit ne pose aucun problème à Safari sous Mac.

      Ombres de texte

      -

      Beaucoup d’utilisateurs mon dit d’utiliser text-shadows avec parcimonie. Mais je pense qu’il ne s’agissait pas là du problème du ralentissement du site. C’est pourquoi je vais les remettre.

      +

      Beaucoup d’utilisateurs mon dit d’utiliser text-shadows avec parcimonie. Mais je pense qu’il ne s’agissait pas là du problème du ralentissement du site. C’est pourquoi je vais les remettre.

      en conclusion

      -

      N’utilisez pas -webkit-gradient avec google Chrome pour l’instant. +

      N’utilisez pas -webkit-gradient avec google Chrome pour l’instant. Utilisez -moz-box-shadow avec parcimonie.

    diff --git a/output/Scratch/fr/blog/2010-07-09-Indecidabilities/index.html b/output/Scratch/fr/blog/2010-07-09-Indecidabilities/index.html index 15ae3db98..14f8322cf 100644 --- a/output/Scratch/fr/blog/2010-07-09-Indecidabilities/index.html +++ b/output/Scratch/fr/blog/2010-07-09-Indecidabilities/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,10 +57,10 @@
    -

    tlàl : Je crée un mode mathématique simple pour parler de différents types d’indécidabilités :

    +

    tlàl : Je crée un mode mathématique simple pour parler de différents types d’indécidabilités :

      -
    • indécidabilité due aux erreurs d’observation ;
    • +
    • indécidabilité due aux erreurs d’observation ;
    • grandes erreurs résultant de petites erreurs de mesure ;
    • indécidabilité fractales ;
    • indécidabilité logique.
    • @@ -79,87 +82,87 @@
      -

      Si le monde a été fabriqué par un démiurge, on peut dire que celui-ci devait avoir le sens de l’humour. +

      Si le monde a été fabriqué par un démiurge, on peut dire que celui-ci devait avoir le sens de l’humour. Et le récit que je vais faire va vous en fournir la preuve. Je vais me mettre à sa place. Je vais créer un monde simplifié. Un monde régi exactement par nos règles mathématiques. -Puis je vais vous parler du mal qui touche cet Univers semblable au notre ; l’indécidabilité. -L’incapacité de savoir si nous avons trouvé la vérité, ou seulement une approximation de celle-ci. -L’incapacité de prédire certaines choses qui semblent pourtant aller de soi. +Puis je vais vous parler du mal qui touche cet Univers semblable au notre ; l’indécidabilité. +L’incapacité de savoir si nous avons trouvé la vérité, ou seulement une approximation de celle-ci. +L’incapacité de prédire certaines choses qui semblent pourtant aller de soi. Voilà comment tout aurait pu commencer.

      -

      no name

      +

      leftblogimage(“genesis.png”)

      -

      Au début, il n’y avait rien. +

      Au début, il n’y avait rien. Puis un article de blog commença à prendre forme. -J’inspire profondément pour sentir la pesanteur de ce que je vais accomplir. -Attention, une dernier moment de tension et je crée l’Univers. -Un Univers qui n’existera que le temps de la lecture de cet article. +J’inspire profondément pour sentir la pesanteur de ce que je vais accomplir. +Attention, une dernier moment de tension et je crée l’Univers. +Un Univers qui n’existera que le temps de la lecture de cet article. Me voici le démiurge de cet Univers et te voilà son observateur privilégié.

      -

      Comme j’aime bien tout contrôler, je fabrique ce monde avec quelques règles simples. +

      Comme j’aime bien tout contrôler, je fabrique ce monde avec quelques règles simples. Je décide que les vrais règles de ce monde sont celles que nous pensons qui régissent notre monde. -Notez qu’il y a une grande différence. -Pour leur monde, ce que l’on croit vrai aujourd’hui, est vraiment vrai pour eux. +Notez qu’il y a une grande différence. +Pour leur monde, ce que l’on croit vrai aujourd’hui, est vraiment vrai pour eux. Leur monde est donc plus simple à priori que le notre. En particulier, on peut le décrire avec des axiomes et des règles mathématiques. -Alors qu’il est possible que ce ne soit pas le cas de notre Univers. +Alors qu’il est possible que ce ne soit pas le cas de notre Univers. Mais nous reviendront là-dessus plus tard.

      Bon au travail maintenant, je crée une Terre. -J’y ajoute des habitants intelligents, les Ys. +J’y ajoute des habitants intelligents, les Ys. Bien entendu ils se posent des questions. En particulier, ils se demandent quelles sont les lois qui régissent leur monde. -Ils pensent que connaître toutes ces règles leur permettrait de connaître l’avenir. +Ils pensent que connaître toutes ces règles leur permettrait de connaître l’avenir. Leur naïveté est touchante. Ah, si seulement ils savaient. Mais je suis là pour les aider à apprendre.

      Comme je suis un Dieu un peu facétieux, je vais leur jouer quelques tours. -Sinon on s’ennuierai à mourir. +Sinon on s’ennuierai à mourir. Le premier est de leur donner des sens imparfaits. -De plus il leur est impossible d’avoir des mesures parfaites. +De plus il leur est impossible d’avoir des mesures parfaites. Je leur laisse cependant toutes libertés pour améliorer leur technologie et diminuer ces erreurs de mesures.

      Les habitants de ce monde pensent que celui-ci est plat. -Certains d’entre eux pensent qu’il est possible de découvrir les règles du monde que j’ai créé. +Certains d’entre eux pensent qu’il est possible de découvrir les règles du monde que j’ai créé. Et bien que le jeu commence.

      -

      Commençons par leur première leçon, les erreurs causent de l’indécidabilité.

      +

      Commençons par leur première leçon, les erreurs causent de l’indécidabilité.

      Indécidabilité dues aux erreurs de mesures

      -

      Voici ce que pense l’un de ces individus.

      +

      Voici ce que pense l’un de ces individus.

      -

      Tous les triangles que j’observe semble avoir une propriété commune. +

      Tous les triangles que j’observe semble avoir une propriété commune. La somme de leurs angles est toujours π radiants (180°). -Il s’agit certainement d’une loi de mon Univers. +Il s’agit certainement d’une loi de mon Univers. Mais comment être certain que tous les triangles de mon Univers possèdent cette propriété ?

      -

      no name

      +

      three triangles

      -

      Certain d’entre eux commencent à formaliser un petit peu le problème +

      Certain d’entre eux commencent à formaliser un petit peu le problème et ils finissent faire une preuve mathématique. Magnifique ! La preuve est correcte, mais il reste un petit problème. -La preuve s’appuie sur des axiomes et des règles. +La preuve s’appuie sur des axiomes et des règles. Comment être certain que ces règles et ces axiomes sont vrai dans leur monde? Ils auront beau faire des mesures de plus en plus précises qui conforteront cette formule, -ils n’auront que l’espoir et jamais la certitude que la formule est vrai. -Simplement parce que le seul moyen de vérifier la véracité des axiomes est par l’observation. -Hors en tant que dieu facétieux, j’ai interdit les observation avec des mesures parfaites.

      +ils n’auront que l’espoir et jamais la certitude que la formule est vrai. +Simplement parce que le seul moyen de vérifier la véracité des axiomes est par l’observation. +Hors en tant que dieu facétieux, j’ai interdit les observation avec des mesures parfaites.

      -

      Bien entendu, ils prient, ils m’appellent à l’aide. +

      Bien entendu, ils prient, ils m’appellent à l’aide. Et comme tout Dieu qui se respecte, je ne réponds pas. -Ah ah ah ! J’ai toujours aimé faire ce genre de chose. -Ensuite je ferai comme si je n’existe pas. +Ah ah ah ! J’ai toujours aimé faire ce genre de chose. +Ensuite je ferai comme si je n’existe pas. Encore un bonne blague !

      Si certains se sentent accablés, il leur reste un espoir :

      @@ -172,21 +175,21 @@ Encore un bonne blague !

      Indécidabilité avec erreurs croissantes

      -

      no name

      +

      Three bodies

      Malheureusement pour eux, il y a le problème des 3 corps. Prenons les formules de la gravitation Universelle et appliquons la à deux corps célestes. Si on connait la position de ces corps avec un grande précision, on pourra aussi connaître la position future de ces corps avec une grande précision. -L’hypothèse selon laquelle de petite erreurs de mesures impliquent de petites erreurs prédictive est confortée. +L’hypothèse selon laquelle de petite erreurs de mesures impliquent de petites erreurs prédictive est confortée. Cependant, il y a un problème. Reprenons le même problème mais avec trois corps. Par exemple, avec le Soleil, la Terre et la Lune. -Dans ce cas, les erreurs de mesures initiales vont s’amplifier. -S’amplifier au point de rendre toute prédiction inutilisable.

      +Dans ce cas, les erreurs de mesures initiales vont s’amplifier. +S’amplifier au point de rendre toute prédiction inutilisable.

      -

      Là encore une voix d’espoir s’élève :

      +

      Là encore une voix d’espoir s’élève :

      -

      Peut-être pouvons nous calculer l’erreur maximale acceptable pour prédire quelque chose. +

      Peut-être pouvons nous calculer l’erreur maximale acceptable pour prédire quelque chose. Et nous pourrions au moins savoir ce que nous pouvons prédire ou pas.

      @@ -196,60 +199,60 @@ Et nous pourrions au moins savoir ce que nous pouvons prédire ou pas.

      Considérons la question suivante :

      -

      no name

      +

      Mandelbrot set

      Soit des coordonnées GPS précises à 1m près. Les coordonnées sont proches des côtes de la Bretagne. Ce point va-t-il tomber dans la mer ou sur la terre ferme ?

      -

      Et bien, pour certaines coordonnées, c’est impossible de le savoir. -Même si je réduis l’erreur à une valeur infinitésimale. -Simplement parce que certains voisinages autour d’un point contiennent toujours à la fois de l’eau et de la terre. +

      Et bien, pour certaines coordonnées, c’est impossible de le savoir. +Même si je réduis l’erreur à une valeur infinitésimale. +Simplement parce que certains voisinages autour d’un point contiennent toujours à la fois de l’eau et de la terre. Et ce quelque soit la taille du voisinage.

      -

      On peut même imaginer une structure ou tous les points sont au bord de celle-ci, on ne peut donc pas se permettre d’erreur1.

      +

      On peut même imaginer une structure ou tous les points sont au bord de celle-ci, on ne peut donc pas se permettre d’erreur1.

      Mais que vois-je ? -Un petit malin essaye de trouver la vérité en s’extrayant de mon Monde et en faisant un article sur un blog ? +Un petit malin essaye de trouver la vérité en s’extrayant de mon Monde et en faisant un article sur un blog ? Ça ne va pas se passer comme ça ! Croyez moi ! -> Faire des prédictions précises à partir des données observées semble être une quête vouée à l’échec. -> Mais je suis persuadé que l’on peut aller au delà. -> Au diable ce Dieu qui nous empêche d’avoir des mesures précises ! +> Faire des prédictions précises à partir des données observées semble être une quête vouée à l’échec. +> Mais je suis persuadé que l’on peut aller au delà. +> Au diable ce Dieu qui nous empêche d’avoir des mesures précises ! > Inventons notre propre Univers mathématique. > Un monde qui se suffit à lui-même. -> Un monde dans lequel il n’y aura plus d’erreur de mesure. +> Un monde dans lequel il n’y aura plus d’erreur de mesure. > Un monde entièrement contrôlé par des règles que nous aurons choisi. > Un monde similaire au notre mais où tout pourra être prédit.

      Indécidabilité logique

      -

      no name

      +

      recursive stack overflow

      -

      Jusqu’ici, tous les problèmes d’indécidabilités étaient dûs aux erreurs. -Maintenant peut-être que privé d’erreur de mesure, on pourrait enfin résoudre tous les problèmes.
      +

      Jusqu’ici, tous les problèmes d’indécidabilités étaient dûs aux erreurs. +Maintenant peut-être que privé d’erreur de mesure, on pourrait enfin résoudre tous les problèmes.
      Et bien non. Même dans un monde mathématique complètement contrôlé. -On peut créer un objet pour lequel on ne pourra pas décider à l’avance ce qu’il fait.

      +On peut créer un objet pour lequel on ne pourra pas décider à l’avance ce qu’il fait.

      -

      Il s’agit du problème de l’arrêt.

      +

      Il s’agit du problème de l’arrêt.

      -

      Le Théorème stipule qu’il n’existe pas de programme permettant de décider si un autre programme s’arrête. +

      Le Théorème stipule qu’il n’existe pas de programme permettant de décider si un autre programme s’arrête. La preuve est suffisamment simple pour rentrer dans ce post, donc je me fais un petit plaisir en la donnant.

      -

      Supposons qu’il existe un programme qui puisse dire si un autre programme s’arrête. Plus précisément :

      +

      Supposons qu’il existe un programme qui puisse dire si un autre programme s’arrête. Plus précisément :

      Hypothèse: Il existe un programme P tel que:

        -
      • P(x,y) réponde “s’arrête” en un temps fini si et seulement si x(y)2 s’arrête effectivement en temps fini et
      • -
      • P(x,y) réponde “ne s’arrête pas” en un temps fini dans le cas contraire.
      • +
      • P(x,y) réponde “s’arrête” en un temps fini si et seulement si x(y)2 s’arrête effectivement en temps fini et
      • +
      • P(x,y) réponde “ne s’arrête pas” en un temps fini dans le cas contraire.
      -

      Remarque: Tout code de programme est une chaîne de caractère qui peut être utilisée aussi comme entrée d’un autre programme. +

      Remarque: Tout code de programme est une chaîne de caractère qui peut être utilisée aussi comme entrée d’un autre programme. Ainsi écrire P(x,x) est autorisé.

      -

      Soit le programme Q que j’écris comme suit :

      +

      Soit le programme Q que j’écris comme suit :

       Q(x) :
      @@ -260,14 +263,14 @@ Q(x) :
         

      Maintenant que répond P(Q,Q)?

        -
      • si P(Q,Q) répond “s’arrête” ça implique que P(Q,Q)=”ne s’arrête pas”
      • -
      • si P(Q,Q) répond “ne s’arrête pas” ça implique que P(Q,Q)=”s’arrête”
      • +
      • si P(Q,Q) répond “s’arrête” ça implique que P(Q,Q)=”ne s’arrête pas”
      • +
      • si P(Q,Q) répond “ne s’arrête pas” ça implique que P(Q,Q)=”s’arrête”

      Il y a donc une contradiction que le seul moyen de régler est par la non existence du programme P.

      -

      C’est simple, je suis le démiurge de ce monde imaginaire. +

      C’est simple, je suis le démiurge de ce monde imaginaire. Et même moi, je dois me soumettre à cette règle. Comme quoi, avoir la possibilité de créer le monde et la toute puissance sont deux choses différentes.

      @@ -278,15 +281,15 @@ Comme quoi, avoir la possibilité de créer le monde et la toute puissance sont

      Après tout ceci, il peut sembler difficile de savoir en quoi nous pouvons croire. -Mais ce serait une erreur de jeter le bébé avec l’eau du bain. -Dans une seconde partie, j’expliquerai ce que nous pouvons espérer et qu’elle attitude nous devons adopter une fois que l’on a réalisé que beaucoup de vérité nous sont inaccessibles.

      +Mais ce serait une erreur de jeter le bébé avec l’eau du bain. +Dans une seconde partie, j’expliquerai ce que nous pouvons espérer et qu’elle attitude nous devons adopter une fois que l’on a réalisé que beaucoup de vérité nous sont inaccessibles.


      1. Pensez aux deux ensembles R\Q et Q.

      2. -

        C’est-à-dire le programme x prenant l’entrée y.

        +

        C’est-à-dire le programme x prenant l’entrée y.

      @@ -406,7 +409,7 @@ Dans une seconde partie, j’expliquerai ce que nous pouvons espérer et qu&
    Écrit le : 11/08/2010 - modifié le : 11/04/2012 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/2010-07-31-New-style-after-holidays/index.html b/output/Scratch/fr/blog/2010-07-31-New-style-after-holidays/index.html index 1cd87b43f..a8c3cbe74 100644 --- a/output/Scratch/fr/blog/2010-07-31-New-style-after-holidays/index.html +++ b/output/Scratch/fr/blog/2010-07-31-New-style-after-holidays/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,11 +57,11 @@
    -

    Avant les vacances beaucoup d’utilisateurs se sont plaints de la lenteur de rendu de mon site. -Il s’agit notamment de problèmes avec Chrome en particulier. +

    Avant les vacances beaucoup d’utilisateurs se sont plaints de la lenteur de rendu de mon site. +Il s’agit notamment de problèmes avec Chrome en particulier. Mais pour éviter tout problème. -J’ai complètement modifié le style de mon site web. -Il est inspiré du style de l’application iBooks© sur iPhone©.

    +J’ai complètement modifié le style de mon site web. +Il est inspiré du style de l’application iBooks© sur iPhone©.

    Dites moi ce que vous pensez de ce nouveau design.

    diff --git a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/.gems b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/.gems index 7c156f5cb..5e3736f7c 100644 --- a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/.gems +++ b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/.gems @@ -1,4 +1,3 @@ - rack rack-rewrite rack-contrib diff --git a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru index 32b16fc1f..48ae35298 100644 --- a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru +++ b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/code/config.ru @@ -1,4 +1,3 @@ - require 'rubygems' require 'rack' require 'rack/contrib' diff --git a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/index.html b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/index.html index 2c7a4d5a3..8f4f96edf 100644 --- a/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/index.html +++ b/output/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,83 +63,80 @@

    Maintenant sur Heroku

    -

    J’ai changé mon hébergeur. Mobileme n’est absolument pas adapté à la diffusion de mon blog. C’est pourquoi je suis passé à Heroku.

    +

    J’ai changé mon hébergeur. Mobileme n’est absolument pas adapté à la diffusion de mon blog. C’est pourquoi je suis passé à Heroku.

    Mais comme vous devez le savoir mon blog est un site complètement statique. -J’utilise nanoc pour l’engendrer. -Avoir un site statique amène beaucoup d’avantages par rapport à un site dynamique. Surtout en terme de sécurité. +J’utilise nanoc pour l’engendrer. +Avoir un site statique amène beaucoup d’avantages par rapport à un site dynamique. Surtout en terme de sécurité. Voici comment configurer un site statique sur heroku.

    -

    La racine de mes fichiers est ‘/output’. Vous devez simplement créer deux fichiers. Un fichier config.ru1 :

    +

    La racine de mes fichiers est ‘/output’. Vous devez simplement créer deux fichiers. Un fichier config.ru1 :

    -
    -
    -require 'rubygems'
    -require 'rack'
    -require 'rack/contrib'
    -require 'rack-rewrite'
    -require 'mime/types'
    +
     
    -use Rack::ETag
    -module ::Rack
    -    class TryStatic < Static
    +
    require 'rubygems'
    +require 'rack'
    +require 'rack/contrib'
    +require 'rack-rewrite'
    +require 'mime/types'
     
    -        def initialize(app, options)
    -            super
    -            @try = ([''] + Array(options.delete(:try)) + [''])
    -        end
    +use Rack::ETag
    +module ::Rack
    +    class TryStatic < Static
     
    -        def call(env)
    -            @next = 0
    -            while @next < @try.size && 404 == (resp = super(try_next(env)))[0] 
    -                @next += 1
    -            end
    -            404 == resp[0] ? @app.call : resp
    -        end
    +        def initialize(app, options)
    +            super
    +            @try = ([''] + Array(options.delete(:try)) + [''])
    +        end
     
    -        private
    -        def try_next(env)
    -            env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
    -        end
    +        def call(env)
    +            @next = 0
    +            while @next < @try.size && 404 == (resp = super(try_next(env)))[0] 
    +                @next += 1
    +            end
    +            404 == resp[0] ? @app.call : resp
    +        end
     
    -    end
    -end
    +        private
    +        def try_next(env)
    +            env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
    +        end
     
    -use Rack::TryStatic, 
    -    :root => "output",                              # static files root dir
    -    :urls => %w[/],                                 # match all requests 
    -    :try => ['.html', 'index.html', '/index.html']  # try these postfixes sequentially
    +    end
    +end
     
    -errorFile='output/Scratch/en/error/404-not_found/index.html'
    -run lambda { [404, {
    -                "Last-Modified"  => File.mtime(errorFile).httpdate,
    -                "Content-Type"   => "text/html",
    -                "Content-Length" => File.size(errorFile).to_s
    -            }, File.read(errorFile)] }
    -
    -
    +use Rack::TryStatic, + :root => "output", # static files root dir + :urls => %w[/], # match all requests + :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially + +errorFile='output/Scratch/en/error/404-not_found/index.html' +run lambda { [404, { + "Last-Modified" => File.mtime(errorFile).httpdate, + "Content-Type" => "text/html", + "Content-Length" => File.size(errorFile).to_s + }, File.read(errorFile)] } +

    et un fichier .gems qui liste les gems nécessaires.

    -
    -
    -rack
    -rack-rewrite
    -rack-contrib
    -
    -
    + -

    Maintenant il suffit de suivre l’introduction rapide d’heroku pour créer une nouvelle application :

    +
    rack
    +rack-rewrite
    +rack-contrib
    +
    -
    -git init
    +

    Maintenant il suffit de suivre l’introduction rapide d’heroku pour créer une nouvelle application :

    + +
    git init
     git add .
     heroku create
     git push heroku master
    -
    +

    Maintenant je peux rediriger correctement mes erreurs 404. -J’espère que ça a pu vous être utile.

    +J’espère que ça a pu vous être utile.


    1. diff --git a/output/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html b/output/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html index 905b044b1..818e53ace 100644 --- a/output/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html +++ b/output/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,23 +57,22 @@
      -

      J’ai dû envoyer un mail en ligne de commande récemment. -Quelle ne fût pas ma surprise lorsque je constatais que ce n’était vraiment pas évident. -Je n’avais ni pine ni mutt. Seulement mail et mailx.

      +

      J’ai dû envoyer un mail en ligne de commande récemment. +Quelle ne fût pas ma surprise lorsque je constatais que ce n’était vraiment pas évident. +Je n’avais ni pine ni mutt. Seulement mail et mailx.

      -

      Ce qu’on trouve sur internet pour envoyer un mail avec fichier attaché c’est ça :

      +

      Ce qu’on trouve sur internet pour envoyer un mail avec fichier attaché c’est ça :

      -
      -uuencode fic.jpg fic.jpg | mail -s 'Subject'
      -
      +
      uuencode fic.jpg fic.jpg | mail -s 'Subject'
      +
      -

      Bon, alors, bête et discipliné j’ai essayé. +

      Bon, alors, bête et discipliné j’ai essayé. Et bien, ça marche presque tout le temps. -Pour mon fichier ça n’a pas marché du tout. -Je l’ai compressé au format .gz, .bz2 et .zip. +Pour mon fichier ça n’a pas marché du tout. +Je l’ai compressé au format .gz, .bz2 et .zip. Avec le format .bz2 le mail reçu avait bien un fichier attaché. Mais avec les formats .gz et .zip, ça ne fonctionnait pas. -Au lieu d’avoir un fichier attaché j’avais un message qui contenait quelque chose comme :

      +Au lieu d’avoir un fichier attaché j’avais un message qui contenait quelque chose comme :

       begin 664 fic.jpg
      @@ -83,17 +85,16 @@ end
       

      Pas très lisible. -Après pas mal de recherche j’ai trouvé la solution. -Le problème c’est uuencode qui est une méthode qui devrait devenir obsolète pour envoyer les fichiers. +Après pas mal de recherche j’ai trouvé la solution. +Le problème c’est uuencode qui est une méthode qui devrait devenir obsolète pour envoyer les fichiers. Il vaut mieux utiliser le format MIME pour envoyer des fichiers attachés.

      -

      Donc finalement le mieux est de faire ça “à la main” avec sendmail. -Je n’ai quand même pas utilisé telnet. +

      Donc finalement le mieux est de faire ça “à la main” avec sendmail. +Je n’ai quand même pas utilisé telnet. La commande à lancer est :

      -
      -sendmail -t -oi < mailcontent.txt
      -
      +
      sendmail -t -oi < mailcontent.txt
      +

      Bien entendu il faut créer le fichier mailcontent.txt qui contient :

      @@ -125,8 +126,8 @@ uuencode -m fic.jpg fic.jpg

      Et voilà. -Parfois la technique c’est tellement simple. -Si j’en ai besoin encore quelques fois, je pense que j’écrirai un émetteur de mail en shell.

      +Parfois la technique c’est tellement simple. +Si j’en ai besoin encore quelques fois, je pense que j’écrirai un émetteur de mail en shell.

      diff --git a/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb b/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb index 0617e0637..7aeec2be6 100644 --- a/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb +++ b/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb @@ -1,4 +1,3 @@ - def gitmtime filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'') ext=%{.#{@item[:extension]}} diff --git a/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html b/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html index db6d2e3d9..934be6712 100644 --- a/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html +++ b/output/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,35 +57,34 @@
      -

      Vous pouvez remarquer qu’à la fin de chaque page je donne une date de dernière modification. +

      Vous pouvez remarquer qu’à la fin de chaque page je donne une date de dernière modification. Précédemment cette date était calculée en utilisant la date du fichier. -Mais il arrive fréquemment que je fasse un touch d’un fichier pour engendrer tout le site de nouveau. -Donc la date n’est pas nécessairement la vraie de modification du contenue.

      +Mais il arrive fréquemment que je fasse un touch d’un fichier pour engendrer tout le site de nouveau. +Donc la date n’est pas nécessairement la vraie de modification du contenue.

      -

      J’utilise git pour versionner mon site web. -Et cet outil me permet de récupérer la dernière date de vraie modification d’un fichier. -Voici comment je m’y prend avec nanoc :

      +

      J’utilise git pour versionner mon site web. +Et cet outil me permet de récupérer la dernière date de vraie modification d’un fichier. +Voici comment je m’y prend avec nanoc :

      -
      -
      -def gitmtime
      -    filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'')
      -    ext=%{.#{@item[:extension]}}
      -    filepath<<=ext
      -    if not FileTest.exists?(filepath)
      -        filepath.sub!(ext,%{#{@item.raw_filename}#{ext}})
      -    end
      -    str=`git log -1 --format='%ci' -- #{filepath}`
      -    if str.nil? or str.empty?
      -        return Time.now
      -    else
      -        return DateTime.parse( str )
      -    end
      -end
      -
      -
      + -

      Bien entendu je sais que c’est très lent et absolument pas optimisé. +

      def gitmtime
      +    filepath=@item.path.sub('/Scratch/','content/html/').sub(/\/$/,'')
      +    ext=%{.#{@item[:extension]}}
      +    filepath<<=ext
      +    if not FileTest.exists?(filepath)
      +        filepath.sub!(ext,%{#{@item.raw_filename}#{ext}})
      +    end
      +    str=`git log -1 --format='%ci' -- #{filepath}`
      +    if str.nil? or str.empty?
      +        return Time.now
      +    else
      +        return DateTime.parse( str )
      +    end
      +end
      +
      + +

      Bien entendu je sais que c’est très lent et absolument pas optimisé. Mais ça fonctionne comme prévu. Maintenant la date que vous voyez en bas de la page correspond exactement à la dernière date de modification de son contenu.

      diff --git a/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c b/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c index 26c5a31f0..cd9879240 100644 --- a/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c +++ b/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/code/iphone_base64_sha1.c @@ -1,5 +1,4 @@ - - (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result { char *c_baseString=(char *)[baseString UTF8String]; CC_SHA1(c_baseString, strlen(c_baseString), result); diff --git a/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html b/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html index 9774144ec..207d558e2 100644 --- a/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html +++ b/output/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,69 +57,68 @@
      -

      Allons directement à l’essentiel : -voici deux fonctions à intégrer à votre application iPhone pour afficher l’encodage en base64 ou en hexadecimal du hash sha1 d’un string en Objective-C pour iPhone.

      +

      Allons directement à l’essentiel : +voici deux fonctions à intégrer à votre application iPhone pour afficher l’encodage en base64 ou en hexadecimal du hash sha1 d’un string en Objective-C pour iPhone.

      -

      Pour l’usage c’est très simple, copiez le code dans la classe de votre choix. +

      Pour l’usage c’est très simple, copiez le code dans la classe de votre choix. Puis :

      -
      -#import <CommonCrypto/CommonDigest.h>
      +
      #import <CommonCrypto/CommonDigest.h>
       ...
      -NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"];
      +NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"];
       ...
      -NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"];
      -
      +NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"]; +
      -

      L’algorithme pour l’encodage en base64 doit être programmé sur iPhone. -Il n’y a pas de librairie officielle qui s’occupe de ça.

      +

      L’algorithme pour l’encodage en base64 doit être programmé sur iPhone. +Il n’y a pas de librairie officielle qui s’occupe de ça.

      -
      -
      +
       
      -- (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result {
      -    char *c_baseString=(char *)[baseString UTF8String];
      -    CC_SHA1(c_baseString, strlen(c_baseString), result);
      -    return result;
      +
      
      +- (unsigned char *)sha1:(NSString *)baseString result:(unsigned char *)result {
      +    char *c_baseString=(char *)[baseString UTF8String];
      +    CC_SHA1(c_baseString, strlen(c_baseString), result);
      +    return result;
       }
       
      -- (NSString *)base64:(unsigned char *)result {
      +- (NSString *)base64:(unsigned char *)result {
           NSString *password=[[NSString alloc] init];
      -    static const unsigned char cb64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      -    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i+=3) {
      -        password=[password stringByAppendingFormat:@"%c%c%c%c",
      -            cb64[(result[i] &0xFC)>>2],
      -            cb64[((result[i] & 0x03) << 4)
      -                | ((result[i + 1] & 0xF0) >> 4)],
      -            cb64[((result[i + 1] & 0x0F) << 2)
      -                | ((result[i + 2] & 0xC0) >> 6)],
      -            cb64[result[i+2]&0x3F]
      +    static const unsigned char cb64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      +    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i+=3) {
      +        password=[password stringByAppendingFormat:@"%c%c%c%c",
      +            cb64[(result[i] &0xFC)>>2],
      +            cb64[((result[i] & 0x03) << 4)
      +                | ((result[i + 1] & 0xF0) >> 4)],
      +            cb64[((result[i + 1] & 0x0F) << 2)
      +                | ((result[i + 2] & 0xC0) >> 6)],
      +            cb64[result[i+2]&0x3F]
                       ];            
           }
      -    return password;
      +    return password;
       }
       
      -- (NSString *)hexadecimalRepresentation:(unsigned char *)result {
      +- (NSString *)hexadecimalRepresentation:(unsigned char *)result {
           NSString *password=[[NSString alloc] init];
      -    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
      -        password=[password stringByAppendingFormat:@"%02x", result[i]];
      +    for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
      +        password=[password stringByAppendingFormat:@"%02x", result[i]];
           }
      -    return password;
      +    return password;
       }
       
       - (NSString *)b64_sha1:(NSString *)inputString {
      -    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
      +    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
           [self sha1:inputString result:result];
      -    return [self base64:result];
      +    return [self base64:result];
       }
       
       - (NSString *)hex_sha1:(NSString *)inputString {
      -    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
      +    unsigned char result[CC_SHA1_DIGEST_LENGTH+1];
           [self sha1:inputString result:result];
      -    return [self hexadecimalRepresentation:result];
      +    return [self hexadecimalRepresentation:result];
       }
      -
      -
      + +
      diff --git a/output/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/index.html b/output/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/index.html index 6f1ba2245..941810e71 100644 --- a/output/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/index.html +++ b/output/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,24 +57,24 @@
      -

      Vous avez pu constater que j’ai modifié le design de mon blog. -Maintenant il doit être beaucoup plus léger qu’avant. -Je n’utilise plus de CSS3 et beaucoup moins de javascript. +

      Vous avez pu constater que j’ai modifié le design de mon blog. +Maintenant il doit être beaucoup plus léger qu’avant. +Je n’utilise plus de CSS3 et beaucoup moins de javascript. Bien entendu, même avant, mes pages étaient parfaitement lisibles sans javascript. -Mais, je me suis aperçu que les systèmes de CSS3 sont loin d’être au point. -J’utilisait des gradient en CSS3, ainsi que des ombres sous le texte. Ça avait un rendu très sympa. Sauf… -Ce n’était pas compatible ie6, sous Chrome le rendu était d’une lenteur incroyable. -J’ai donc décidé de faire un site à minima. -Je voulais qu’il soit joli et le plus simple possible pour assurer sa compatibilité. +Mais, je me suis aperçu que les systèmes de CSS3 sont loin d’être au point. +J’utilisait des gradient en CSS3, ainsi que des ombres sous le texte. Ça avait un rendu très sympa. Sauf… +Ce n’était pas compatible ie6, sous Chrome le rendu était d’une lenteur incroyable. +J’ai donc décidé de faire un site à minima. +Je voulais qu’il soit joli et le plus simple possible pour assurer sa compatibilité. Les règles que je me suis fixées sont donc:

        -
      • pas d’élément CSS qui commence par -moz ou -webkit, etc… ;
      • -
      • pas d’ombre sous le texte pour donner une impression de profondeur ;
      • +
      • pas d’élément CSS qui commence par -moz ou -webkit, etc… ;
      • +
      • pas d’ombre sous le texte pour donner une impression de profondeur ;
      • nettoyer pas mal le code et enlever tout ce que je peux ;
      -

      J’espère que ce nouveau design vous plaît.

      +

      J’espère que ce nouveau design vous plaît.

      diff --git a/output/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html b/output/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html index 1e12f879b..e6a24b53f 100644 --- a/output/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html +++ b/output/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,7 +57,7 @@
      -

      Title image

      +

      Title image

      @@ -66,23 +69,23 @@
    2. Récupérez un certificat signé par une AC: cliquez ici pour un certificat gratuit ;
    3. ouvrez le fichier ;
    4. supprimer le fichier en mode sécurisé ;
    5. -
    6. utilisez Mail plutôt que l’interface web de gmail.
    7. +
    8. utilisez Mail plutôt que l’interface web de gmail.
    -

    J’ai (re)découvert comment adoptez la norme S/MIME. -J’ai été surpris de voir à quel point ce fut aisé. -Il y a seulement quelques années c’était bien plus difficile à accomplir. +

    J’ai (re)découvert comment adoptez la norme S/MIME. +J’ai été surpris de voir à quel point ce fut aisé. +Il y a seulement quelques années c’était bien plus difficile à accomplir. Maintenant je peux signer et chiffrer mes mails.

    Pourquoi est-ce important ?

    -

    Signer : cela permet de certifier avec une absolue certitude que la personne qui a écrit le mail est vous ou au moins qu’elle a utilisé votre ordinateur.

    +

    Signer : cela permet de certifier avec une absolue certitude que la personne qui a écrit le mail est vous ou au moins qu’elle a utilisé votre ordinateur.

    -

    Chiffrer : parce que parfois il est nécessaire d’être certain qu’une conversation reste privée.

    +

    Chiffrer : parce que parfois il est nécessaire d’être certain qu’une conversation reste privée.

    Comment procéder ?

    @@ -90,12 +93,12 @@ Maintenant je peux signer et chiffrer mes mails.

  • Récupérez un certificat signé par une authorité de certification : cliquez ici pour récupérer un certificat gratuit ;
  • ouvrez le fichier ;
  • supprimer le fichier en mode sécurisé ;
  • -
  • utilisez Mail plutôt que l’interface web de gmail. +
  • utilisez Mail plutôt que l’interface web de gmail. Maintenant vous devriez voir ces icônes : no name
  • -

    n.b. : si vous utilisez gmail et que vous ne travaillez pas toujours avec un Mac, vous devriez considérer d’utiliser le module gmail S/MIME de firefox.

    +

    n.b. : si vous utilisez gmail et que vous ne travaillez pas toujours avec un Mac, vous devriez considérer d’utiliser le module gmail S/MIME de firefox.

    @@ -212,7 +215,7 @@ Maintenant vous devriez voir ces icônes :
    Écrit le : 10/10/2010 - modifié le : 10/10/2010 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.c b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.c index 214ab6ee5..aa3982825 100644 --- a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.c +++ b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.c @@ -1,4 +1,3 @@ - #include #include #include diff --git a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.py b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.py index ddeedd473..c0d400125 100644 --- a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.py +++ b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python from struct import calcsize, unpack from sys import argv, exit diff --git a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.rb b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.rb index fc63899b5..aa3c90678 100644 --- a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.rb +++ b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum.rb @@ -1,4 +1,3 @@ - data = ARGF.read keys = %w[id totallength wavefmt format pcm channels frequency bytes_per_second diff --git a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum2.c b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum2.c index 0f9850943..48b713868 100644 --- a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum2.c +++ b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/code/wavsum2.c @@ -1,4 +1,3 @@ - #include #include #include // for memcmp diff --git a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/index.html b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/index.html index 99406b376..e798754cd 100644 --- a/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/index.html +++ b/output/Scratch/fr/blog/2010-10-14-Fun-with-wav/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,322 +69,315 @@
    -

    J’ai eu besoin de calculer la somme des valeurs absolue des données d’un fichier wav. -Pour des raison d’efficacité (et aussi de fun), j’ai fait le programme en C.

    +

    J’ai eu besoin de calculer la somme des valeurs absolue des données d’un fichier wav. +Pour des raison d’efficacité (et aussi de fun), j’ai fait le programme en C.

    -

    Celà faisait longtemps que je n’avais pas programmé en C. +

    Celà faisait longtemps que je n’avais pas programmé en C. De mémoire il était peu aisé de manipuler des fichiers. -Mais je dois concéder que j’ai été étonné de la clarté du code que j’ai obtenu.

    +Mais je dois concéder que j’ai été étonné de la clarté du code que j’ai obtenu.

    -

    Tout d’abord, un fichier wav se compose d’un entête qui contient pas mal de meta données. +

    Tout d’abord, un fichier wav se compose d’un entête qui contient pas mal de meta données. Cet entête a été optimisé pour prendre peu de place. -Donc on discute de l’entête avec des nombres d’octets :

    +Donc on discute de l’entête avec des nombres d’octets :

    • Les 4 premiers octets doivent contenir RIFF en ASCII ;
    • les 4 octects suivant correspondent à un entier codé sur 32 bits qui donne la taille du fichier moins 8 octets. etc..
    -

    Etonnamment je pense que lire ce type de fichier avec un langage de haut niveau aurait été plus pénible qu’en C. -La preuve, il m’a suffit de chercher sur le net le format complet de l’entête et de l’écrire dans un struct.

    +

    Etonnamment je pense que lire ce type de fichier avec un langage de haut niveau aurait été plus pénible qu’en C. +La preuve, il m’a suffit de chercher sur le net le format complet de l’entête et de l’écrire dans un struct.

    -
    -struct wavfile
    +
    struct wavfile
     {
    -    char        id[4];          // should always contain "RIFF"
    -    int     totallength;    // total file length minus 8
    -    char        wavefmt[8];     // should be "WAVEfmt "
    -    int     format;         // 16 for PCM format
    -    short     pcm;            // 1 for PCM format
    -    short     channels;       // channels
    -    int     frequency;      // sampling frequency
    -    int     bytes_per_second;
    -    short     bytes_by_capture;
    -    short     bits_per_sample;
    -    char        data[4];        // should always contain "data"
    -    int     bytes_in_data;
    +    char        id[4];          // should always contain "RIFF"
    +    int     totallength;    // total file length minus 8
    +    char        wavefmt[8];     // should be "WAVEfmt "
    +    int     format;         // 16 for PCM format
    +    short     pcm;            // 1 for PCM format
    +    short     channels;       // channels
    +    int     frequency;      // sampling frequency
    +    int     bytes_per_second;
    +    short     bytes_by_capture;
    +    short     bits_per_sample;
    +    char        data[4];        // should always contain "data"
    +    int     bytes_in_data;
     };
    -
    +
    -

    Si j’avais eu à faire ça en Ruby, je pense qu’il m’aurait fallu pour chaque bloc de l’entête écrire un bout de code de lecture du bon nombre d’octets. -Alors qu’en C il m’a suffit d’écrire:

    +

    Si j’avais eu à faire ça en Ruby, je pense qu’il m’aurait fallu pour chaque bloc de l’entête écrire un bout de code de lecture du bon nombre d’octets. +Alors qu’en C il m’a suffit d’écrire:

    -
    -fread(&header,sizeof(header),1,wav)
    -
    +
    fread(&header,sizeof(header),1,wav)
    +

    Et en une seule étape ma structure de donnée a été remplie avec les valeurs souhaitées. Magique !

    -

    Ensuite, récupérer un entier à partir de deux octets n’est pas non plus une opération naturelle dans les nouveaux langages de programmation. -Alors qu’en C. Pour récupérer un entier codé sur 16 bits il suffit d’écrire :

    +

    Ensuite, récupérer un entier à partir de deux octets n’est pas non plus une opération naturelle dans les nouveaux langages de programmation. +Alors qu’en C. Pour récupérer un entier codé sur 16 bits il suffit d’écrire :

    -
    -short value=0;
    -while( fread(&value,sizeof(value),1,wav) ) {
    -    // do something with value
    +
    short value=0;
    +while( fread(&value,sizeof(value),1,wav) ) {
    +    // do something with value
     }
    -
    +

    Finallement je suis arrivé au code suivant, sachant que le format de wav était connu, avec notamment échantillonage sur 16 bits en 48000Hz :

    -
    -
    -#include <stdio.h>
    -#include <stdlib.h>
    -#include <stdint.h>
    +
     
    -struct wavfile
    +
    #include <stdio.h>
    +#include <stdlib.h>
    +#include <stdint.h>
    +
    +struct wavfile
     {
    -    char        id[4];          // should always contain "RIFF"
    -    int     totallength;    // total file length minus 8
    -    char        wavefmt[8];     // should be "WAVEfmt "
    -    int     format;         // 16 for PCM format
    -    short     pcm;            // 1 for PCM format
    -    short     channels;       // channels
    -    int     frequency;      // sampling frequency
    -    int     bytes_per_second;
    -    short     bytes_by_capture;
    -    short     bits_per_sample;
    -    char        data[4];        // should always contain "data"
    -    int     bytes_in_data;
    +    char        id[4];          // should always contain "RIFF"
    +    int     totallength;    // total file length minus 8
    +    char        wavefmt[8];     // should be "WAVEfmt "
    +    int     format;         // 16 for PCM format
    +    short     pcm;            // 1 for PCM format
    +    short     channels;       // channels
    +    int     frequency;      // sampling frequency
    +    int     bytes_per_second;
    +    short     bytes_by_capture;
    +    short     bits_per_sample;
    +    char        data[4];        // should always contain "data"
    +    int     bytes_in_data;
     };
     
    -int main(int argc, char *argv[]) {
    -    char *filename=argv[1];
    -    FILE *wav = fopen(filename,"rb");
    -    struct wavfile header;
    +int main(int argc, char *argv[]) {
    +    char *filename=argv[1];
    +    FILE *wav = fopen(filename,"rb");
    +    struct wavfile header;
     
    -    if ( wav == NULL ) {
    -        fprintf(stderr,"Can't open input file %s", filename);
    -        exit(1);
    +    if ( wav == NULL ) {
    +        fprintf(stderr,"Can't open input file %s", filename);
    +        exit(1);
         }
     
     
    -    // read header
    -    if ( fread(&header,sizeof(header),1,wav) < 1 )
    +    // read header
    +    if ( fread(&header,sizeof(header),1,wav) < 1 )
         {
    -        fprintf(stderr,"Can't read file header\n");
    -        exit(1);
    +        fprintf(stderr,"Can't read file header\n");
    +        exit(1);
         }
    -    if (    header.id[0] != 'R'
    -         || header.id[1] != 'I' 
    -         || header.id[2] != 'F' 
    -         || header.id[3] != 'F' ) { 
    -        fprintf(stderr,"ERROR: Not wav format\n"); 
    -        exit(1); 
    +    if (    header.id[0] != 'R'
    +         || header.id[1] != 'I' 
    +         || header.id[2] != 'F' 
    +         || header.id[3] != 'F' ) { 
    +        fprintf(stderr,"ERROR: Not wav format\n"); 
    +        exit(1); 
         }
     
    -    fprintf(stderr,"wav format\n");
    +    fprintf(stderr,"wav format\n");
     
    -    // read data
    -    long sum=0;
    -    short value=0;
    -    while( fread(&value,sizeof(value),1,wav) ) {
    -        // fprintf(stderr,"%d\n", value);
    -        if (value<0) { value=-value; }
    +    // read data
    +    long sum=0;
    +    short value=0;
    +    while( fread(&value,sizeof(value),1,wav) ) {
    +        // fprintf(stderr,"%d\n", value);
    +        if (value<0) { value=-value; }
             sum += value;
         }
    -    printf("%ld\n",sum);
    -    exit(0);
    +    printf("%ld\n",sum);
    +    exit(0);
     }
    -
    -
    + -

    Bien entendu ce code n’est qu’un hack. +

    Bien entendu ce code n’est qu’un hack. Mais on voit bien comment on peut facilement améliorer ce code, ajouter des cas possibles par exemple. Comme je dis souvent : le bon outil pour la bonne tâche. On voit en effet que pour cette tâche C est bien supérieur à Ruby par exemple.

    -

    _màj : pour des raisons de compatibilité (machines 64 bits) j’ai utilisé int16_t au lieu de short et int au lieu de int.

    +

    _màj : pour des raisons de compatibilité (machines 64 bits) j’ai utilisé int16_t au lieu de short et int au lieu de int.

    -

    Je serai curieux de savoir s’il existe un manière plus propre en Ruby que je ne connais pas. -Certainement qu’en Python ça doit être la cas.

    +

    Je serai curieux de savoir s’il existe un manière plus propre en Ruby que je ne connais pas. +Certainement qu’en Python ça doit être la cas.

    Màj (2) : après toutes les remarques concernant la portabilité. -J’ai fait une nouvelle version qui devrait être plus portable. +J’ai fait une nouvelle version qui devrait être plus portable. Elle fait aussi plus de test pour vérifier le fichier. -Cependant j’utilise une assertion spécifique à gcc pour être certain que la structure de donnée n’ai pas de “trou” :

    +Cependant j’utilise une assertion spécifique à gcc pour être certain que la structure de donnée n’ai pas de “trou” :

    -
    -__attribute__((__packed__))
    -
    +
    __attribute__((__packed__))
    +
    -

    Le nouveau code n’utilise pas mmap et devrait être plus compatible.
    +

    Le nouveau code n’utilise pas mmap et devrait être plus compatible.
    Voici le dernier résultat :

    -
    -
    -#include <stdio.h>
    -#include <stdlib.h>
    -#include <string.h> // for memcmp
    -#include <stdint.h> // for int16_t and int32_t
    +
     
    -struct wavfile
    +
    #include <stdio.h>
    +#include <stdlib.h>
    +#include <string.h> // for memcmp
    +#include <stdint.h> // for int16_t and int32_t
    +
    +struct wavfile
     {
    -    char    id[4];          // should always contain "RIFF"
    -    int32_t totallength;    // total file length minus 8
    -    char    wavefmt[8];     // should be "WAVEfmt "
    -    int32_t format;         // 16 for PCM format
    -    int16_t pcm;            // 1 for PCM format
    -    int16_t channels;       // channels
    -    int32_t frequency;      // sampling frequency
    -    int32_t bytes_per_second;
    -    int16_t bytes_by_capture;
    -    int16_t bits_per_sample;
    -    char    data[4];        // should always contain "data"
    -    int32_t bytes_in_data;
    +    char    id[4];          // should always contain "RIFF"
    +    int32_t totallength;    // total file length minus 8
    +    char    wavefmt[8];     // should be "WAVEfmt "
    +    int32_t format;         // 16 for PCM format
    +    int16_t pcm;            // 1 for PCM format
    +    int16_t channels;       // channels
    +    int32_t frequency;      // sampling frequency
    +    int32_t bytes_per_second;
    +    int16_t bytes_by_capture;
    +    int16_t bits_per_sample;
    +    char    data[4];        // should always contain "data"
    +    int32_t bytes_in_data;
     } __attribute__((__packed__));
     
    -int is_big_endian(void) {
    -    union {
    -        uint32_t i;
    -        char c[4];
    -    } bint = {0x01000000};
    -    return bint.c[0]==1;
    +int is_big_endian(void) {
    +    union {
    +        uint32_t i;
    +        char c[4];
    +    } bint = {0x01000000};
    +    return bint.c[0]==1;
     }
     
    -int main(int argc, char *argv[]) {
    -    char *filename=argv[1];
    -    FILE *wav = fopen(filename,"rb");
    -    struct wavfile header;
    +int main(int argc, char *argv[]) {
    +    char *filename=argv[1];
    +    FILE *wav = fopen(filename,"rb");
    +    struct wavfile header;
     
    -    if ( wav == NULL ) {
    -        fprintf(stderr,"Can't open input file %s\n", filename);
    -        exit(1);
    +    if ( wav == NULL ) {
    +        fprintf(stderr,"Can't open input file %s\n", filename);
    +        exit(1);
         }
     
     
    -    // read header
    -    if ( fread(&header,sizeof(header),1,wav) < 1 ) {
    -        fprintf(stderr,"Can't read input file header %s\n", filename);
    -        exit(1);
    +    // read header
    +    if ( fread(&header,sizeof(header),1,wav) < 1 ) {
    +        fprintf(stderr,"Can't read input file header %s\n", filename);
    +        exit(1);
         }
     
    -    // if wav file isn't the same endianness than the current environment
    -    // we quit
    -    if ( is_big_endian() ) {
    -        if (   memcmp( header.id,"RIFX", 4) != 0 ) {
    -            fprintf(stderr,"ERROR: %s is not a big endian wav file\n", filename); 
    -            exit(1);
    +    // if wav file isn't the same endianness than the current environment
    +    // we quit
    +    if ( is_big_endian() ) {
    +        if (   memcmp( header.id,"RIFX", 4) != 0 ) {
    +            fprintf(stderr,"ERROR: %s is not a big endian wav file\n", filename); 
    +            exit(1);
             }
    -    } else {
    -        if (   memcmp( header.id,"RIFF", 4) != 0 ) {
    -            fprintf(stderr,"ERROR: %s is not a little endian wav file\n", filename); 
    -            exit(1);
    +    } else {
    +        if (   memcmp( header.id,"RIFF", 4) != 0 ) {
    +            fprintf(stderr,"ERROR: %s is not a little endian wav file\n", filename); 
    +            exit(1);
             }
         }
     
    -    if (   memcmp( header.wavefmt, "WAVEfmt ", 8) != 0 
    -        || memcmp( header.data, "data", 4) != 0 
    +    if (   memcmp( header.wavefmt, "WAVEfmt ", 8) != 0 
    +        || memcmp( header.data, "data", 4) != 0 
                 ) {
    -        fprintf(stderr,"ERROR: Not wav format\n"); 
    -        exit(1); 
    +        fprintf(stderr,"ERROR: Not wav format\n"); 
    +        exit(1); 
         }
    -    if (header.format != 16) {
    -        fprintf(stderr,"\nERROR: not 16 bit wav format.");
    -        exit(1);
    +    if (header.format != 16) {
    +        fprintf(stderr,"\nERROR: not 16 bit wav format.");
    +        exit(1);
         }
    -    fprintf(stderr,"format: %d bits", header.format);
    -    if (header.format == 16) {
    -        fprintf(stderr,", PCM");
    -    } else {
    -        fprintf(stderr,", not PCM (%d)", header.format);
    +    fprintf(stderr,"format: %d bits", header.format);
    +    if (header.format == 16) {
    +        fprintf(stderr,", PCM");
    +    } else {
    +        fprintf(stderr,", not PCM (%d)", header.format);
         }
    -    if (header.pcm == 1) {
    -        fprintf(stderr, " uncompressed" );
    -    } else {
    -        fprintf(stderr, " compressed" );
    +    if (header.pcm == 1) {
    +        fprintf(stderr, " uncompressed" );
    +    } else {
    +        fprintf(stderr, " compressed" );
         }
    -    fprintf(stderr,", channel %d", header.pcm);
    -    fprintf(stderr,", freq %d", header.frequency );
    -    fprintf(stderr,", %d bytes per sec", header.bytes_per_second );
    -    fprintf(stderr,", %d bytes by capture", header.bytes_by_capture );
    -    fprintf(stderr,", %d bits per sample", header.bytes_by_capture );
    -    fprintf(stderr,"\n" );
    +    fprintf(stderr,", channel %d", header.pcm);
    +    fprintf(stderr,", freq %d", header.frequency );
    +    fprintf(stderr,", %d bytes per sec", header.bytes_per_second );
    +    fprintf(stderr,", %d bytes by capture", header.bytes_by_capture );
    +    fprintf(stderr,", %d bits per sample", header.bytes_by_capture );
    +    fprintf(stderr,"\n" );
     
    -    if ( memcmp( header.data, "data", 4) != 0 ) { 
    -        fprintf(stderr,"ERROR: Prrroblem?\n"); 
    -        exit(1); 
    +    if ( memcmp( header.data, "data", 4) != 0 ) { 
    +        fprintf(stderr,"ERROR: Prrroblem?\n"); 
    +        exit(1); 
         }
    -    fprintf(stderr,"wav format\n");
    +    fprintf(stderr,"wav format\n");
     
    -    // read data
    -    long long sum=0;
    -    int16_t value;
    -    int i=0;
    -    fprintf(stderr,"---\n", value);
    -    while( fread(&value,sizeof(value),1,wav) ) {
    -        if (value<0) { value=-value; }
    +    // read data
    +    long long sum=0;
    +    int16_t value;
    +    int i=0;
    +    fprintf(stderr,"---\n", value);
    +    while( fread(&value,sizeof(value),1,wav) ) {
    +        if (value<0) { value=-value; }
             sum += value;
         }
    -    printf("%lld\n",sum);
    -    exit(0);
    +    printf("%lld\n",sum);
    +    exit(0);
     }
    -
    -
    +

    Màj(3) : Sur reddit Bogdanp a proposé une version en Python :

    -
    -
    -#!/usr/bin/env python
    -from struct import calcsize, unpack
    -from sys import argv, exit
    +
     
    -def word_iter(f):
    -    while True:
    -        _bytes = f.read(2)
    +
    #!/usr/bin/env python
    +from struct import calcsize, unpack
    +from sys import argv, exit
     
    -    if len(_bytes) != 2:
    -        raise StopIteration
    +def word_iter(f):
    +    while True:
    +        _bytes = f.read(2)
     
    -    yield unpack("=h", _bytes)[0]
    +    if len(_bytes) != 2:
    +        raise StopIteration
     
    -try:
    -    with open(argv[1], "rb") as f:
    -        wav = "=4ci8cihhiihh4ci"
    -        wav_size = calcsize(wav)
    -        metadata = unpack(wav, f.read(wav_size))
    +    yield unpack("=h", _bytes)[0]
     
    -        if "".join(metadata[:4]) != "RIFF":
    -            print "error: not wav file."
    -            exit(1)
    +try:
    +    with open(argv[1], "rb") as f:
    +        wav = "=4ci8cihhiihh4ci"
    +        wav_size = calcsize(wav)
    +        metadata = unpack(wav, f.read(wav_size))
     
    -        print sum(abs(word) for word in word_iter(f))
    -except IOError:
    -    print "error: can't open input file '%s'." % argv[1]
    -    exit(1)
    -
    -
    + if "".join(metadata[:4]) != "RIFF": + print "error: not wav file." + exit(1) + + print sum(abs(word) for word in word_iter(f)) +except IOError: + print "error: can't open input file '%s'." % argv[1] + exit(1) +

    et luikore a proposé une version Ruby assez impressionnante :

    -
    -
    -data = ARGF.read
    - keys = %w[id totallength wavefmt format
    -       pcm channels frequency bytes_per_second
    -         bytes_by_capture bits_per_sample
    -           data bytes_in_data sum
    - ]
    - values = data.unpack 'Z4 i Z8 i s s i i s s Z4 i s*'
    - sum = values.drop(12).map(&:abs).inject(:+)
    - keys.zip(values.take(12) << sum) {|k, v|
    -       puts "#{k.ljust 17}: #{v}"
    +
    +
    +
    data = ARGF.read
    + keys = %w[id totallength wavefmt format
    +       pcm channels frequency bytes_per_second
    +         bytes_by_capture bits_per_sample
    +           data bytes_in_data sum
    + ]
    + values = data.unpack 'Z4 i Z8 i s s i i s s Z4 i s*'
    + sum = values.drop(12).map(&:abs).inject(:+)
    + keys.zip(values.take(12) << sum) {|k, v|
    +       puts "#{k.ljust 17}: #{v}"
      }
    -
    -
    + +
    diff --git a/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb b/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb index 16c0e6d86..17ebf5524 100644 --- a/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb +++ b/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/code/macros.rb @@ -1,4 +1,3 @@ - # usage: # --- # ... diff --git a/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html b/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html index a2fd204f6..2b4efcf2d 100644 --- a/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html +++ b/output/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -58,24 +61,23 @@
    -

    tlàl : J’ai fait un système simple de macros pour mon blog. Par exemple, il me suffit d’écrire %latex et ça affiche LaTeX.

    +

    tlàl : J’ai fait un système simple de macros pour mon blog. Par exemple, il me suffit d’écrire %latex et ça affiche LaTeX.

    -

    J’ai ajouter un système de macro pour mon système de blog. -Lorsqu’on est habitué à LaTeX et que l’on commence à écrire des articles +

    J’ai ajouter un système de macro pour mon système de blog. +Lorsqu’on est habitué à LaTeX et que l’on commence à écrire des articles un peu conséquent avec des notations mathématiques, -les macros deviennent vite quelque chose d’indispensable.

    +les macros deviennent vite quelque chose d’indispensable.

    -

    Dans l’entête de mes fichiers j’écris simplement:

    +

    Dans l’entête de mes fichiers j’écris simplement:

    -
    -macros:
    -  test: "This is a macro test"
    -  latex: '<span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>'
    -
    +
    macros:
    +  test: "This is a macro test"
    +  latex: '<span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>'
    +

    Puis dans le corps ça va remplacer :

    @@ -87,53 +89,52 @@ les macros deviennent vite quelque chose d’indispensable.

    Le code est assez simple. Pour les utilisateurs de nanoc il suffit de copier le fichier suivant dans le répertoire lib.

    -
    -
    -# usage:
    -# ---
    -# ...
    -# macros:
    -#   test: "passed test"
    -# ---
    -# ...
    -# Here is a This is a macro test.
    -#
    -class Macros < Nanoc3::Filter
    -    identifier :falacy
    -    attr_accessor :macro
    -    def initialize(arg)
    -        super
    -        @macro={}
    -        @macro[:tlal] = %{<span class="sc"><abbr title="Trop long à lire">tlàl</abbr> : </span>}
    -        @macro[:tldr] = %{<span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span>}
    -        if @item.nil?
    -            if not arg.nil?
    -                @macro.merge!( arg )
    -            end
    -        else
    -            if not @item[:macros].nil?
    -                @macro.merge!( @item[:macros] )
    -            end
    -        end
    -    end
    -    def macro_value_for(macro_name)
    -        if macro_name.nil? or macro_name=="" or @macro[macro_name.intern].nil?
    -            return %{%#{macro_name}} 
    -        end
    -        return @macro[macro_name.intern]
    -    end
    -    def run(content, params={})
    -        content.gsub(/%(\w*)/) do |m| 
    -            if m != '%'
    -                macro_value_for($1)
    -            else
    +
    +
    +
    # usage:
    +# ---
    +# ...
    +# macros:
    +#   test: "passed test"
    +# ---
    +# ...
    +# Here is a This is a macro test.
    +#
    +class Macros < Nanoc3::Filter
    +    identifier :falacy
    +    attr_accessor :macro
    +    def initialize(arg)
    +        super
    +        @macro={}
    +        @macro[:tlal] = %{<span class="sc"><abbr title="Trop long à lire">tlàl</abbr> : </span>}
    +        @macro[:tldr] = %{<span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span>}
    +        if @item.nil?
    +            if not arg.nil?
    +                @macro.merge!( arg )
    +            end
    +        else
    +            if not @item[:macros].nil?
    +                @macro.merge!( @item[:macros] )
    +            end
    +        end
    +    end
    +    def macro_value_for(macro_name)
    +        if macro_name.nil? or macro_name=="" or @macro[macro_name.intern].nil?
    +            return %{%#{macro_name}} 
    +        end
    +        return @macro[macro_name.intern]
    +    end
    +    def run(content, params={})
    +        content.gsub(/%(\w*)/) do |m| 
    +            if m != '%'
    +                macro_value_for($1)
    +            else
                     m
    -            end
    -        end
    -    end
    -end
    -
    -
    + end + end + end +end +

    Les macros peuvent être vraiment utiles. Lisez cet article par exemple.

    diff --git a/output/Scratch/fr/blog/2011-01-03-Happy-New-Year/index.html b/output/Scratch/fr/blog/2011-01-03-Happy-New-Year/index.html index 60a9311ad..97d327977 100644 --- a/output/Scratch/fr/blog/2011-01-03-Happy-New-Year/index.html +++ b/output/Scratch/fr/blog/2011-01-03-Happy-New-Year/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,15 +57,15 @@

    Bonne et heureuse année !

    -

    J’étais très occupé ces derniers mois. +

    J’étais très occupé ces derniers mois. Maintenant il me semble que je vais pouvoir faire revivre ce blog.

    -

    J’ai fait un outil qui permet d’écrire des livre en utilisant une syntaxe proche de markdown. -C’est un markdown avec des macros (essentiel pour les textes longs). +

    J’ai fait un outil qui permet d’écrire des livre en utilisant une syntaxe proche de markdown. +C’est un markdown avec des macros (essentiel pour les textes longs). De plus le système gère la génération de pages HTML ainsi que du PDF engendré avec du XeLaTeX. -Je n’en ai pas encore terminé avec ça. Mais si je tarde trop, je communiquerai lorsque j’aurai fini le minimum.

    +Je n’en ai pas encore terminé avec ça. Mais si je tarde trop, je communiquerai lorsque j’aurai fini le minimum.

    -

    J’ai écrit un framework MVC pour application javascript simple mais néanmoins très rapide.

    +

    J’ai écrit un framework MVC pour application javascript simple mais néanmoins très rapide.

    Meilleurs vœux à tous !

    diff --git a/output/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html b/output/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html index c0a275e7d..70e7e3424 100644 --- a/output/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html +++ b/output/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,9 +63,9 @@
    -

    Mise à jour : Je pense que je vais finallement changer d’avis. +

    Mise à jour : Je pense que je vais finallement changer d’avis. Pourquoi ? -Tout d’abord, je viens de découvrir un convertisseur javascript vers coffeescript, ensuite Denis Knauf m’a laissé un commentaire et m’a appris l’existence d’une fonction CoffeeScript.eval. De plus, il faut voir CoffeeScript comme javascript avec une syntaxe similaire à Ruby et pas comme un langage similaire à Ruby.

    +Tout d’abord, je viens de découvrir un convertisseur javascript vers coffeescript, ensuite Denis Knauf m’a laissé un commentaire et m’a appris l’existence d’une fonction CoffeeScript.eval. De plus, il faut voir CoffeeScript comme javascript avec une syntaxe similaire à Ruby et pas comme un langage similaire à Ruby.

    @@ -72,14 +75,14 @@ Tout d’abord, je viens de découvrir un convertisseur javascript vers coff
    -

    tlàl : Qu’est-ce qui n’allait pas avec Coffeescript? La meta-programmation, il faut le “vendre” aux autres, une nouvelle étape de compilation intermédiaire sans fournir les avantages de Cappuccino, la sensation que c’est un peu instable.

    +

    tlàl : Qu’est-ce qui n’allait pas avec Coffeescript? La meta-programmation, il faut le “vendre” aux autres, une nouvelle étape de compilation intermédiaire sans fournir les avantages de Cappuccino, la sensation que c’est un peu instable.

    Le commentaire le mieux classé de la question suivante posée sur HackerNews mentionnait CoffeeScript. -Récemment j’ai beaucoup programmé en javascript. +Récemment j’ai beaucoup programmé en javascript. Après avoir essayé Sroutcore, Cappuccino, @@ -87,29 +90,29 @@ Après avoir essayé javascriptMVC, Je me suis décidé à créer mon propre framework MVC minimal pour client javascript.1

    -

    Je me suis battu avec l’horrible syntaxe de javascript. C’était comme revenir des années dans le passé :

    +

    Je me suis battu avec l’horrible syntaxe de javascript. C’était comme revenir des années dans le passé :

    • une syntaxe à la Java très verbeuse ;
    • une syntaxe follement verbeuse et étrange pour la programmation orientée objet ;
    • -
    • pas de manière naturelle de se référer à l’instance d’une classe ;
    • -
    • etc…
    • +
    • pas de manière naturelle de se référer à l’instance d’une classe ;
    • +
    • etc…
    -

    J’étais tellement ennuyé par tous ces point qu’il était arrivé un moment où je commençais à vouloir faire mon propre CoffeeScript.

    +

    J’étais tellement ennuyé par tous ces point qu’il était arrivé un moment où je commençais à vouloir faire mon propre CoffeeScript.

    -

    J’ai fini une première version de mon framework MVC en javascript et j’ai appris l’existence de CoffeeScript. Merci à git, j’ai immédiatement créé une nouvelle branche dans le seul but d’essayer CoffeeScript.

    +

    J’ai fini une première version de mon framework MVC en javascript et j’ai appris l’existence de CoffeeScript. Merci à git, j’ai immédiatement créé une nouvelle branche dans le seul but d’essayer CoffeeScript.

    Voici mon expérience :

      -
    1. J’ai dû installer node.js et utiliser npm simplement pour utiliser CoffeeScript. Ce n’était pas très difficile, mais pas aussi facile que ce que j’aurai aimé.
    2. +
    3. J’ai dû installer node.js et utiliser npm simplement pour utiliser CoffeeScript. Ce n’était pas très difficile, mais pas aussi facile que ce que j’aurai aimé.
    4. Les fichier javascript existants ne sont pas compatible avec coffee.
    5. -
    6. Il n’y a pas script pour aider à transformer les anciens fichiers javascripts en fichier coffee. Du coups j’ai dû faire ça manuellement. +
    7. Il n’y a pas script pour aider à transformer les anciens fichiers javascripts en fichier coffee. Du coups j’ai dû faire ça manuellement. Merci à vim, il ne fut pas très difficile de transformer 90% des fichiers avec des expressions régulières. - L’option --watch de coffee était très utile pour debugger cette transformation. - Cependant, il m’a fallu écrire mon propre script pour que tous mes fichiers soient watchés dans tous les sous-répertoires.
    8. -
    9. Quelque chose à laquelle je n’avais pas pensé. J’ai fait un peu de meta-programmation en javascript en utilisant eval. Mais pour que celà fonctionne correctement, il faut que la chaîne de caractère que je passe à eval soit codée en javascript et pas en coffee. C’est un peu comme écrire dans deux langages différents au même endroit. Ça ne me parraissait vraiment pas agréable.
    10. + L’option --watch de coffee était très utile pour debugger cette transformation. + Cependant, il m’a fallu écrire mon propre script pour que tous mes fichiers soient watchés dans tous les sous-répertoires. +
    11. Quelque chose à laquelle je n’avais pas pensé. J’ai fait un peu de meta-programmation en javascript en utilisant eval. Mais pour que celà fonctionne correctement, il faut que la chaîne de caractère que je passe à eval soit codée en javascript et pas en coffee. C’est un peu comme écrire dans deux langages différents au même endroit. Ça ne me parraissait vraiment pas agréable.

    Conclusion

    @@ -118,75 +121,73 @@ Je me suis décidé à créer mon propre framework MVC minimal pour client javas
    • Code plus lisible : CoffeeScript résoud la majorité des problèmes de syntaxes de javascript
    • -
    • Concision : j’ai gagné 14% de lignes, 22% de mots et 14% de caractères.
    • +
    • Concision : j’ai gagné 14% de lignes, 22% de mots et 14% de caractères.

    Inconvénients :

      -
    • Ajout d’une nouvelle étape de compilation avant de pouvoir vérifier le comportement de mon site
    • -
    • Facilité d’utilisation : il m’a fallu créer un script pour gérer la génératio automatique des fichiers
    • +
    • Ajout d’une nouvelle étape de compilation avant de pouvoir vérifier le comportement de mon site
    • +
    • Facilité d’utilisation : il m’a fallu créer un script pour gérer la génératio automatique des fichiers
    • Il faut apprendre un autre langage proche de ruby
    • La meta-programmation devient une expérience désagréable
    • Je dois convaincre les personnes travaillant avec moi :
        -
      • d’installer node.js, npm et CoffeeScript ;
      • +
      • d’installer node.js, npm et CoffeeScript ;
      • de se souvenir de lancer un script à chaque session de codage ;
      • -
      • d’apprendre un autre language proche de ruby.
      • +
      • d’apprendre un autre language proche de ruby.

    Les deux derniers points étant de mon point de vue les plus problématiques.

    -

    Mais même si j’avais à travailler seul, je n’utiliserai certainement pas CoffeeScript. -Il s’agit d’un tier dont la moindre mise à jour pourrait rendre mon code inutilisable. -Cette situation m’est déjà arrivée plusieurs fois et c’est très désagrable. +

    Mais même si j’avais à travailler seul, je n’utiliserai certainement pas CoffeeScript. +Il s’agit d’un tier dont la moindre mise à jour pourrait rendre mon code inutilisable. +Cette situation m’est déjà arrivée plusieurs fois et c’est très désagrable. Beaucoup plus que coder avec une mauvaise syntaxe.

    Digression

    Je suis attristé. -J’espérais tant pouvoir programmer Javascript avec une touche de Ruby. -En fin de compte, cette solution n’est pas pour moi. -Je vais devoir utiliser l’horrible syntaxe javascript pour l’instant. -À la limite j’aurai préféré un script Ruby2Js par exemple2. -Mais il me semble que ça serait un travail très difficile rien que pour simuler l’accès à la classe courante.

    +J’espérais tant pouvoir programmer Javascript avec une touche de Ruby. +En fin de compte, cette solution n’est pas pour moi. +Je vais devoir utiliser l’horrible syntaxe javascript pour l’instant. +À la limite j’aurai préféré un script Ruby2Js par exemple2. +Mais il me semble que ça serait un travail très difficile rien que pour simuler l’accès à la classe courante.

    -

    Typiquement @x est transformé en this.x. Mais le code suivant ne fait pas ce que j’attendrai de lui.

    +

    Typiquement @x est transformé en this.x. Mais le code suivant ne fait pas ce que j’attendrai de lui.

    -
    --> 
    -class MyClass
    -  foo: ->
    -    alert('ok')
    +
    -> 
    +class MyClass
    +  foo: ->
    +    alert('ok')
     
    -  bar: ->
    -    $('#content').load( '/content.html', ( -> @foo(x) ) )
    -    # Ça n'appellera pas MyClass.foo
    -
    + bar: -> + $('#content').load( '/content.html', ( -> @foo(x) ) ) + # Ça n'appellera pas MyClass.foo +

    La seule façon de résoudre ce problème est avec le code suivant :

    -
    --> 
    -class MyClass
    -  foo: ->
    -    alert('ok')
    +
    -> 
    +class MyClass
    +  foo: ->
    +    alert('ok')
     
    -  bar: ->
    -    self=this
    -    $('#content').load( '/content.html', ( -> self.foo(x) ) )
    -
    + bar: -> + self=this + $('#content').load( '/content.html', ( -> self.foo(x) ) ) +

    Sachant celà, la notation @ perd tout son intérêt pour moi.


    1. -

      Je sais que ce n’est certainement ni la meilleure ni la plus productive des décisions. Mais j’aime bien fabriquer les choses pour savoir comment tout fonctionne dans le détail.

      +

      Je sais que ce n’est certainement ni la meilleure ni la plus productive des décisions. Mais j’aime bien fabriquer les choses pour savoir comment tout fonctionne dans le détail.

    2. -

      Je sais qu’il existe un projet rb2js, mais il ne résoud pas le problème dont je parle.

      +

      Je sais qu’il existe un projet rb2js, mais il ne résoud pas le problème dont je parle.

    @@ -306,7 +307,7 @@ Mais il me semble que ça serait un travail très difficile rien que pour simule
    Écrit le : 03/01/2011 - modifié le : 26/10/2011 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/index.html b/output/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/index.html index 9b207aec9..fb32b1d06 100644 --- a/output/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/index.html +++ b/output/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,7 +57,7 @@

    Title image

    -

    J’héberge mon site sur github à partir d’aujourd’hui.

    +

    J’héberge mon site sur github à partir d’aujourd’hui.

    @@ -171,7 +174,7 @@
    Écrit le : 20/04/2011 - modifié le : 20/04/2011 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/A-more-convenient-diff/code/ydiff b/output/Scratch/fr/blog/A-more-convenient-diff/code/ydiff index ebd336f1a..90d6c8589 100644 --- a/output/Scratch/fr/blog/A-more-convenient-diff/code/ydiff +++ b/output/Scratch/fr/blog/A-more-convenient-diff/code/ydiff @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Load colors helpers diff --git a/output/Scratch/fr/blog/A-more-convenient-diff/index.html b/output/Scratch/fr/blog/A-more-convenient-diff/index.html index ecec3d160..7f9b507f6 100644 --- a/output/Scratch/fr/blog/A-more-convenient-diff/index.html +++ b/output/Scratch/fr/blog/A-more-convenient-diff/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,38 +57,38 @@
    -

    diff est un utilitaire très pratique, mais il n’est pas facile à lire pour nous, les Hommes.

    +

    diff est un utilitaire très pratique, mais il n’est pas facile à lire pour nous, les Hommes.

    -

    C’est pourquoi, lorsque vous utilisez git, il vous montre un formatage plus agréable avec des couleurs.

    +

    C’est pourquoi, lorsque vous utilisez git, il vous montre un formatage plus agréable avec des couleurs.

    -

    Voici le script que j’utilise lorsque je veux avoir un diff à la git.

    +

    Voici le script que j’utilise lorsque je veux avoir un diff à la git.

    -
    -
    -#!/usr/bin/env zsh
    +
     
    -# Load colors helpers
    -autoload -U colors && colors
    +
    #!/usr/bin/env zsh
    +
    +# Load colors helpers
    +autoload -U colors && colors
     
     function colorize_diff {
    -    while read line; do
    -    case ${line[0]} in
    -    +) print -n $fg[green];;
    -    -) print -n $fg[red];;
    -    @) # Display in cyan the @@ positions @@
    -       if [[ ${line[1]} = '@' ]]; then
    -           line=$(print $line | perl -pe 's#(\@\@[^\@]*\@\@)(.*)$#'$fg[cyan]'$1'$reset_color'$2#')
    -       fi;;
    +    while read line; do
    +    case ${line[0]} in
    +    +) print -n $fg[green];;
    +    -) print -n $fg[red];;
    +    @) # Display in cyan the @@ positions @@
    +       if [[ ${line[1]} = '@' ]]; then
    +           line=$(print $line | perl -pe 's#(\@\@[^\@]*\@\@)(.*)$#'$fg[cyan]'$1'$reset_color'$2#')
    +       fi;;
     
    -    esac
    -        print -- $line
    -        print -n $reset_color
    -        done
    +    esac
    +        print -- $line
    +        print -n $reset_color
    +        done
     }
     
    -diff -u $* | colorize_diff
    -
    -
    +diff -u $* | colorize_diff + +
    diff --git a/output/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs b/output/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs index 6483d21b2..6a817486a 100644 --- a/output/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs +++ b/output/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs @@ -1,4 +1,3 @@ - a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003) newtype C = C (Double,Double) deriving (Show,Eq) instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0) diff --git a/output/Scratch/fr/blog/Haskell-Mandelbrot/index.html b/output/Scratch/fr/blog/Haskell-Mandelbrot/index.html index e569c536a..5a74993e1 100644 --- a/output/Scratch/fr/blog/Haskell-Mandelbrot/index.html +++ b/output/Scratch/fr/blog/Haskell-Mandelbrot/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,25 +57,23 @@
    -

    Voici le code “obfusqué” :

    +

    Voici le code “obfusqué” :

    -
    -
    -a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003)
    -newtype C = C (Double,Double) deriving (Show,Eq)
    -instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0)
    -r(C(x,y))=x;i(C(x,y))=y
    -f c z 0=0;f c z n=if(r(abs(z))>2)then n else f c ((z*z)+c) (n-1)
    -h j k = map (\z->(f (C z) (C(0,0)) 32,(fst z>l - q/2))) [(x,y)|y<-[p,(p+((o-p)/a))..o],x<-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b
    -u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&$@#"!!i):rst p;rst True="\n";rst False=""
    -main = putStrLn $ im 0 where cl n (C (x,y))=let cs=(1.1**n-1) in C ((x+cs*(r e))/cs+1,(y+cs*(i e))/cs+1);bl n=cl n c;tr n=cl n d;im n=u (bl n) (tr n)++"\x1b[H\x1b[25A"++im (n+1)
    -
    -
    + + +
    a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003)
    +newtype C = C (Double,Double) deriving (Show,Eq)
    +instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0)
    +r(C(x,y))=x;i(C(x,y))=y
    +f c z 0=0;f c z n=if(r(abs(z))>2)then n else f c ((z*z)+c) (n-1)
    +h j k = map (\z->(f (C z) (C(0,0)) 32,(fst z>l - q/2))) [(x,y)|y<-[p,(p+((o-p)/a))..o],x<-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b
    +u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&$@#"!!i):rst p;rst True="\n";rst False=""
    +main = putStrLn $ im 0 where cl n (C (x,y))=let cs=(1.1**n-1) in C ((x+cs*(r e))/cs+1,(y+cs*(i e))/cs+1);bl n=cl n c;tr n=cl n d;im n=u (bl n) (tr n)++"\x1b[H\x1b[25A"++im (n+1)
    +

    Pour le lancer, haskell doit être installé. Puis vous devez écrire dans un terminal :

    -
    ghc --make animandel.hs && animandel
    -
    +
    ghc --make animandel.hs && animandel

    Voici le résultat après 50 itérations.

    @@ -107,37 +108,36 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$&&&&&&&&&&WWWW

    Here is the more readable version. I believe with this far more readable version, no more explanation is needed.

    -
    -nbvert = 30
    +
    nbvert = 30
     nbhor = 79
     zoomfactor = 1.01
    -init_bottom_left = C (-2.0,-2.0)
    -init_top_right   = C (3.0,2.0)
    -interrest        = C (-1.713,-0.000)
    +init_bottom_left = C (-2.0,-2.0)
    +init_top_right   = C (3.0,2.0)
    +interrest        = C (-1.713,-0.000)
     
    -newtype Complex = C (Float,Float) deriving (Show,Eq)
    -instance Num Complex where
    -    fromInteger n     = C (fromIntegral n,0.0)
    -    C (x,y) * C (z,t) = C (z*x - y*t, y*z + x*t)
    -    C (x,y) + C (z,t) = C (x+z, y+t)
    -    abs (C (x,y))     = C (sqrt (x*x + y*y),0.0)
    -    signum (C (x,y))  = C (signum x , 0.0)
    +newtype Complex = C (Float,Float) deriving (Show,Eq)
    +instance Num Complex where
    +    fromInteger n     = C (fromIntegral n,0.0)
    +    C (x,y) * C (z,t) = C (z*x - y*t, y*z + x*t)
    +    C (x,y) + C (z,t) = C (x+z, y+t)
    +    abs (C (x,y))     = C (sqrt (x*x + y*y),0.0)
    +    signum (C (x,y))  = C (signum x , 0.0)
     
    -real :: Complex -> Float
    -real (C (x,y))    = x
    -im :: Complex -> Float
    -im   (C (x,y))    = y
    +real :: Complex -> Float
    +real (C (x,y))    = x
    +im :: Complex -> Float
    +im   (C (x,y))    = y
     
    -cabs :: Complex -> Float
    -cabs = real.abs
    +cabs :: Complex -> Float
    +cabs = real.abs
     
    -f :: Complex -> Complex -> Int -> Int
    +f :: Complex -> Complex -> Int -> Int
     f c z 0 = 0
    -f c z n = if (cabs z > 2) then n else f c ((z*z)+c) (n-1) 
    +f c z n = if (cabs z > 2) then n else f c ((z*z)+c) (n-1) 
     
     
    -bmandel bottomleft topright = map (\z -> (f (C z) (C(0,0)) 32, (fst z > right - hstep/2 ))) [(x,y) | y <- [bottom,(bottom + vstep)..top], x<-[left,(left + hstep)..right]]
    -    where
    +bmandel bottomleft topright = map (\z -> (f (C z) (C(0,0)) 32, (fst z > right - hstep/2 ))) [(x,y) | y <- [bottom,(bottom + vstep)..top], x<-[left,(left + hstep)..right]]
    +    where
             top = im topright
             bottom = im bottomleft
             left = real bottomleft
    @@ -145,31 +145,32 @@ bmandel bottomleft topright = map (\z -> (f (mandel :: (Complex,Complex) -> String
    -mandel (bottomleft,topright) = concat $ map treat $ bmandel bottomleft topright
    -    where
    -        treat (i,jump) = " .,:;rcuowijlbCUOW&$@#" !! (div (i*22) 32):rst jump
    -        rst True = "\n"
    -        rst False = ""
    +mandel :: (Complex,Complex) -> String
    +mandel (bottomleft,topright) = concat $ map treat $ bmandel bottomleft topright
    +    where
    +        treat (i,jump) = " .,:;rcuowijlbCUOW&$@#" !! (div (i*22) 32):rst jump
    +        rst True = "\n"
    +        rst False = ""
     
    -cdiv :: Complex -> Float -> Complex
    -cdiv (C(x,y)) r = C(x/r, y/r) 
    -cmul :: Complex -> Float -> Complex
    -cmul (C(x,y)) r = C(x*r, y*r) 
    +cdiv :: Complex -> Float -> Complex
    +cdiv (C(x,y)) r = C(x/r, y/r) 
    +cmul :: Complex -> Float -> Complex
    +cmul (C(x,y)) r = C(x*r, y*r) 
     
    -zoom :: Complex -> Complex -> Complex -> Float -> (Complex,Complex)
    +zoom :: Complex -> Complex -> Complex -> Float -> (Complex,Complex)
     zoom bl tr center magn = (f bl, f tr)
    -    where
    -        f point = ((center `cmul` magn) + point ) `cdiv` (magn + 1)
    +    where
    +        f point = ((center `cmul` magn) + point ) `cdiv` (magn + 1)
         
     
    -main = do
    -    x <- getContents
    -    putStrLn $ infinitemandel 0
    -    where
    +main = do
    +    x <- getContents
    +    putStrLn $ infinitemandel 0
    +    where
             window n = zoom init_bottom_left init_top_right interrest (zoomfactor**n) 
    -        infinitemandel n = mandel (window n) ++ "\x1b[H\x1b[25A" ++ infinitemandel (n+1)
    -
    + infinitemandel n = mandel (window n) ++ "\x1b[H\x1b[25A" ++ infinitemandel (n+1) +
    +
    diff --git a/output/Scratch/fr/blog/Haskell-the-Hard-Way/index.html b/output/Scratch/fr/blog/Haskell-the-Hard-Way/index.html index 88ba09033..260eb5fc1 100644 --- a/output/Scratch/fr/blog/Haskell-the-Hard-Way/index.html +++ b/output/Scratch/fr/blog/Haskell-the-Hard-Way/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -77,7 +80,7 @@
  • Introduction
    • Install
    • -
    • Don’t be afraid
    • +
    • Don’t be afraid
    • Very basic Haskell
      • Function declaration
      • @@ -153,9 +156,9 @@

        Je pense vraiment que tous les développeurs devraient apprendre Haskell. -Peut-être pas devenir des ninjas d’Haskell, +Peut-être pas devenir des ninjas d’Haskell, mais au moins savoir ce que ce langage a de particulier. -Son apprentissage ouvre énormément l’esprit.

        +Son apprentissage ouvre énormément l’esprit.

        La plupart des langages partagent les mêmes fondamentaux :

        @@ -167,64 +170,64 @@ Son apprentissage ouvre énormément l’esprit.

      Haskell est très différent. -Ce langage utilise des concepts dont je n’avais jamais entendu parlé avant. +Ce langage utilise des concepts dont je n’avais jamais entendu parlé avant. Beaucoup de ces concepts pourront vous aider à devenir un meilleur développeur.

      Plier son esprit à Haskell peut être difficile. Ce le fût pour moi. -Dans cet article, j’essaye de fournir les informations qui m’ont manquées lors de mon apprentissage.

      +Dans cet article, j’essaye de fournir les informations qui m’ont manquées lors de mon apprentissage.

      Cet article sera certainement difficile à suivre. -Mais c’est voulu. -Il n’y a pas de raccourci pour apprendre Haskell. -C’est difficile. -Mais je pense que c’est une bonne chose. -C’est parce qu’Haskell est difficile qu’il est intéressant.

      +Mais c’est voulu. +Il n’y a pas de raccourci pour apprendre Haskell. +C’est difficile. +Mais je pense que c’est une bonne chose. +C’est parce qu’Haskell est difficile qu’il est intéressant.

      -

      La manière conventionnelle d’apprendre Haskell est de lire deux livres. -En premier “Learn You a Haskell” -et ensuite “Real World Haskell”. -Je pense aussi que c’est la bonne manière de s’y prendre. -Mais apprendre même un tout petit peu d’Haskell est presque impossible sans se plonger réellement dans ces livres.

      +

      La manière conventionnelle d’apprendre Haskell est de lire deux livres. +En premier “Learn You a Haskell” +et ensuite “Real World Haskell”. +Je pense aussi que c’est la bonne manière de s’y prendre. +Mais apprendre même un tout petit peu d’Haskell est presque impossible sans se plonger réellement dans ces livres.

      -

      Cet article fait un résumé très dense et rapide des aspect majeurs d’Haskell. -J’y ai aussi rajouté des informations qui m’ont manqué pendant l’apprentissage de ce langage.

      +

      Cet article fait un résumé très dense et rapide des aspect majeurs d’Haskell. +J’y ai aussi rajouté des informations qui m’ont manqué pendant l’apprentissage de ce langage.

      Pour les francophones ; je suis désolé. -Je n’ai pas eu le courage de tout retraduire en français. -Sachez cependant que si vous êtes plusieurs à insister, je ferai certainement l’effort de traduire l’article en entier. -Et si vous vous sentez d’avoir une bonne âme je ne suis pas contre un peu d’aide. +Je n’ai pas eu le courage de tout retraduire en français. +Sachez cependant que si vous êtes plusieurs à insister, je ferai certainement l’effort de traduire l’article en entier. +Et si vous vous sentez d’avoir une bonne âme je ne suis pas contre un peu d’aide. Les sources de cet article sont sur gihub.

      Cet article contient cinq parties :

        -
      • Introduction : un exemple rapide pour montrer qu’Haskell peut être facile.
      • -
      • Les bases d’Haskell : La syntaxe et des notions essentielles
      • +
      • Introduction : un exemple rapide pour montrer qu’Haskell peut être facile.
      • +
      • Les bases d’Haskell : La syntaxe et des notions essentielles
      • Partie difficile :
        • Style fonctionnel : un exemple progressif, du style impératif au style fonctionnel ;
        • -
        • Types : la syntaxe et un exemple d’arbre binaire ;
        • +
        • Types : la syntaxe et un exemple d’arbre binaire ;
        • Structure infinie : manipulons un arbre infini !
      • Partie de difficulté infernale :
        • Utiliser les IO : un exemple très minimal ;
        • -
        • Le truc des IO révélé : les détails cachés d’IO qui m’ont manqués
        • +
        • Le truc des IO révélé : les détails cachés d’IO qui m’ont manqués
        • Les monades : incroyable à quel point on peut généraliser
      • Appendice :
          -
        • Revenons sur les arbres infinis : une discussion plus mathématique sur la manipulation d’arbres infinis.
        • +
        • Revenons sur les arbres infinis : une discussion plus mathématique sur la manipulation d’arbres infinis.

      Note: Chaque fois que vous voyez un séparateur avec un nom de fichier se terminant par lhs, vous pouvez cliquer sur le nom de fichier et télécharger le fichier. -Si vous sauvegardez le fichier sour le nom filename.lhs, vous pouvez l’exécuter avec :

      +Si vous sauvegardez le fichier sour le nom filename.lhs, vous pouvez l’exécuter avec :

       runhaskell filename.lhs
       
      @@ -262,76 +265,77 @@ Vous devriez voir un lien juste en dessous.

      The Scream

      -

      Many book/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc…). +

      Many book/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc…). I will do the exact opposite. -At first I won’t show you any Haskell super power. +At first I won’t show you any Haskell super power. I will start with similarities between Haskell and other programming languages. -Let’s jump to the mandatory “Hello World”.

      +Let’s jump to the mandatory “Hello World”.

      -
      -main = putStrLn "Hello World!"
      -
      + + +
      main = putStrLn "Hello World!"
      +
      + +

      To run it, you can save this code in a hello.hs and:

      -
      -~ runhaskell ./hello.hs
      -Hello World!
      -
      +
      ~ runhaskell ./hello.hs
      +Hello World!
      +

      You could also download the literate Haskell source. You should see a link just above the introduction title. Download this file as 00_hello_world.lhs and:

      -
      -~ runhaskell 00_hello_world.lhs
      -Hello World!
      -
      +
      ~ runhaskell 00_hello_world.lhs
      +Hello World!
      +

      01_basic/10_Introduction/00_hello_world.lhs


      01_basic/10_Introduction/10_hello_you.lhs

      -

      Now, a program asking your name and replying “Hello” using the name you entered:

      +

      Now, a program asking your name and replying “Hello” using the name you entered:

      -
      -main = do
      -    print "What is your name?"
      -    name <- getLine
      -    print ("Hello " ++ name ++ "!")
      -
      + + +
      main = do
      +    print "What is your name?"
      +    name <- getLine
      +    print ("Hello " ++ name ++ "!")
      +
      + +

      First, let us compare with a similar program in some imperative languages:

      -
      - # Python
      -print "What is your name?"
      -name = raw_input()
      -print "Hello %s!" % name
      -
      +
       # Python
      +print "What is your name?"
      +name = raw_input()
      +print "Hello %s!" % name
      +
      -
      - # Ruby
      -puts "What is your name?"
      -name = gets.chomp
      -puts "Hello #{name}!"
      -
      +
       # Ruby
      +puts "What is your name?"
      +name = gets.chomp
      +puts "Hello #{name}!"
      +
      -
      -// In C
      - #include <stdio.h>
      -int main (int argc, char **argv) {
      -    char name[666]; // <- An Evil Number!
      -    // What if my name is more than 665 character long?
      -    printf("What is your name?\n"); 
      -    scanf("%s", name);
      -    printf("Hello %s!\n", name);
      -    return 0;
      +
      // In C
      + #include <stdio.h>
      +int main (int argc, char **argv) {
      +    char name[666]; // <- An Evil Number!
      +    // What if my name is more than 665 character long?
      +    printf("What is your name?\n"); 
      +    scanf("%s", name);
      +    printf("Hello %s!\n", name);
      +    return 0;
       }
      -
      +

      The structure is the same, but there are some syntax differences. A major part of this tutorial will be dedicated to explaining why.

      @@ -356,7 +360,7 @@ This means, main will cause side effects.

      Functional

      Haskell is a functional language. -If you have an imperative language background, you’ll have to learn a lot of new things. +If you have an imperative language background, you’ll have to learn a lot of new things. Hopefully many of these new concepts will help you to program even in imperative languages.

      Smart Static Typing

      @@ -365,8 +369,8 @@ Hopefully many of these new concepts will help you to program even in imperative

      Purity

      -

      Generally your functions won’t modify anything in the outside world. -This means, it can’t modify the value of a variable, can’t get user input, can’t write on the screen, can’t launch a missile. +

      Generally your functions won’t modify anything in the outside world. +This means, it can’t modify the value of a variable, can’t get user input, can’t write on the screen, can’t launch a missile. On the other hand, parallelism will be very easy to achieve. Haskell makes it clear where effects occur and where you are pure. Also, it will be far easier to reason about your program. @@ -387,7 +391,7 @@ In consequence, it provides a very elegant way to manipulate infinite structures

      A last warning on how you should read Haskell code. For me, it is like reading scientific papers. Some parts are very clear, but when you see a formula, just focus and read slower. -Also, while learning Haskell, it really doesn’t matter much if you don’t understand syntax details. +Also, while learning Haskell, it really doesn’t matter much if you don’t understand syntax details. If you meet a >>=, <$>, <- or any other weird symbol, just ignore them and follows the flow of the code.

      Function declaration

      @@ -396,51 +400,45 @@ If you meet a >>=, <$>, <-

      In C:

      -
      -int f(int x, int y) {
      -    return x*x + y*y;
      +
      int f(int x, int y) {
      +    return x*x + y*y;
       }
      -
      +

      In Javascript:

      -
      -function f(x,y) {
      -    return x*x + y*y;
      +
      function f(x,y) {
      +    return x*x + y*y;
       }
      -
      +

      in Python:

      -
      -def f(x,y):
      -    return x*x + y*y
      -
      +
      def f(x,y):
      +    return x*x + y*y
      +

      in Ruby:

      -
      -def f(x,y)
      -    x*x + y*y
      -end
      -
      +
      def f(x,y)
      +    x*x + y*y
      +end
      +

      In Scheme:

      -
      -(define (f x y)
      -    (+ (* x x) (* y y)))
      -
      +
      (define (f x y)
      +    (+ (* x x) (* y y)))
      +

      Finally, the Haskell way is:

      -
      -f x y = x*x + y*y
      -
      +
      f x y = x*x + y*y
      +

      Very clean. No parenthesis, no def.

      -

      Don’t forget, Haskell uses functions and types a lot. +

      Don’t forget, Haskell uses functions and types a lot. It is thus very easy to define them. The syntax was particularly well thought for these objects.

      @@ -450,16 +448,19 @@ The syntax was particularly well thought for these objects.

      This is not mandatory. The compiler is smart enough to discover it for you.

      -

      Let’s play a little.

      +

      Let’s play a little.

      -
      --- We declare the type using ::
      -f :: Int -> Int -> Int
      +
      +
      +
      -- We declare the type using ::
      +f :: Int -> Int -> Int
       f x y = x*x + y*y
       
      -main = print (f 2 3)
      -
      +main = print (f 2 3) +
      + +
      ~ runhaskell 20_very_basic.lhs
       13
      @@ -473,12 +474,15 @@ main = print (f 2 3)
       

      Now try

      -
      -f :: Int -> Int -> Int
      +
      +
      +
      f :: Int -> Int -> Int
       f x y = x*x + y*y
       
      -main = print (f 2.3 4.2)
      -
      +main = print (f 2.3 4.2) +
      + +

      You get this error:

      @@ -491,7 +495,7 @@ main = print (f 2.3 4.2) In the expression: print (f 2.3 4.2)
      -

      The problem: 4.2 isn’t an Int.

      +

      The problem: 4.2 isn’t an Int.

      01_basic/10_Introduction/21_very_basic.lhs

      @@ -499,19 +503,22 @@ main = print (f 2.3 4.2)

      01_basic/10_Introduction/22_very_basic.lhs

      The solution, -don’t declare the type for f. +don’t declare the type for f. Haskell will infer the most general type for us:

      -
      -f x y = x*x + y*y
       
      -main = print (f 2.3 4.2)
      -
      + +
      f x y = x*x + y*y
      +
      +main = print (f 2.3 4.2)
      +
      + +

      It works! -Great, we don’t have to declare a new function for every single type. -For example, in C, you’ll have to declare a function for int, for float, for long, for double, etc…

      +Great, we don’t have to declare a new function for every single type. +For example, in C, you’ll have to declare a function for int, for float, for long, for double, etc…

      But, what type should we declare? To discover the type Haskell has found for us, just launch ghci:

      @@ -533,13 +540,13 @@ Prelude>
      let f x y = x*x + y*y
      Num a => a -> a -> a
       
      -

      First, let’s focus on the right part a -> a -> a. +

      First, let’s focus on the right part a -> a -> a. To understand it, just look at a list of progressive examples:

      - + @@ -572,13 +579,13 @@ To understand it, just look at a list of progressive examples:

      In the type a -> a -> a, the letter a is a type variable. It means f is a function with two arguments and both arguments and the result have the same type. The type variable a could take many different type value. -For example Int, Integer, Float

      +For example Int, Integer, Float

      -

      So instead of having a forced type like in C with declaring the function for int, long, float, double, etc… +

      So instead of having a forced type like in C with declaring the function for int, long, float, double, etc… We declare only one function like in a dynamically typed language.

      Generally a can be any type. -For example a String, an Int, but also more complex types, like Trees, other functions, etc… +For example a String, an Int, but also more complex types, like Trees, other functions, etc… But here our type is prefixed with Num a => .

      Num is a type class. @@ -632,12 +639,15 @@ It is time to make a real application.

      But just before that, we should verify the type system works as expected:

      -
      -f :: Num a => a -> a -> a
      +
      +
      +
      f :: Num a => a -> a -> a
       f x y = x*x + y*y
       
      -main = print (f 3 2.4)
      -
      +main = print (f 3 2.4) +
      + +

      It works, because, 3 is a valid representation both for Fractional numbers like Float and for Integer. As 2.4 is a Fractional number, 3 is then interpreted as being also a Fractional number.

      @@ -650,16 +660,19 @@ As 2.4 is a Fractional number, 3 is then interpreted a

      If we force our function to work with different types, it will fail:

      -
      -f :: Num a => a -> a -> a
      +
      +
      +
      f :: Num a => a -> a -> a
       f x y = x*x + y*y
       
      -x :: Int
      +x :: Int
       x = 3
      -y :: Float
      +y :: Float
       y = 2.4
      -main = print (f x y) -- won't work because type x ≠ type y
      -
      +main = print (f x y) -- won't work because type x ≠ type y +
      + +

      The compiler complains. The two parameters must have the same type.

      @@ -750,7 +763,7 @@ Data.Ratio> (11 % 15) * (5 % 3)

      Remark: -In real code you shouldn’t use list of char to represent text. +In real code you shouldn’t use list of char to represent text. You should mostly use Data.Text instead. If you want to represent stream of ASCII char, you should use Data.ByteString.

      @@ -806,36 +819,45 @@ f :: a -> b -> c ⇔ f is a function from a to (b→c) f :: (a -> b) -> c ⇔ f is a function from (a→b) to c -

      Defining the type of a function before its declaration isn’t mandatory. +

      Defining the type of a function before its declaration isn’t mandatory. Haskell infers the most general type for you. But it is considered a good practice to do so.

      Infix notation

      -
      -square :: Num a => a -> a  
      +
      +
      +
      square :: Num a => a -> a  
       square x = x^2
      -
      +
      + +

      Note ^ use infix notation. For each infix operator there its associated prefix notation. You just have to put it inside parenthesis.

      -
      -square' x = (^) x 2
      +
      +
      +
      square' x = (^) x 2
       
       square'' x = (^2) x
      -
      +
      + +

      We can remove x in the left and right side! -It’s called η-reduction.

      +It’s called η-reduction.

      -
      -square''' = (^2)
      -
      + + +
      square''' = (^2)
      +
      + +

      Note we can declare function with ' in their name. Here:

      @@ -849,10 +871,13 @@ Here:

      An implementation of the absolute function.

      -
      -absolute :: (Ord a, Num a) => a -> a
      -absolute x = if x >= 0 then x else -x
      -
      + + +
      absolute :: (Ord a, Num a) => a -> a
      +absolute x = if x >= 0 then x else -x
      +
      + +

      Note: the if .. then .. else Haskell notation is more like the ¤?¤:¤ C operator. You cannot forget the else.

      @@ -860,11 +885,14 @@ absolute x = if x >= 0 the

      Another equivalent version:

      -
      -absolute' x
      +
      +
      +
      absolute' x
           | x >= 0 = x
      -    | otherwise = -x
      -
      + | otherwise = -x +
      + +

      Notation warning: indentation is important in Haskell. @@ -874,17 +902,20 @@ Like in Python, a bad indentation could break your code!

      -
      -main = do
      -      print $ square 10
      -      print $ square' 10
      -      print $ square'' 10
      -      print $ square''' 10
      -      print $ absolute 10
      -      print $ absolute (-10)
      -      print $ absolute' 10
      -      print $ absolute' (-10)
      -
      + + +
      main = do
      +      print $ square 10
      +      print $ square' 10
      +      print $ square'' 10
      +      print $ square''' 10
      +      print $ absolute 10
      +      print $ absolute (-10)
      +      print $ absolute' 10
      +      print $ absolute' (-10)
      +
      + +
      @@ -903,7 +934,7 @@ We will select a problem and solve it using a standard imperative way. Then I will make the code evolve. The end result will be both more elegant and easier to adapt.

      -

      Let’s solve the following problem:

      +

      Let’s solve the following problem:

      Given a list of integers, return the sum of the even numbers in the list.

      @@ -913,21 +944,20 @@ The end result will be both more elegant and easier to adapt.

      To show differences between the functional and imperative approach, -I’ll start by providing an imperative solution (in Javascript):

      +I’ll start by providing an imperative solution (in Javascript):

      -
      -function evenSum(list) {
      -    var result = 0;
      -    for (var i=0; i< list.length ; i++) {
      -        if (list[i] % 2 ==0) {
      -            result += list[i];
      +
      function evenSum(list) {
      +    var result = 0;
      +    for (var i=0; i< list.length ; i++) {
      +        if (list[i] % 2 ==0) {
      +            result += list[i];
               }
           }
      -    return result;
      +    return result;
       }
      -
      +
      -

      But, in Haskell we don’t have variables, nor for loop. +

      But, in Haskell we don’t have variables, nor for loop. One solution to achieve the same result without loops is to use recursion.

      @@ -940,61 +970,56 @@ Most of the time Haskell will handle recursive functions efficiently.

      Here is a C version of the recursive function. Note that for simplicity, I assume the int list ends with the first 0 value.

      -
      -int evenSum(int *list) {
      -    return accumSum(0,list);
      +
      int evenSum(int *list) {
      +    return accumSum(0,list);
       }
       
      -int accumSum(int n, int *list) {
      -    int x;
      -    int *xs;
      -    if (*list == 0) { // if the list is empty
      -        return n;
      -    } else {
      -        x = list[0]; // let x be the first element of the list
      -        xs = list+1; // let xs be the list without x
      -        if ( 0 == (x%2) ) { // if x is even
      -            return accumSum(n+x, xs);
      -        } else {
      -            return accumSum(n, xs);
      +int accumSum(int n, int *list) {
      +    int x;
      +    int *xs;
      +    if (*list == 0) { // if the list is empty
      +        return n;
      +    } else {
      +        x = list[0]; // let x be the first element of the list
      +        xs = list+1; // let xs be the list without x
      +        if ( 0 == (x%2) ) { // if x is even
      +            return accumSum(n+x, xs);
      +        } else {
      +            return accumSum(n, xs);
               }
           }
       }
      -
      +

      Keep this code in mind. We will translate it into Haskell. But before, I need to introduce three simple but useful functions we will use:

      -
      -even :: Integral a => a -> Bool
      -head :: [a] -> a
      -tail :: [a] -> [a]
      -
      +
      even :: Integral a => a -> Bool
      +head :: [a] -> a
      +tail :: [a] -> [a]
      +

      even verifies if a number is even.

      -
      -even :: Integral a => a -> Bool
      -even 3  ⇒ False
      -even 2  ⇒ True
      -
      +
      even :: Integral a => a -> Bool
      +even 3  ⇒ False
      +even 2  ⇒ True
      +

      head returns the first element of a list:

      -
      -head :: [a] -> a
      -head [1,2,3] ⇒ 1
      -head []      ⇒ ERROR
      -
      +
      head :: [a] -> a
      +head [1,2,3] ⇒ 1
      +head []      ⇒ ERROR
      +

      tail returns all elements of a list, except the first:

      -
      -tail :: [a] -> [a]
      -tail [1,2,3] ⇒ [2,3]
      -tail [3]     ⇒ []
      -tail []      ⇒ ERROR
      -
      +
      tail :: [a] -> [a]
      +tail [1,2,3] ⇒ [2,3]
      +tail [3]     ⇒ []
      +tail []      ⇒ ERROR
      +

      Note that for any non empty list l, l ⇔ (head l):(tail l)

      @@ -1006,20 +1031,23 @@ But before, I need to introduce three simple but useful functions we will use:evenSum returns the sum of all even numbers in a list:

      -
      --- Version 1
      -evenSum :: [Integer] -> Integer
      +
      +
      +
      -- Version 1
      +evenSum :: [Integer] -> Integer
       
       evenSum l = accumSum 0 l
       
      -accumSum n l = if l == []
      -                  then n
      -                  else let x = head l 
      -                           xs = tail l 
      -                       in if even x
      -                              then accumSum (n+x) xs
      -                              else accumSum n xs
      -
      +accumSum n l = if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +

      To test a function you can use ghci:

      @@ -1061,16 +1089,18 @@ accumSum (0+2+4) [] In reality many things can be improved. First, we can generalize the type.

      -
      -evenSum :: Integral a => [a] -> a
      -
      +
      evenSum :: Integral a => [a] -> a
      +
      -
      -main = do print $ evenSum [1..10]
      -
      + + +
      main = do print $ evenSum [1..10]
      +
      + +
      @@ -1080,30 +1110,36 @@ main = do print $ evenS

      02_Hard_Part/12_Functions.lhs

      Next, we can use sub functions using where or let. -This way our accumSum function won’t pollute the global namespace.

      +This way our accumSum function won’t pollute the global namespace.

      -
      --- Version 2
      -evenSum :: Integral a => [a] -> a
      +
      +
      +
      -- Version 2
      +evenSum :: Integral a => [a] -> a
       
       evenSum l = accumSum 0 l
      -    where accumSum n l = 
      -            if l == []
      -                then n
      -                else let x = head l 
      -                         xs = tail l 
      -                     in if even x
      -                            then accumSum (n+x) xs
      -                            else accumSum n xs
      -
      + where accumSum n l = + if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1115,16 +1151,19 @@ main = print $ evenSum [1..10]

      Next, we can use pattern matching.

      -
      --- Version 3
      +
      +
      +
      -- Version 3
       evenSum l = accumSum 0 l
      -    where 
      +    where 
               accumSum n [] = n
               accumSum n (x:xs) = 
      -             if even x
      -                then accumSum (n+x) xs
      -                else accumSum n xs
      -
      + if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +

      What is pattern matching? Use values instead of general parameter names3.

      @@ -1132,30 +1171,27 @@ Use values instead of general parameter names -foo [] = <x> +
      foo [] =  <x>
       foo l  =  <y>
      -
      +

      But pattern matching goes even further. It is also able to inspect the inner data of a complex value. We can replace

      -
      -foo l =  let x  = head l 
      -             xs = tail l
      -         in if even x 
      -             then foo (n+x) xs
      -             else foo n xs
      -
      +
      foo l =  let x  = head l 
      +             xs = tail l
      +         in if even x 
      +             then foo (n+x) xs
      +             else foo n xs
      +

      with

      -
      -foo (x:xs) = if even x 
      -                 then foo (n+x) xs
      -                 else foo n xs
      -
      +
      foo (x:xs) = if even x 
      +                 then foo (n+x) xs
      +                 else foo n xs
      +

      This is a very useful feature. It makes our code both terser and easier to read.

      @@ -1163,9 +1199,12 @@ It makes our code both terser and easier to read.

      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1177,38 +1216,42 @@ main = print $ evenSum [1..10]

      In Haskell you can simplify function definition by η-reducing them. For example, instead of writing:

      -
      -f x = (some expresion) x
      -
      +
      f x = (some expresion) x
      +

      you can simply write

      -
      -f = some expression
      -
      +
      f = some expression
      +

      We use this method to remove the l:

      -
      --- Version 4
      -evenSum :: Integral a => [a] -> a
      +
      +
      +
      -- Version 4
      +evenSum :: Integral a => [a] -> a
       
       evenSum = accumSum 0
      -    where 
      +    where 
               accumSum n [] = n
               accumSum n (x:xs) = 
      -             if even x
      -                then accumSum (n+x) xs
      -                else accumSum n xs
      -
      + if even x + then accumSum (n+x) xs + else accumSum n xs +
      + +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1227,27 +1270,24 @@ Higher order functions are functions taking functions as parameter.

      Here are some examples:

      -
      -filter :: (a -> Bool) -> [a] -> [a]
      -map :: (a -> b) -> [a] -> [b]
      -foldl :: (a -> b -> a) -> a -> [b] -> a
      -
      +
      filter :: (a -> Bool) -> [a] -> [a]
      +map :: (a -> b) -> [a] -> [b]
      +foldl :: (a -> b -> a) -> a -> [b] -> a
      +
      -

      Let’s proceed by small steps.

      +

      Let’s proceed by small steps.

      -
      --- Version 5
      -evenSum l = mysum 0 (filter even l)
      -    where 
      +
      -- Version 5
      +evenSum l = mysum 0 (filter even l)
      +    where 
             mysum n [] = n
             mysum n (x:xs) = mysum (n+x) xs 
      -
      +

      where

      -
      -filter even [1..10] ⇔  [2,4,6,8,10]
      -
      +
      filter even [1..10] ⇔  [2,4,6,8,10]
      +

      The function filter takes a function of type (a -> Bool) and a list of type [a]. It returns a list containing only elements for which the function returned true.

      @@ -1270,57 +1310,59 @@ myfunc list = foldl bar initialVa

      If you really want to know how the magic works. Here is the definition of foldl.

      -
      -foldl f z [] = z
      -foldl f z (x:xs) = foldl f (f z x) xs
      -
      +
      foldl f z [] = z
      +foldl f z (x:xs) = foldl f (f z x) xs
      +
      -
      -foldl f z [x1,...xn]
      +
      foldl f z [x1,...xn]
       ⇔  f (... (f (f z x1) x2) ...) xn
      -
      +
      -

      But as Haskell is lazy, it doesn’t evaluate (f z x) and pushes it to the stack. +

      But as Haskell is lazy, it doesn’t evaluate (f z x) and pushes it to the stack. This is why we generally use foldl' instead of foldl; foldl' is a strict version of foldl. -If you don’t understand what lazy and strict means, -don’t worry, just follow the code as if foldl and foldl' where identical.

      +If you don’t understand what lazy and strict means, +don’t worry, just follow the code as if foldl and foldl' where identical.

      Now our new version of evenSum becomes:

      -
      --- Version 6
      --- foldl' isn't accessible by default
      --- we need to import it from the module Data.List
      -import Data.List
      -evenSum l = foldl' mysum 0 (filter even l)
      -  where mysum acc value = acc + value
      -
      +
      -- Version 6
      +-- foldl' isn't accessible by default
      +-- we need to import it from the module Data.List
      +import Data.List
      +evenSum l = foldl' mysum 0 (filter even l)
      +  where mysum acc value = acc + value
      +

      Version we can simplify by using directly a lambda notation. -This way we don’t have to create the temporary name mysum.

      +This way we don’t have to create the temporary name mysum.

      -
      --- Version 7
      --- Generally it is considered a good practice
      --- to import only the necessary function(s)
      -import Data.List (foldl')
      -evenSum l = foldl' (\x y -> x+y) 0 (filter even l)
      -
      + + +
      -- Version 7
      +-- Generally it is considered a good practice
      +-- to import only the necessary function(s)
      +import Data.List (foldl')
      +evenSum l = foldl' (\x y -> x+y) 0 (filter even l)
      +
      + +

      And of course, we note that

      -
      -(\x y -> x+y) ⇔ (+)
      -
      +
      (\x y -> x+y) ⇔ (+)
      +
      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1331,17 +1373,16 @@ main = print $ evenSum [1..10]

      Finally

      -
      --- Version 8
      -import Data.List (foldl')
      -evenSum :: Integral a => [a] -> a
      -evenSum l = foldl' (+) 0 (filter even l)
      -
      +
      -- Version 8
      +import Data.List (foldl')
      +evenSum :: Integral a => [a] -> a
      +evenSum l = foldl' (+) 0 (filter even l)
      +
      -

      foldl' isn’t the easiest function to intuit. +

      foldl' isn’t the easiest function to intuit. If you are not used to it, you should study it a bit.

      -

      To help you understand what’s going on here, a step by step evaluation:

      +

      To help you understand what’s going on here, a step by step evaluation:

         evenSum [1,2,3,4]
      @@ -1357,31 +1398,32 @@ If you are not used to it, you should study it a bit.

      Another useful higher order function is (.). The (.) function corresponds to the mathematical composition.

      -
      -(f . g . h) x ⇔  f ( g (h x))
      -
      +
      (f . g . h) x ⇔  f ( g (h x))
      +

      We can take advantage of this operator to η-reduce our function:

      -
      --- Version 9
      -import Data.List (foldl')
      -evenSum :: Integral a => [a] -> a
      -evenSum = (foldl' (+) 0) . (filter even)
      -
      +
      -- Version 9
      +import Data.List (foldl')
      +evenSum :: Integral a => [a] -> a
      +evenSum = (foldl' (+) 0) . (filter even)
      +

      Also, we could rename some parts to make it clearer:

      -
      --- Version 10 
      -import Data.List (foldl')
      -sum' :: (Num a) => [a] -> a
      -sum' = foldl' (+) 0
      -evenSum :: Integral a => [a] -> a
      -evenSum = sum' . (filter even)
      +
      +
      +
      -- Version 10 
      +import Data.List (foldl')
      +sum' :: (Num a) => [a] -> a
      +sum' = foldl' (+) 0
      +evenSum :: Integral a => [a] -> a
      +evenSum = sum' . (filter even)
        
      -
      +
      + +

      It is time to discuss a bit. What did we gain by using higher order functions?

      @@ -1397,20 +1439,23 @@ We want to get the sum of all even square of element of the list.

      Update the version 10 is extremely easy:

      -
      -squareEvenSum = sum' . (filter even) . (map (^2))
      -squareEvenSum' = evenSum . (map (^2))
      -squareEvenSum'' = sum' . (map (^2)) . (filter even)
      -
      + + +
      squareEvenSum = sum' . (filter even) . (map (^2))
      +squareEvenSum' = evenSum . (map (^2))
      +squareEvenSum'' = sum' . (map (^2)) . (filter even)
      +
      + +
      -

      We just had to add another “transformation function”4.

      +

      We just had to add another “transformation function”4.

      map (^2) [1,2,3,4] ⇔ [1,4,9,16]
       

      The map function simply apply a function to all element of a list.

      -

      We didn’t had to modify anything inside the function definition. +

      We didn’t had to modify anything inside the function definition. It feels more modular. But in addition you can think more mathematically about your function. You can then use your function as any other one. @@ -1424,8 +1469,8 @@ If you want to know how, I suggest you to read this quite fun article: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, Fokkinga and Paterson.

      This example should show you how great pure functional programming is. -Unfortunately, using pure functional programming isn’t well suited to all usages. -Or at least such a language hasn’t been found yet.

      +Unfortunately, using pure functional programming isn’t well suited to all usages. +Or at least such a language hasn’t been found yet.

      One of the great powers of Haskell is the ability to create DSLs (Domain Specific Language) @@ -1442,9 +1487,12 @@ essential aspect of Haskell: Types.

      -
      -main = print $ evenSum [1..10]
      -
      + + +
      main = print $ evenSum [1..10]
      +
      + +
      @@ -1458,7 +1506,7 @@ main = print $ evenSum [1..10]

      tl;dr:

        -
      • type Name = AnotherType is just an alias and the compiler doesn’t do any difference between Name and AnotherType.
      • +
      • type Name = AnotherType is just an alias and the compiler doesn’t do any difference between Name and AnotherType.
      • data Name = NameConstructor AnotherType make a difference.
      • data can construct structures which can be recursives.
      • deriving is magic and create functions for you.
      • @@ -1476,14 +1524,13 @@ It will be easy to detect where you used the wrong parameter at the wrong place

        Static typing is generally essential to reach fast execution time. But most statically typed languages are bad at generalizing concepts. -Haskell’s saving grace is that it can infer types.

        +Haskell’s saving grace is that it can infer types.

        Here is a simple example. The square function in Haskell:

        -
        -square x = x * x
        -
        +
        square x = x * x
        +

        This function can square any Numeral type. You can provide square with an Int, an Integer, a Float a Fractional and even Complex. Proof by example:

        @@ -1506,49 +1553,47 @@ Prelude Data.Complex> square (2 :+ 1)

        Now compare with the amount of code necessary in C:

        -
        -int     int_square(int x) { return x*x; }
        +
        int     int_square(int x) { return x*x; }
         
        -float   float_square(float x) {return x*x; }
        +float   float_square(float x) {return x*x; }
         
        -complex complex_square (complex z) {
        +complex complex_square (complex z) {
             complex tmp; 
             tmp.real = z.real * z.real - z.img * z.img;
        -    tmp.img = 2 * z.img * z.real;
        +    tmp.img = 2 * z.img * z.real;
         }
         
         complex x,y;
         y = complex_square(x);
        -
        +

        For each type, you need to write a new function. The only way to work around this problem is to use some meta-programming trick. For example using the pre-processor. In C++ there is a better way, the C++ templates:

        -
        -#include <iostream>
        -#include <complex>
        -using namespace std;
        +
        #include <iostream>
        +#include <complex>
        +using namespace std;
         
        -template<typename T>
        -T square(T x)
        +template<typename T>
        +T square(T x)
         {
        -    return x*x;
        +    return x*x;
         }
         
        -int main() {
        -    // int
        -    int sqr_of_five = square(5);
        +int main() {
        +    // int
        +    int sqr_of_five = square(5);
             cout << sqr_of_five << endl;
        -    // double
        -    cout << (double)square(5.3) << endl;
        -    // complex
        -    cout << square( complex<double>(5,3) ) 
        +    // double
        +    cout << (double)square(5.3) << endl;
        +    // complex
        +    cout << square( complex<double>(5,3) ) 
                  << endl;
        -    return 0;
        +    return 0;
         }
        -
        +

        C++ does a far better job than C. For more complex function the syntax can be hard to follow: @@ -1566,7 +1611,7 @@ But unlike dynamically typed languages, most errors are caught before the execut Generally, in Haskell:

        -

        “if it compiles it certainly does what you intended”

        +

        “if it compiles it certainly does what you intended”


        @@ -1578,31 +1623,33 @@ Generally, in Haskell:

        First you can use aliases or type synonyms.

        -
        -type Name   = String
        -type Color  = String
         
        -showInfos :: Name ->  Color -> String
        -showInfos name color =  "Name: " ++ name
        -                        ++ ", Color: " ++ color
        -name :: Name
        -name = "Robin"
        -color :: Color
        -color = "Blue"
        -main = putStrLn $ showInfos name color
        -
        + +
        type Name   = String
        +type Color  = String
        +
        +showInfos :: Name ->  Color -> String
        +showInfos name color =  "Name: " ++ name
        +                        ++ ", Color: " ++ color
        +name :: Name
        +name = "Robin"
        +color :: Color
        +color = "Blue"
        +main = putStrLn $ showInfos name color
        +
        + +

        02_Hard_Part/21_Types.lhs


        02_Hard_Part/22_Types.lhs

        -

        But it doesn’t protect you much. +

        But it doesn’t protect you much. Try to swap the two parameter of showInfos and run the program:

        -
        -    putStrLn $ showInfos color name
        -
        +
            putStrLn $ showInfos color name
        +

        It will compile and execute. In fact you can replace Name, Color and String everywhere. @@ -1611,18 +1658,21 @@ The compiler will treat them as completely identical.

        Another method is to create your own types using the keyword data.

        -
        -data Name   = NameConstr String
        -data Color  = ColorConstr String
         
        -showInfos :: Name ->  Color -> String
        -showInfos (NameConstr name) (ColorConstr color) =
        -      "Name: " ++ name ++ ", Color: " ++ color
         
        -name  = NameConstr "Robin"
        -color = ColorConstr "Blue"
        -main = putStrLn $ showInfos name color
        -
        +
        data Name   = NameConstr String
        +data Color  = ColorConstr String
        +
        +showInfos :: Name ->  Color -> String
        +showInfos (NameConstr name) (ColorConstr color) =
        +      "Name: " ++ name ++ ", Color: " ++ color
        +
        +name  = NameConstr "Robin"
        +color = ColorConstr "Blue"
        +main = putStrLn $ showInfos name color
        +
        + +

        Now if you switch parameters of showInfos, the compiler complains! A possible mistake you could never do again. @@ -1630,50 +1680,45 @@ The only price is to be more verbose.

        Also remark constructor are functions:

        -
        -NameConstr  :: String -> Name
        -ColorConstr :: String -> Color
        -
        +
        NameConstr  :: String -> Name
        +ColorConstr :: String -> Color
        +

        The syntax of data is mainly:

        -
        -data TypeName =   ConstructorName  [types]
        -                | ConstructorName2 [types]
        +
        data TypeName =   ConstructorName  [types]
        +                | ConstructorName2 [types]
                         | ...
        -
        +

        Generally the usage is to use the same name for the DataTypeName and DataTypeConstructor.

        Example:

        -
        -data Complex = Num a => Complex a a
        -
        +
        data Complex = Num a => Complex a a
        +

        Also you can use the record syntax:

        -
        -data DataTypeName = DataConstructor {
        -                      field1 :: [type of field1]
        -                    , field2 :: [type of field2]
        +
        data DataTypeName = DataConstructor {
        +                      field1 :: [type of field1]
        +                    , field2 :: [type of field2]
                             ...
        -                    , fieldn :: [type of fieldn] }
        -
        + , fieldn :: [type of fieldn] } +

        And many accessors are made for you. Furthermore you can use another order when setting values.

        Example:

        -
        -data Complex = Num a => Complex { real :: a, img :: a}
        -c = Complex 1.0 2.0
        -z = Complex { real = 3, img = 4 }
        +
        data Complex = Num a => Complex { real :: a, img :: a}
        +c = Complex 1.0 2.0
        +z = Complex { real = 3, img = 4 }
         real c ⇒ 1.0
         img z ⇒ 4
        -
        +

        02_Hard_Part/22_Types.lhs

        @@ -1685,43 +1730,50 @@ img z ⇒ 4

        You already encountered a recursive type: lists. You can re-create lists, but with a more verbose syntax:

        -
        -data List a = Empty | Cons a (List a)
        -
        +
        data List a = Empty | Cons a (List a)
        +

        If you really want to use an easier syntax you can use an infix name for constructors.

        -
        -infixr 5 :::
        -data List a = Nil | a ::: (List a)
        -
        +
        infixr 5 :::
        +data List a = Nil | a ::: (List a)
        +

        The number after infixr is the priority.

        If you want to be able to print (Show), read (Read), test equality (Eq) and compare (Ord) your new data structure you can tell Haskell to derive the appropriate functions for you.

        -
        -infixr 5 :::
        -data List a = Nil | a ::: (List a) 
        -              deriving (Show,Read,Eq,Ord)
        -
        + + +
        infixr 5 :::
        +data List a = Nil | a ::: (List a) 
        +              deriving (Show,Read,Eq,Ord)
        +
        + +

        When you add deriving (Show) to your data declaration, Haskell create a show function for you. -We’ll see soon how you can use your own show function.

        +We’ll see soon how you can use your own show function.

        -
        -convertList [] = Nil
        +
        +
        +
        convertList [] = Nil
         convertList (x:xs) = x ::: convertList xs
        -
        +
        + +
        -
        -main = do
        -      print (0 ::: 1 ::: Nil)
        -      print (convertList [0,1])
        -
        + + +
        main = do
        +      print (0 ::: 1 ::: Nil)
        +      print (convertList [0,1])
        +
        + +

        This prints:

        @@ -1738,26 +1790,32 @@ main = do

        Magritte, l'Arbre

        -

        We’ll just give another standard example: binary trees.

        +

        We’ll just give another standard example: binary trees.

        -
        -import Data.List
         
        -data BinTree a = Empty 
        -                 | Node a (BinTree a) (BinTree a) 
        -                              deriving (Show)
        -
        + +
        import Data.List
        +
        +data BinTree a = Empty 
        +                 | Node a (BinTree a) (BinTree a) 
        +                              deriving (Show)
        +
        + +

        We will also create a function which turns a list into an ordered binary tree.

        -
        -treeFromList :: (Ord a) => [a] -> BinTree a
        -treeFromList [] = Empty
        -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
        -                             (treeFromList (filter (>x) xs))
        -
        + + +
        treeFromList :: (Ord a) => [a] -> BinTree a
        +treeFromList [] = Empty
        +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
        +                             (treeFromList (filter (>x) xs))
        +
        + +

        Look at how elegant this function is. In plain English:

        @@ -1774,9 +1832,12 @@ In plain English:

      -
      -main = print $ treeFromList [7,2,4,8]
      -
      + + +
      main = print $ treeFromList [7,2,4,8]
      +
      + +

      You should obtain the following:

      @@ -1790,7 +1851,7 @@ main = print $ treeFromList [7,2,4,8]

      02_Hard_Part/31_Trees.lhs

      -

      Just for fun, let’s code a better display for our trees. +

      Just for fun, let’s code a better display for our trees. I simply had fun making a nice function to display trees in a general way. You can safely skip this part if you find it too difficult to follow.

      @@ -1800,93 +1861,104 @@ And it might also be useful to make our BinTree an instance of (Eq We will be able to test equality and compare trees.

      -
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -

      Without the deriving (Show), Haskell doesn’t create a show method for us. +

      Without the deriving (Show), Haskell doesn’t create a show method for us. We will create our own version of show. To achieve this, we must declare that our newly created type BinTree a is an instance of the type class Show. The general syntax is:

      -
      -instance Show (BinTree a) where
      -   show t = ... -- You declare your function here
      -
      +
      instance Show (BinTree a) where
      +   show t = ... -- You declare your function here
      +

      Here is my version of how to show a binary tree. -Don’t worry about the apparent complexity. +Don’t worry about the apparent complexity. I made a lot of improvements in order to display even stranger objects.

      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    -- treeshow pref Tree 
      -    --   shows a tree and starts each line with pref
      -    -- We don't display the Empty tree
      -    treeshow pref Empty = ""
      -    -- Leaf
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    -- treeshow pref Tree 
      +    --   shows a tree and starts each line with pref
      +    -- We don't display the Empty tree
      +    treeshow pref Empty = ""
      +    -- Leaf
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    -- Right branch is empty
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    -- Right branch is empty
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    -- Left branch is empty
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    -- Left branch is empty
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- Tree with left and right children non empty
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    -- Tree with left and right children non empty
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- shows a tree using some prefixes to make it nice
      +    -- shows a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replaces "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (show x)
      +    -- pshow replaces "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (show x)
       
      -    -- replaces one char by another string
      +    -- replaces one char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      -
      + | otherwise = x:[] -- "x" +
      + +

      The treeFromList method remains identical.

      -
      -treeFromList :: (Ord a) => [a] -> BinTree a
      -treeFromList [] = Empty
      -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      -                             (treeFromList (filter (>x) xs))
      -
      + + +
      treeFromList :: (Ord a) => [a] -> BinTree a
      +treeFromList [] = Empty
      +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      +                             (treeFromList (filter (>x) xs))
      +
      + +

      And now, we can play:

      -
      -main = do
      -  putStrLn "Int binary tree:"
      -  print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
      -
      + + +
      main = do
      +  putStrLn "Int binary tree:"
      +  print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
      +
      + +
      Int binary tree:
       < 7
      @@ -1907,10 +1979,13 @@ And each following line starts with a :.
       But we could also use another type.

      -
      -  putStrLn "\nString binary tree:"
      -  print $ treeFromList ["foo","bar","baz","gor","yog"]
      -
      + + +
        putStrLn "\nString binary tree:"
      +  print $ treeFromList ["foo","bar","baz","gor","yog"]
      +
      + +
      String binary tree:
       < "foo"
      @@ -1924,11 +1999,14 @@ But we could also use another type.

      make tree of trees!

      -
      -  putStrLn "\nBinary tree of Char binary trees:"
      -  print ( treeFromList 
      -           (map treeFromList ["baz","zara","bar"]))
      -
      + + +
        putStrLn "\nBinary tree of Char binary trees:"
      +  print ( treeFromList 
      +           (map treeFromList ["baz","zara","bar"]))
      +
      + +
      Binary tree of Char binary trees:
       < < 'b'
      @@ -1947,25 +2025,27 @@ make tree of trees!

      Yo Dawg Tree

      -
      -  putStrLn "\nTree of Binary trees of Char binary trees:"
      -  print $ (treeFromList . map (treeFromList . map treeFromList))
      -             [ ["YO","DAWG"]
      -             , ["I","HEARD"]
      -             , ["I","HEARD"]
      -             , ["YOU","LIKE","TREES"] ]
      -
      + + +
        putStrLn "\nTree of Binary trees of Char binary trees:"
      +  print $ (treeFromList . map (treeFromList . map treeFromList))
      +             [ ["YO","DAWG"]
      +             , ["I","HEARD"]
      +             , ["I","HEARD"]
      +             , ["YOU","LIKE","TREES"] ]
      +
      + +

      Which is equivalent to

      -
      -print ( treeFromList (
      -          map treeFromList 
      -             [ map treeFromList ["YO","DAWG"]
      -             , map treeFromList ["I","HEARD"]
      -             , map treeFromList ["I","HEARD"]
      -             , map treeFromList ["YOU","LIKE","TREES"] ]))
      -
      +
      print ( treeFromList (
      +          map treeFromList 
      +             [ map treeFromList ["YO","DAWG"]
      +             , map treeFromList ["I","HEARD"]
      +             , map treeFromList ["I","HEARD"]
      +             , map treeFromList ["YOU","LIKE","TREES"] ]))
      +

      and gives:

      @@ -1995,7 +2075,7 @@ make tree of trees!

      : : : `--'S'
      -

      Notice how duplicate trees aren’t inserted; +

      Notice how duplicate trees aren’t inserted; there is only one tree corresponding to "I","HEARD". We have this for (almost) free, because we have declared Tree to be an instance of Eq.

      @@ -2028,17 +2108,20 @@ Laziness is just a common implementation for non-strict languages.

      For example in Haskell you can do:

      -
      --- numbers = [1,2,..]
      -numbers :: [Integer]
      -numbers = 0:map (1+) numbers
       
      -take' n [] = []
      -take' 0 l = []
      -take' n (x:xs) = x:take' (n-1) xs
       
      -main = print $ take' 10 numbers
      -
      +
      -- numbers = [1,2,..]
      +numbers :: [Integer]
      +numbers = 0:map (1+) numbers
      +
      +take' n [] = []
      +take' 0 l = []
      +take' n (x:xs) = x:take' (n-1) xs
      +
      +main = print $ take' 10 numbers
      +
      + +

      And it stops.

      @@ -2066,87 +2149,98 @@ Also, there is a built-in function take which is equivalent to our This code is mostly the same as the previous one.
      -
      -import Debug.Trace (trace)
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      import Debug.Trace (trace)
      +import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
      +
      +
      + -
      -

      Suppose we don’t mind having an ordered binary tree. +

      Suppose we don’t mind having an ordered binary tree. Here is an infinite binary tree:

      -
      -nullTree = Node 0 nullTree nullTree
      -
      + + +
      nullTree = Node 0 nullTree nullTree
      +
      + +

      A complete binary tree where each node is equal to 0. Now I will prove you can manipulate this object using the following function:

      -
      --- take all element of a BinTree 
      --- up to some depth
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +
      +
      +
      -- take all element of a BinTree 
      +-- up to some depth
      +treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +

      See what occurs for this program:

      -
      -main = print $ treeTakeDepth 4 nullTree
      -
      +
      main = print $ treeTakeDepth 4 nullTree
      +

      This code compiles, runs and stops giving the following result:

      @@ -2168,48 +2262,56 @@ main = print $ treeTakeDepth 4 nullTree

      Just to heat up your neurones a bit more, -let’s make a slightly more interesting tree:

      +let’s make a slightly more interesting tree:

      -
      -iTree = Node 0 (dec iTree) (inc iTree)
      -        where
      -           dec (Node x l r) = Node (x-1) (dec l) (dec r) 
      -           inc (Node x l r) = Node (x+1) (inc l) (inc r) 
      -
      + + +
      iTree = Node 0 (dec iTree) (inc iTree)
      +        where
      +           dec (Node x l r) = Node (x-1) (dec l) (dec r) 
      +           inc (Node x l r) = Node (x+1) (inc l) (inc r) 
      +
      + +

      Another way to create this tree is to use a higher order function. This function should be similar to map, but should work on BinTree instead of list. Here is such a function:

      -
      --- apply a function to each node of Tree
      -treeMap :: (a -> b) -> BinTree a -> BinTree b
      -treeMap f Empty = Empty
      -treeMap f (Node x left right) = Node (f x) 
      +
      +
      +
      -- apply a function to each node of Tree
      +treeMap :: (a -> b) -> BinTree a -> BinTree b
      +treeMap f Empty = Empty
      +treeMap f (Node x left right) = Node (f x) 
                                            (treeMap f left) 
                                            (treeMap f right)
      -
      +
      + +
      -

      Hint: I won’t talk more about this here. +

      Hint: I won’t talk more about this here. If you are interested by the generalization of map to other data structures, search for functor and fmap.

      Our definition is now:

      -
      -infTreeTwo :: BinTree Int
      -infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) 
      +
      +
      +
      infTreeTwo :: BinTree Int
      +infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) 
                           (treeMap (\x -> x+1) infTreeTwo) 
      -
      +
      + +

      Look at the result for

      -
      -main = print $ treeTakeDepth 4 infTreeTwo
      -
      +
      main = print $ treeTakeDepth 4 infTreeTwo
      +
      <  0
       : |-- -1
      @@ -2231,11 +2333,14 @@ main = print $ treeTakeDepth 4 infTreeTwo
       
      -
      -main = do
      -  print $ treeTakeDepth 4 nullTree
      -  print $ treeTakeDepth 4 infTreeTwo
      -
      + + +
      main = do
      +  print $ treeTakeDepth 4 nullTree
      +  print $ treeTakeDepth 4 infTreeTwo
      +
      + +
      @@ -2248,7 +2353,7 @@ Now, some of the really hardcore stuff can start.

      If you are like me, you should get the functional style. You should also understand a bit more the advantages of laziness by default. -But you also don’t really understand where to start in order to make a real +But you also don’t really understand where to start in order to make a real program. And in particular:

      @@ -2299,9 +2404,9 @@ To use pure functions you could do action2 (purefunction x) for exa

      In this section, I will explain how to use IO, not how it works. -You’ll see how Haskell separates the pure from the impure parts of the program.

      +You’ll see how Haskell separates the pure from the impure parts of the program.

      -

      Don’t stop because you’re trying to understand the details of the syntax. +

      Don’t stop because you’re trying to understand the details of the syntax. Answers will come in the next section.

      What to achieve?

      @@ -2312,18 +2417,21 @@ Print the sum of the numbers

      -
      -toList :: String -> [Integer]
      -toList input = read ("[" ++ input ++ "]")
       
      -main = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  print $ sum (toList input)
      -
      + +
      toList :: String -> [Integer]
      +toList input = read ("[" ++ input ++ "]")
      +
      +main = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  print $ sum (toList input)
      +
      + +

      It should be straightforward to understand the behavior of this program. -Let’s analyze the types in more detail.

      +Let’s analyze the types in more detail.

      putStrLn :: String -> IO ()
       getLine  :: IO String
      @@ -2369,9 +2477,9 @@ The meaning of this sentence should be clearer by the end of the next section.
       

      03_Hell/01_IO/02_progressive_io_example.lhs

      -

      Now let’s see how this program behaves. +

      Now let’s see how this program behaves. For example, what occur if the user enter something strange? -Let’s try:

      +Let’s try:

          % runghc 02_progressive_io_example.lhs
           Enter a list of numbers (separated by comma):
      @@ -2388,16 +2496,18 @@ Use the type Maybe.
       It is a very common type in Haskell.

      -
      -import Data.Maybe
      -
      + + +
      import Data.Maybe
      +
      + +

      What is this thing? Maybe is a type which takes one parameter. Its definition is:

      -
      -data Maybe a = Nothing | Just a
      -
      +
      data Maybe a = Nothing | Just a
      +

      This is a nice way to tell there was an error while trying to create/compute a value. @@ -2405,46 +2515,55 @@ The maybeRead function is a great example of this. This is a function similar to the function read5, but if something goes wrong the returned value is Nothing. If the value is right, it returns Just <the value>. -Don’t try to understand too much of this function. +Don’t try to understand too much of this function. I use a lower level function than read; reads.

      -
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -
      + + +
      maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +
      + +

      Now to be a bit more readable, we define a function which goes like this: If the string has the wrong format, it will return Nothing. -Otherwise, for example for “1,2,3”, it will return Just [1,2,3].

      +Otherwise, for example for “1,2,3”, it will return Just [1,2,3].

      -
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -
      + + +
      getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +
      + +

      We simply have to test the value in our main function.

      -
      -main :: IO ()
      -main = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> print (sum l)
      -          Nothing -> error "Bad format. Good Bye."
      -
      + + +
      main :: IO ()
      +main = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> print (sum l)
      +          Nothing -> error "Bad format. Good Bye."
      +
      + +

      In case of error, we display a nice error message.

      -

      Note that the type of each expression in the main’s do block remains of the form IO a. +

      Note that the type of each expression in the main’s do block remains of the form IO a. The only strange construction is error. -I’ll say error msg will simply take the needed type (here IO ()).

      +I’ll say error msg will simply take the needed type (here IO ()).

      One very important thing to note is the type of all the functions defined so far. There is only one function which contains IO in its type: main. @@ -2474,31 +2593,37 @@ I certainly forget many advantages, but the three main reasons are:

      We keep the first part:

      -
      -import Data.Maybe
       
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -
      + +
      import Data.Maybe
      +
      +maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +
      + +

      Now, we create a function which will ask the user for an list of integers until the input is right.

      -
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers (separated by comma):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      + + +
      askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers (separated by comma):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
      +
      + +

      This function is of type IO [Integer]. Such a type means that we retrieved a value of type [Integer] through some IO actions. @@ -2508,19 +2633,22 @@ Some people might explain while waving their hands:

      «This is an [Integer] inside an IO»

      -

      If you want to understand the details behind all of this, you’ll have to read the next section. +

      If you want to understand the details behind all of this, you’ll have to read the next section. But sincerely, if you just want to use IO. Just practice a little and remember to think about the type.

      Finally our main function is quite simpler:

      -
      -main :: IO ()
      -main = do
      +
      +
      +
      main :: IO ()
      +main = do
         list <- askUser
      -  print $ sum list
      -
      + print $ sum list +
      + +

      We have finished with our introduction to IO. This was quite fast. Here are the main things to remember:

      @@ -2528,7 +2656,7 @@ This was quite fast. Here are the main things to remember:

      • in the do bloc, each expression must have the type IO a. You are then limited in the number of expressions available. -For example, getLine, print, putStrLn, etc…
      • +For example, getLine, print, putStrLn, etc…
      • Try to externalize the pure functions as much as possible.
      • the IO a type means: an IO action which returns an element of type a. IO represents actions; under the hood, IO a is the type of a function. @@ -2575,7 +2703,7 @@ But look at a typical main function:

        which must be passed on to the next action.

        We create a function bind or (>>=). -With bind we don’t need temporary names anymore.

        +With bind we don’t need temporary names anymore.

        main =
           action1 >>= action2 >>= action3 >>= action4
        @@ -2594,24 +2722,23 @@ With bind we don’t need temporary names anymore.

        Why did we use this strange syntax, and what exactly is this IO type? It looks a bit like magic.

        -

        For now let’s just forget all about the pure parts of our program, and focus +

        For now let’s just forget all about the pure parts of our program, and focus on the impure parts:

        -
        -askUser :: IO [Integer]
        -askUser = do
        -  putStrLn "Enter a list of numbers (separated by commas):"
        -  input <- getLine
        -  let maybeList = getListFromString input in
        -      case maybeList of
        -          Just l  -> return l
        -          Nothing -> askUser
        +
        askUser :: IO [Integer]
        +askUser = do
        +  putStrLn "Enter a list of numbers (separated by commas):"
        +  input <- getLine
        +  let maybeList = getListFromString input in
        +      case maybeList of
        +          Just l  -> return l
        +          Nothing -> askUser
         
        -main :: IO ()
        -main = do
        +main :: IO ()
        +main = do
           list <- askUser
        -  print $ sum list
        -
        + print $ sum list +

        First remark; it looks like an imperative structure. Haskell is powerful enough to make impure code look imperative. @@ -2630,38 +2757,34 @@ The fact that a file exists or not can be seen as different states of the world. It is explicitly said main is a function that potentially changes the state of the world. Its type is then something like:

        -
        -main :: World -> World
        -
        +
        main :: World -> World
        +

        Not all functions may have access to this variable. Those which have access to this variable are impure. -Functions to which the world variable isn’t provided are pure6.

        +Functions to which the world variable isn’t provided are pure6.

        Haskell considers the state of the world as an input variable to main. But the real type of main is closer to this one7:

        -
        -main :: World -> ((),World)
        -
        +
        main :: World -> ((),World)
        +

        The () type is the null type. Nothing to see here.

        -

        Now let’s rewrite our main function with this in mind:

        +

        Now let’s rewrite our main function with this in mind:

        -
        -main w0 =
        -    let (list,w1) = askUser w0 in
        -    let (x,w2) = print (sum list,w1) in
        +
        main w0 =
        +    let (list,w1) = askUser w0 in
        +    let (x,w2) = print (sum list,w1) in
             x 
        -
        +

        First, we note that all functions which have side effects must have the type:

        -
        -World -> (a,World)
        -
        +
        World -> (a,World)
        +

        Where a is the type of the result. For example, a getChar function should have the type World -> (Char,World).

        @@ -2692,37 +2815,34 @@ Under the hood, print will evaluate as:

      Now, if you look at the style of the main function, it is clearly awkward. -Let’s try to do the same to the askUser function:

      +Let’s try to do the same to the askUser function:

      -
      -askUser :: World -> ([Integer],World)
      -
      +
      askUser :: World -> ([Integer],World)
      +

      Before:

      -
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers:"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      +
      askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers:"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
      +

      After:

      -
      -askUser w0 =
      -    let (_,w1)     = putStrLn "Enter a list of numbers:" in
      -    let (input,w2) = getLine w1 in
      -    let (l,w3)     = case getListFromString input of
      -                      Just l   -> (l,w2)
      -                      Nothing  -> askUser w2
      -    in
      +
      askUser w0 =
      +    let (_,w1)     = putStrLn "Enter a list of numbers:" in
      +    let (input,w2) = getLine w1 in
      +    let (l,w3)     = case getListFromString input of
      +                      Just l   -> (l,w2)
      +                      Nothing  -> askUser w2
      +    in
               (l,w3)
      -
      +

      This is similar, but awkward. Look at all these temporary w? names.

      @@ -2733,38 +2853,34 @@ Look at all these temporary w? names.

      We see a pattern. Each line is of the form:

      -
      -let (y,w') = action x w in
      -
      +
      let (y,w') = action x w in
      +
      -

      Even if for some line the first x argument isn’t needed. +

      Even if for some line the first x argument isn’t needed. The output type is a couple, (answer, newWorldValue). Each function f must have a type similar to:

      -
      -f :: World -> (a,World)
      -
      +
      f :: World -> (a,World)
      +

      Not only this, but we can also note that we always follow the same usage pattern:

      -
      -let (y,w1) = action1 w0 in
      -let (z,w2) = action2 w1 in
      -let (t,w3) = action3 w2 in
      +
      let (y,w1) = action1 w0 in
      +let (z,w2) = action2 w1 in
      +let (t,w3) = action3 w2 in
       ...
      -
      +

      Each action can take from 0 to n parameters. And in particular, each action can take a parameter from the result of a line above.

      For example, we could also have:

      -
      -let (_,w1) = action1 x w0   in
      -let (z,w2) = action2 w1     in
      -let (_,w3) = action3 x z w2 in
      +
      let (_,w1) = action1 x w0   in
      +let (z,w2) = action2 w1     in
      +let (_,w3) = action3 x z w2 in
       ...
      -
      +

      And of course actionN w :: (World) -> (a,World).

      @@ -2782,237 +2898,221 @@ let (y,w2) = action2 w1 in
      -

      Jocker pencil trick

      +

      Jocker pencil trick

      Now, we will do a magic trick. -We will make the temporary world symbol “disappear”. +We will make the temporary world symbol “disappear”. We will bind the two lines. -Let’s define the bind function. +Let’s define the bind function. Its type is quite intimidating at first:

      -
      -bind :: (World -> (a,World)) 
      -        -> (a -> (World -> (b,World))) 
      -        -> (World -> (b,World)) 
      -
      +
      bind :: (World -> (a,World)) 
      +        -> (a -> (World -> (b,World))) 
      +        -> (World -> (b,World)) 
      +

      But remember that (World -> (a,World)) is the type for an IO action. -Now let’s rename it for clarity:

      +Now let’s rename it for clarity:

      -
      -type IO a = World -> (a, World)
      -
      +
      type IO a = World -> (a, World)
      +

      Some example of functions:

      -
      -getLine :: IO String
      -print :: Show a => a -> IO ()
      -
      +
      getLine :: IO String
      +print :: Show a => a -> IO ()
      +

      getLine is an IO action which takes a world as parameter and returns a couple (String,World). Which can be summarized as: getLine is of type IO String. -Which we also see as, an IO action which will return a String “embeded inside an IO”.

      +Which we also see as, an IO action which will return a String “embeded inside an IO”.

      The function print is also interesting. It takes one argument which can be shown. In fact it takes two arguments. The first is the value to print and the other is the state of world. It then returns a couple of type ((),World). -This means it changes the state of the world, but doesn’t yield anymore data.

      +This means it changes the state of the world, but doesn’t yield anymore data.

      This type helps us simplify the type of bind:

      -
      -bind :: IO a 
      -        -> (a -> IO b) 
      -        -> IO b
      -
      +
      bind :: IO a 
      +        -> (a -> IO b) 
      +        -> IO b
      +

      It says that bind takes two IO actions as parameter and return another IO action.

      Now, remember the important patterns. The first was:

      -
      -let (x,w1) = action1 w0 in
      -let (y,w2) = action2 x w1 in
      +
      let (x,w1) = action1 w0 in
      +let (y,w2) = action2 x w1 in
       (y,w2)
      -
      +

      Look at the types:

      -
      -action1  :: IO a
      -action2  :: a -> IO b
      -(y,w2)   :: IO b
      -
      +
      action1  :: IO a
      +action2  :: a -> IO b
      +(y,w2)   :: IO b
      +
      -

      Doesn’t it seem familiar?

      +

      Doesn’t it seem familiar?

      -
      -(bind action1 action2) w0 =
      -    let (x, w1) = action1 w0
      +
      (bind action1 action2) w0 =
      +    let (x, w1) = action1 w0
               (y, w2) = action2 x w1
      -    in  (y, w2)
      -
      + in (y, w2) +
      -

      The idea is to hide the World argument with this function. Let’s go: +

      The idea is to hide the World argument with this function. Let’s go: As an example imagine if we wanted to simulate:

      -
      -let (line1,w1) = getLine w0 in
      -let ((),w2) = print line1 in
      +
      let (line1,w1) = getLine w0 in
      +let ((),w2) = print line1 in
       ((),w2)
      -
      +

      Now, using the bind function:

      -
      -(res,w2) = (bind getLine (\l -> print l)) w0
      -
      +
      (res,w2) = (bind getLine (\l -> print l)) w0
      +

      As print is of type (World → ((),World)), we know res = () (null type). -If you didn’t see what was magic here, let’s try with three lines this time.

      +If you didn’t see what was magic here, let’s try with three lines this time.

      -
      -let (line1,w1) = getLine w0 in
      -let (line2,w2) = getLine w1 in
      -let ((),w3) = print (line1 ++ line2) in
      +
      let (line1,w1) = getLine w0 in
      +let (line2,w2) = getLine w1 in
      +let ((),w3) = print (line1 ++ line2) in
       ((),w3)
      -
      +

      Which is equivalent to:

      -
      -(res,w3) = bind getLine (\line1 ->
      -             bind getLine (\line2 -> 
      -               print (line1 ++ line2)))
      -
      +
      (res,w3) = bind getLine (\line1 ->
      +             bind getLine (\line2 -> 
      +               print (line1 ++ line2)))
      +
      -

      Didn’t you notice something? +

      Didn’t you notice something? Yes, no temporary World variables are used anywhere! This is MA. GIC.

      We can use a better notation. -Let’s use (>>=) instead of bind. +Let’s use (>>=) instead of bind. (>>=) is an infix function like (+); reminder 3 + 4 ⇔ (+) 3 4

      -
      -(res,w3) = getLine >>=
      -           \line1 -> getLine >>=
      -           \line2 -> print (line1 ++ line2)
      -
      +
      (res,w3) = getLine >>=
      +           \line1 -> getLine >>=
      +           \line2 -> print (line1 ++ line2)
      +

      Ho Ho Ho! Happy Christmas Everyone! Haskell has made syntactical sugar for us:

      -
      -do
      +
      do
         x <- action1
         y <- action2
         z <- action3
         ...
      -
      +

      Is replaced by:

      -
      -action1 >>= \x ->
      +
      action1 >>= \x ->
       action2 >>= \y ->
       action3 >>= \z ->
       ...
      -
      +

      Note you can use x in action2 and x and y in action3.

      But what about the lines not using the <-? Easy, another function blindBind:

      -
      -blindBind :: IO a -> IO b -> IO b
      +
      blindBind :: IO a -> IO b -> IO b
       blindBind action1 action2 w0 =
           bind action (\_ -> action2) w0
      -
      +
      -

      I didn’t simplify this definition for clarity purpose. -Of course we can use a better notation, we’ll use the (>>) operator.

      +

      I didn’t simplify this definition for clarity purpose. +Of course we can use a better notation, we’ll use the (>>) operator.

      And

      -
      -do
      +
      do
           action1
           action2
           action3
      -
      +

      Is transformed into

      -
      -action1 >>
      +
      action1 >>
       action2 >> 
       action3
      -
      +

      Also, another function is quite useful.

      -
      -putInIO :: a -> IO a
      -putInIO x = IO (\w -> (x,w))
      -
      +
      putInIO :: a -> IO a
      +putInIO x = IO (\w -> (x,w))
      +
      -

      This is the general way to put pure values inside the “IO context”. +

      This is the general way to put pure values inside the “IO context”. The general name for putInIO is return. This is quite a bad name when you learn Haskell. return is very different from what you might be used to.


      03_Hell/01_IO/21_Detailled_IO.lhs

      -

      To finish, let’s translate our example:

      +

      To finish, let’s translate our example:

      -
      +
      
      +askUser :: IO [Integer]
      +askUser = do
      +  putStrLn "Enter a list of numbers (separated by commas):"
      +  input <- getLine
      +  let maybeList = getListFromString input in
      +      case maybeList of
      +          Just l  -> return l
      +          Nothing -> askUser
       
      -askUser :: IO [Integer]
      -askUser = do
      -  putStrLn "Enter a list of numbers (separated by commas):"
      -  input <- getLine
      -  let maybeList = getListFromString input in
      -      case maybeList of
      -          Just l  -> return l
      -          Nothing -> askUser
      -
      -main :: IO ()
      -main = do
      +main :: IO ()
      +main = do
         list <- askUser
      -  print $ sum list
      -
      + print $ sum list +

      Is translated into:

      -
      -import Data.Maybe
       
      -maybeRead :: Read a => String -> Maybe a
      -maybeRead s = case reads s of
      -                  [(x,"")]    -> Just x
      -                  _           -> Nothing
      -getListFromString :: String -> Maybe [Integer]
      -getListFromString str = maybeRead $ "[" ++ str ++ "]"
      -askUser :: IO [Integer]
      +
      +
      import Data.Maybe
      +
      +maybeRead :: Read a => String -> Maybe a
      +maybeRead s = case reads s of
      +                  [(x,"")]    -> Just x
      +                  _           -> Nothing
      +getListFromString :: String -> Maybe [Integer]
      +getListFromString str = maybeRead $ "[" ++ str ++ "]"
      +askUser :: IO [Integer]
       askUser = 
      -    putStrLn "Enter a list of numbers (sep. by commas):" >>
      -    getLine >>= \input ->
      -    let maybeList = getListFromString input in
      -      case maybeList of
      -        Just l -> return l
      -        Nothing -> askUser
      +    putStrLn "Enter a list of numbers (sep. by commas):" >>
      +    getLine >>= \input ->
      +    let maybeList = getListFromString input in
      +      case maybeList of
      +        Just l -> return l
      +        Nothing -> askUser
       
      -main :: IO ()
      +main :: IO ()
       main = askUser >>=
      -  \list -> print $ sum list
      -
      + \list -> print $ sum list +
      + +

      You can compile this code to verify it keeps working.

      @@ -3046,19 +3146,18 @@ To be an instance of this type class, you must provide the functions (> The function (>>) will be derived from (>>=). Here is how the type class Monad is declared (mostly):

      -
      -class Monad m  where
      -  (>>=) :: m a -> (a -> m b) -> m b
      -  return :: a -> m a
      +
      class Monad m  where
      +  (>>=) :: m a -> (a -> m b) -> m b
      +  return :: a -> m a
       
      -  (>>) :: m a -> m b -> m b
      +  (>>) :: m a -> m b -> m b
         f >> g = f >>= \_ -> g
       
      -  -- You should generally safely ignore this function
      -  -- which I believe exists for historical reason
      -  fail :: String -> m a
      -  fail = error
      -
      + -- You should generally safely ignore this function + -- which I believe exists for historical reason + fail :: String -> m a + fail = error +

      Remarks:

      @@ -3071,7 +3170,7 @@ A better word should have been typeclass. That means a set of types. For a type to belong to a class, all functions of the class must be provided for this type.
    • In this particular example of type class, the type m must be a type that takes an argument. -for example IO a, but also Maybe a, [a], etc…
    • +for example IO a, but also Maybe a, [a], etc…
    • To be a useful monad, your function must obey some rules. If your construction does not obey these rules strange things might happens:

      @@ -3095,69 +3194,75 @@ It is particularly useful to remove very deep if..then..else.. cons if you can afford to follow a list of operations without being negative.

      -
      -deposit  value account = account + value
      +
      +
      +
      deposit  value account = account + value
       withdraw value account = account - value
       
      -eligible :: (Num a,Ord a) => a -> Bool
      +eligible :: (Num a,Ord a) => a -> Bool
       eligible account = 
      -  let account1 = deposit 100 account in
      -    if (account1 < 0) 
      -    then False
      -    else 
      -      let account2 = withdraw 200 account1 in
      -      if (account2 < 0) 
      -      then False
      -      else 
      -        let account3 = deposit 100 account2 in
      -        if (account3 < 0) 
      -        then False
      -        else 
      -          let account4 = withdraw 300 account3 in
      -          if (account4 < 0) 
      -          then False
      -          else 
      -            let account5 = deposit 1000 account4 in
      -            if (account5 < 0) 
      -            then False
      -            else
      -              True
      +  let account1 = deposit 100 account in
      +    if (account1 < 0) 
      +    then False
      +    else 
      +      let account2 = withdraw 200 account1 in
      +      if (account2 < 0) 
      +      then False
      +      else 
      +        let account3 = deposit 100 account2 in
      +        if (account3 < 0) 
      +        then False
      +        else 
      +          let account4 = withdraw 300 account3 in
      +          if (account4 < 0) 
      +          then False
      +          else 
      +            let account5 = deposit 1000 account4 in
      +            if (account5 < 0) 
      +            then False
      +            else
      +              True
      +
      +main = do
      +  print $ eligible 300 -- True
      +  print $ eligible 299 -- False
      +
      + -main = do - print $ eligible 300 -- True - print $ eligible 299 -- False -

      03_Hell/02_Monads/10_Monads.lhs


      03_Hell/02_Monads/11_Monads.lhs

      -

      Now, let’s make it better using Maybe and the fact that it is a Monad

      +

      Now, let’s make it better using Maybe and the fact that it is a Monad

      -
      -deposit :: (Num a) => a -> a -> Maybe a
      -deposit value account = Just (account + value)
       
      -withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      -withdraw value account = if (account < value) 
      -                         then Nothing 
      -                         else Just (account - value)
       
      -eligible :: (Num a, Ord a) => a -> Maybe Bool
      -eligible account = do
      +
      deposit :: (Num a) => a -> a -> Maybe a
      +deposit value account = Just (account + value)
      +
      +withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      +withdraw value account = if (account < value) 
      +                         then Nothing 
      +                         else Just (account - value)
      +
      +eligible :: (Num a, Ord a) => a -> Maybe Bool
      +eligible account = do
         account1 <- deposit 100 account 
         account2 <- withdraw 200 account1 
         account3 <- deposit 100 account2 
         account4 <- withdraw 300 account3 
         account5 <- deposit 1000 account4
      -  Just True
      +  Just True
      +
      +main = do
      +  print $ eligible 300 -- Just True
      +  print $ eligible 299 -- Nothing
      +
      + -main = do - print $ eligible 300 -- Just True - print $ eligible 299 -- Nothing -

      03_Hell/02_Monads/11_Monads.lhs

      @@ -3167,28 +3272,31 @@ main = do

      Not bad, but we can make it even better:

      -
      -deposit :: (Num a) => a -> a -> Maybe a
      -deposit value account = Just (account + value)
       
      -withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      -withdraw value account = if (account < value) 
      -                         then Nothing 
      -                         else Just (account - value)
       
      -eligible :: (Num a, Ord a) => a -> Maybe Bool
      +
      deposit :: (Num a) => a -> a -> Maybe a
      +deposit value account = Just (account + value)
      +
      +withdraw :: (Num a,Ord a) => a -> a -> Maybe a
      +withdraw value account = if (account < value) 
      +                         then Nothing 
      +                         else Just (account - value)
      +
      +eligible :: (Num a, Ord a) => a -> Maybe Bool
       eligible account =
         deposit 100 account >>=
         withdraw 200 >>=
         deposit 100  >>=
         withdraw 300 >>=
         deposit 1000 >>
      -  return True
      +  return True
      +
      +main = do
      +  print $ eligible 300 -- Just True
      +  print $ eligible 299 -- Nothing
      +
      + -main = do - print $ eligible 300 -- Just True - print $ eligible 299 -- Nothing -

      We have proven that Monads are a good way to make our code more elegant. Note this idea of code organization, in particular for Maybe can be used @@ -3200,7 +3308,7 @@ In fact, this is the kind of construction we make naturally.

      The first element in the sequence being evaluated to Nothing will stop the complete evaluation. -This means you don’t execute all lines. +This means you don’t execute all lines. You have this for free, thanks to laziness.

    • @@ -3221,22 +3329,25 @@ But now a cooler example, lists.

      Here we go:

      -
      -import Control.Monad (guard)
      +
      +
      +
      import Control.Monad (guard)
       
       allCases = [1..10]
       
      -resolve :: [(Int,Int,Int)]
      -resolve = do
      +resolve :: [(Int,Int,Int)]
      +resolve = do
                     x <- allCases
                     y <- allCases
                     z <- allCases
                     guard $ 4*x + 2*y < z
      -              return (x,y,z)
      +              return (x,y,z)
      +
      +main = do
      +  print resolve
      +
      + -main = do - print resolve -

      MA. GIC. :

      @@ -3246,14 +3357,17 @@ main = do

      For the list monad, there is also a syntactical sugar:

      -
      -  print $ [ (x,y,z) | x <- allCases, 
      +
      +
      +
        print $ [ (x,y,z) | x <- allCases, 
                             y <- allCases, 
                             z <- allCases, 
                             4*x + 2*y < z ]
      -
      +
      + +
      -

      I won’t list all the monads, but there are many monads. +

      I won’t list all the monads, but there are many monads. Using monads simplifies the manipulation of several notions in pure languages. In particular, monad are very useful for:

      @@ -3263,10 +3377,10 @@ In particular, monad are very useful for:

    • generating pseudo random numbers,
    • keeping configuration state,
    • writing state,
    • -
    • +
    • -

      If you have followed me until here, then you’ve done it! +

      If you have followed me until here, then you’ve done it! You know monads8!

      and to understand when you can use them and create your own. But you already @@ -3294,7 +3408,7 @@ Unfortunately we removed two properties from our tree:

      In this section we will try to keep the first property. -Concerning the second one, we must relax it but we’ll discuss how to +Concerning the second one, we must relax it but we’ll discuss how to keep it as much as possible.

      @@ -3302,94 +3416,109 @@ keep it as much as possible.

      This code is mostly the same as the one in the [tree section](#trees).
      -
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
       
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      +-- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
      +
      +
      + -

      Our first step is to create some pseudo-random number list:

      -
      -shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
      -
      + + +
      shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
      +
      + +

      Just as a reminder, here is the definition of treeFromList

      -
      -treeFromList :: (Ord a) => [a] -> BinTree a
      -treeFromList []    = Empty
      -treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      -                             (treeFromList (filter (>x) xs))
      -
      + + +
      treeFromList :: (Ord a) => [a] -> BinTree a
      +treeFromList []    = Empty
      +treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
      +                             (treeFromList (filter (>x) xs))
      +
      + +

      and treeTakeDepth:

      -
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +
      +
      +
      treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +

      See the result of:

      -
      -main = do
      -      putStrLn "take 10 shuffle"
      -      print $ take 10 shuffle
      -      putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
      -      print $ treeTakeDepth 4 (treeFromList shuffle)
      -
      + + +
      main = do
      +      putStrLn "take 10 shuffle"
      +      print $ take 10 shuffle
      +      putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
      +      print $ treeTakeDepth 4 (treeFromList shuffle)
      +
      + +
      % runghc 02_Hard_Part/41_Infinites_Structures.lhs
       take 10 shuffle
      @@ -3418,9 +3547,8 @@ Beware though, it will only work if you always have something to put into a bran
       
       

      For example

      -
      -treeTakeDepth 4 (treeFromList [1..]) 
      -
      +
      treeTakeDepth 4 (treeFromList [1..]) 
      +

      will loop forever. Simply because it will try to access the head of filter (<1) [2..]. @@ -3446,62 +3574,68 @@ But filter is not smart enought to understand that the result is th This code is mostly the same as the preceding one.

      -
      -import Debug.Trace (trace)
      -import Data.List
      -data BinTree a = Empty 
      -                 | Node a (BinTree a) (BinTree a) 
      -                  deriving (Eq,Ord)
      -
      + + +
      import Debug.Trace (trace)
      +import Data.List
      +data BinTree a = Empty 
      +                 | Node a (BinTree a) (BinTree a) 
      +                  deriving (Eq,Ord)
      +
      + +
      -
      --- declare BinTree a to be an instance of Show
      -instance (Show a) => Show (BinTree a) where
      -  -- will start by a '<' before the root
      -  -- and put a : a begining of line
      -  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      -    where
      -    treeshow pref Empty = ""
      -    treeshow pref (Node x Empty Empty) = 
      +
      +
      +
      -- declare BinTree a to be an instance of Show
      +instance (Show a) => Show (BinTree a) where
      +  -- will start by a '<' before the root
      +  -- and put a : a begining of line
      +  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
      +    where
      +    treeshow pref Empty = ""
      +    treeshow pref (Node x Empty Empty) = 
                         (pshow pref x)
       
      -    treeshow pref (Node x left Empty) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " left)
      +    treeshow pref (Node x left Empty) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " left)
       
      -    treeshow pref (Node x Empty right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x Empty right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    treeshow pref (Node x left right) = 
      -                  (pshow pref x) ++ "\n" ++
      -                  (showSon pref "|--" "|  " left) ++ "\n" ++
      -                  (showSon pref "`--" "   " right)
      +    treeshow pref (Node x left right) = 
      +                  (pshow pref x) ++ "\n" ++
      +                  (showSon pref "|--" "|  " left) ++ "\n" ++
      +                  (showSon pref "`--" "   " right)
       
      -    -- show a tree using some prefixes to make it nice
      +    -- show a tree using some prefixes to make it nice
           showSon pref before next t = 
                         pref ++ before ++ treeshow (pref ++ next) t
       
      -    -- pshow replace "\n" by "\n"++pref
      -    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
      +    -- pshow replace "\n" by "\n"++pref
      +    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)
       
      -    -- replace on char by another string
      +    -- replace on char by another string
           replace c new string =
      -      concatMap (change c new) string
      -      where
      +      concatMap (change c new) string
      +      where
                 change c new x 
                     | x == c = new
      -              | otherwise = x:[] -- "x"
      +              | otherwise = x:[] -- "x"
       
      -treeTakeDepth _ Empty = Empty
      -treeTakeDepth 0 _     = Empty
      -treeTakeDepth n (Node x left right) = let
      +treeTakeDepth _ Empty = Empty
      +treeTakeDepth 0 _     = Empty
      +treeTakeDepth n (Node x left right) = let
                 nl = treeTakeDepth (n-1) left
                 nr = treeTakeDepth (n-1) right
      -          in
      -              Node x nl nr
      -
      + in + Node x nl nr +
      + +
      @@ -3513,23 +3647,26 @@ We generated only 4331 different numbers. To resolve this we make a slightly better shuffle function.

      -
      -shuffle = map rand [1..]
      -          where 
      -              rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2)
      -              p x = m*x^2 + n*x + o -- some polynome
      +
      +
      +
      shuffle = map rand [1..]
      +          where 
      +              rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2)
      +              p x = m*x^2 + n*x + o -- some polynome
                     m = 3123    
                     n = 31
                     o = 7641
                     c = 1237
      -
      +
      + +

      This shuffle function has the property (hopefully) not to have an upper nor lower bound. -But having a better shuffle list isn’t enough not to enter an infinite loop.

      +But having a better shuffle list isn’t enough not to enter an infinite loop.

      Generally, we cannot decide whether filter (<x) xs is empty. -Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. -This new version of code can create binary tree which don’t have the following property for some of its nodes:

      +Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. +This new version of code can create binary tree which don’t have the following property for some of its nodes:

      Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.

      @@ -3541,42 +3678,51 @@ Furthermore, by construction, each node value is unique in the tree.

      Here is our new version of treeFromList. We simply have replaced filter by safefilter.

      -
      -treeFromList :: (Ord a, Show a) => [a] -> BinTree a
      -treeFromList []    = Empty
      -treeFromList (x:xs) = Node x left right
      -          where 
      +
      +
      +
      treeFromList :: (Ord a, Show a) => [a] -> BinTree a
      +treeFromList []    = Empty
      +treeFromList (x:xs) = Node x left right
      +          where 
                     left = treeFromList $ safefilter (<x) xs
                     right = treeFromList $ safefilter (>x) xs
      -
      +
      + +
      -

      This new function safefilter is almost equivalent to filter but don’t enter infinite loop if the result is a finite list. +

      This new function safefilter is almost equivalent to filter but don’t enter infinite loop if the result is a finite list. If it cannot find an element for which the test is true after 10000 consecutive steps, then it considers to be the end of the search.

      -
      -safefilter :: (a -> Bool) -> [a] -> [a]
      +
      +
      +
      safefilter :: (a -> Bool) -> [a] -> [a]
       safefilter f l = safefilter' f l nbTry
      -  where
      +  where
             nbTry = 10000
             safefilter' _ _ 0 = []
             safefilter' _ [] _ = []
             safefilter' f (x:xs) n = 
      -                  if f x 
      -                     then x : safefilter' f xs nbTry 
      -                     else safefilter' f xs (n-1) 
      -
      + if f x + then x : safefilter' f xs nbTry + else safefilter' f xs (n-1) +
      + +

      Now run the program and be happy:

      -
      -main = do
      -      putStrLn "take 10 shuffle"
      -      print $ take 10 shuffle
      -      putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
      -      print $ treeTakeDepth 8 (treeFromList $ shuffle)
      -
      + + +
      main = do
      +      putStrLn "take 10 shuffle"
      +      print $ take 10 shuffle
      +      putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
      +      print $ treeTakeDepth 8 (treeFromList $ shuffle)
      +
      + +

      You should realize the time to print each value is different. This is because Haskell compute each value when it needs it. @@ -3598,26 +3744,25 @@ safefilter' f l = if filter f (take 10000 l) == [] then [] else filter f l

      -

      Explain why it doesn’t work and can enter into an infinite loop.

      +

      Explain why it doesn’t work and can enter into an infinite loop.

    • Suppose that shuffle is real random list with growing bounds. -If you study a bit this structure, you’ll discover that with probability 1, +If you study a bit this structure, you’ll discover that with probability 1, this structure is finite. Using the following code (suppose we could use safefilter' directly as if was not in the where of safefilter) find a definition of f such that with probability 1, -treeFromList’ shuffle is infinite. And prove it. +treeFromList’ shuffle is infinite. And prove it. Disclaimer, this is only a conjecture.
    • -
      -treeFromList' []  n = Empty
      -treeFromList' (x:xs) n = Node x left right
      -    where
      +
      treeFromList' []  n = Empty
      +treeFromList' (x:xs) n = Node x left right
      +    where
               left = treeFromList' (safefilter' (<x) xs (f n)
               right = treeFromList' (safefilter' (>x) xs (f n)
               f = ???
      -
      +

      04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs

      @@ -3636,7 +3781,7 @@ Thank you man.

      Même si tous les langages récents essayent de les cacher, ils restent présents.

    • -

      I know I’m cheating. But I will talk about non-strict later.

      +

      I know I’m cheating. But I will talk about non-strict later.

    • For the brave, a more complete explanation of pattern matching can be found here.

      @@ -3648,13 +3793,13 @@ Thank you man.

      Which itself is very similar to the javascript eval on a string containing JSON).

    • -

      There are some unsafe exceptions to this rule. But you shouldn’t see such use on a real application except maybe for debugging purpose.

      +

      There are some unsafe exceptions to this rule. But you shouldn’t see such use on a real application except maybe for debugging purpose.

    • For the curious the real type is data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}. All the # as to do with optimisation and I swapped the fields in my example. But mostly, the idea is exactly the same.

    • -

      Well, you’ll certainly need to practice a bit to get used to them

      +

      Well, you’ll certainly need to practice a bit to get used to them

    • @@ -3762,7 +3907,7 @@ Thank you man.

      Écrit le : 08/02/2012 - modifié le : 17/04/2012 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/Higher-order-function-in-zsh/code/functional.sh b/output/Scratch/fr/blog/Higher-order-function-in-zsh/code/functional.sh index 5e3900291..bf99fa3bf 100644 --- a/output/Scratch/fr/blog/Higher-order-function-in-zsh/code/functional.sh +++ b/output/Scratch/fr/blog/Higher-order-function-in-zsh/code/functional.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env zsh # Provide higer-order functions diff --git a/output/Scratch/fr/blog/Higher-order-function-in-zsh/index.html b/output/Scratch/fr/blog/Higher-order-function-in-zsh/index.html index 2d8d4f81a..a86df044c 100644 --- a/output/Scratch/fr/blog/Higher-order-function-in-zsh/index.html +++ b/output/Scratch/fr/blog/Higher-order-function-in-zsh/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -60,16 +63,16 @@
      -

      tlàl : des fonctions d’ordres supérieurs en zsh.

      +

      tlàl : des fonctions d’ordres supérieurs en zsh.

      -

      Tout d’abord, pourquoi c’est important d’avoir ces fonctions. -Plus je programmais avec zsh plus j’essayais d’avoir un style fonctionnel.

      +

      Tout d’abord, pourquoi c’est important d’avoir ces fonctions. +Plus je programmais avec zsh plus j’essayais d’avoir un style fonctionnel.

      -

      Le minimum pour pouvoir avoir du code plus lisible c’est de posséder les fonctions map, filter et fold.

      +

      Le minimum pour pouvoir avoir du code plus lisible c’est de posséder les fonctions map, filter et fold.

      Voici pourquoi avec une comparaison. Commençons par un programme qui converti tous les gif en png dans plusieurs répertoires projets contenant tous des répertoires resources. @@ -77,38 +80,36 @@ Avant :

      Avant ⇒

      -
      -# for each directory in projects dir
      -for toProject in /path/to/projects/*(/N); do
      -    # toProject is /path/to/projects/foo
      -    # project become foo (:t for tail)
      -    project=${toProject:t}
      -    for toResource in $toProject/resources/*.gif(.N); do
      -        convert $toResource ${toResource:r}.png && \
      -        \rm -f $toResource
      -    done
      -done
      -
      +
      # for each directory in projects dir
      +for toProject in /path/to/projects/*(/N); do
      +    # toProject is /path/to/projects/foo
      +    # project become foo (:t for tail)
      +    project=${toProject:t}
      +    for toResource in $toProject/resources/*.gif(.N); do
      +        convert $toResource ${toResource:r}.png && \
      +        \rm -f $toResource
      +    done
      +done
      +
        -
      • Le (/N) permet de sélectionner seulement les répertoires sans casser la boucle s’il n’y a pas de “match”.
      • -
      • Le (.N) permet de sélection seulement les fichiers, aussi sans tout arréter s’il ne trouve rien.
      • -
      • Le :t signfie “tail” ; si toto=/path/to/file.ext alors ${toto:t}=file.ext.
      • +
      • Le (/N) permet de sélectionner seulement les répertoires sans casser la boucle s’il n’y a pas de “match”.
      • +
      • Le (.N) permet de sélection seulement les fichiers, aussi sans tout arréter s’il ne trouve rien.
      • +
      • Le :t signfie “tail” ; si toto=/path/to/file.ext alors ${toto:t}=file.ext.

      Après

      -
      -gif_to_png() { convert $1 ${1:r}.png && \rm -f $1 }
      +
      gif_to_png() { convert $1 ${1:r}.png && \rm -f $1 }
       
      -handle_resources() { map gif_to_png $1/resources/*.gif(.N) }
      +handle_resources() { map gif_to_png $1/resources/*.gif(.N) }
       
       map handle_resources /path/to/projects/*(/N)
      -
      +

      Plus de bloc ! -Oui, c’est un poil plus difficile à lire pour les non initiés. -Mais c’est à la fois plus concis et plus robuste.

      +Oui, c’est un poil plus difficile à lire pour les non initiés. +Mais c’est à la fois plus concis et plus robuste.

      Et encore ce code ne possède pas de test. Recommençons sur le même principe.

      @@ -117,111 +118,109 @@ Recommençons sur le même principe.

      Before ⇒

      -
      -for toProject in Projects/*; do
      -    project=$toProject:t
      -    if print -- project | grep -v s >/dev/null
      -    then
      -        print $project
      -        for toResource in $toProject/*(.N); do
      -            if print -- ${toResource:t} | grep $project >/dev/null; then
      -                print -- "X $toResource"
      -            fi
      -        done
      -    fi
      -done
      -
      +
      for toProject in Projects/*; do
      +    project=$toProject:t
      +    if print -- project | grep -v s >/dev/null
      +    then
      +        print $project
      +        for toResource in $toProject/*(.N); do
      +            if print -- ${toResource:t} | grep $project >/dev/null; then
      +                print -- "X $toResource"
      +            fi
      +        done
      +    fi
      +done
      +

      After ⇒

      -
      -contain_no_s() { print $1 | grep -v s }
      +
      contain_no_s() { print $1 | grep -v s }
       
       function verify_file_name {                               
      -    local project=$1:t
      -    contains_project_name() { print $1:t | grep $project }
      -    map "print -- X" $(filter contains_project_name $1/*(.N))
      +    local project=$1:t
      +    contains_project_name() { print $1:t | grep $project }
      +    map "print -- X" $(filter contains_project_name $1/*(.N))
       }
       
      -map verify_file_name $( filter contain_no_s Projects/* )
      -
      +map verify_file_name $( filter contain_no_s Projects/* ) +

      La première version peu paraître plus facile à lire. -Mais la seconde est plus bien supérieure en terme d’architecture. -Je ne veux pas discuster ici pourquoi c’est mieux. -Je vous demande simplement de me croire quand je dis que l’approche fonctionnelle est supérieure.

      +Mais la seconde est plus bien supérieure en terme d’architecture. +Je ne veux pas discuster ici pourquoi c’est mieux. +Je vous demande simplement de me croire quand je dis que l’approche fonctionnelle est supérieure.

      -

      Actuellement il me manque une fonction lambda, si quelqu’un à une idée elle serait la bienvenue. +

      Actuellement il me manque une fonction lambda, si quelqu’un à une idée elle serait la bienvenue. Je ne sais pas encore comment créer facilement des fonctions anonymes.

      Voici le code source :

      -
      -
      -#!/usr/bin/env zsh
      +
       
      -# Provide higer-order functions 
      +
      #!/usr/bin/env zsh
       
      -# usage:
      -#
      -# $ foo(){print "x: $1"}
      -# $ map foo a b c d
      -# x: a
      -# x: b
      -# x: c
      -# x: d
      +# Provide higer-order functions 
      +
      +# usage:
      +#
      +# $ foo(){print "x: $1"}
      +# $ map foo a b c d
      +# x: a
      +# x: b
      +# x: c
      +# x: d
       function map {
      -    local func_name=$1
      +    local func_name=$1
           shift
      -    for elem in $@; print -- $(eval $func_name $elem)
      +    for elem in $@; print -- $(eval $func_name $elem)
       }
       
      -# $ bar() { print $(($1 + $2)) }
      -# $ fold bar 0 1 2 3 4 5
      -# 15
      -# -- but also
      -# $ fold bar 0 $( seq 1 100 )
      +# $ bar() { print $(($1 + $2)) }
      +# $ fold bar 0 1 2 3 4 5
      +# 15
      +# -- but also
      +# $ fold bar 0 $( seq 1 100 )
       function fold {
      -    if (($#<2)) {
      -        print -- "ERROR fold use at least 2 arguments" >&2
      +    if (($#<2)) {
      +        print -- "ERROR fold use at least 2 arguments" >&2
               return 1
           }
      -    if (($#<3)) {
      -        print -- $2
      +    if (($#<3)) {
      +        print -- $2
               return 0
      -    } else {
      +    } else {
               local acc
               local right
      -        local func_name=$1
      -        local init_value=$2
      -        local first_value=$3
      +        local func_name=$1
      +        local init_value=$2
      +        local first_value=$3
               shift 3
      -        right=$( fold $func_name $init_value $@ )
      -        acc=$( eval "$func_name $first_value $right" )
      -        print -- $acc
      +        right=$( fold $func_name $init_value $@ )
      +        acc=$( eval "$func_name $first_value $right" )
      +        print -- $acc
               return 0
           }
       }
       
      -# usage:
      -#
      -# $ baz() { print $1 | grep baz }
      -# $ filter baz titi bazaar biz
      -# bazaar
      +# usage:
      +#
      +# $ baz() { print $1 | grep baz }
      +# $ filter baz titi bazaar biz
      +# bazaar
       function filter {
      -    local predicate=$1
      +    local predicate=$1
           local result
           typeset -a result
           shift
      -    for elem in $@; do
      -        if eval $predicate $elem >/dev/null; then
      -            result=( $result $elem )
      -        fi
      -    done
      -    print $result
      +    for elem in $@; do
      +        if eval $predicate $elem >/dev/null; then
      +            result=( $result $elem )
      +        fi
      +    done
      +    print $result
       }
      -
      -
      +
      + @@ -338,7 +337,7 @@ function filter {
      Écrit le : 28/09/2011 - modifié le : 26/10/2011 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/Learn-Vim-Progressively/index.html b/output/Scratch/fr/blog/Learn-Vim-Progressively/index.html index f9569f8ce..f03c40ef7 100644 --- a/output/Scratch/fr/blog/Learn-Vim-Progressively/index.html +++ b/output/Scratch/fr/blog/Learn-Vim-Progressively/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -66,7 +69,7 @@
      -

      Vim ou l’éditeur qui vallait 3 milliards :

      +

      Vim ou l’éditeur qui vallait 3 milliards :

      Meilleur, plus fort, plus rapide.

      @@ -76,7 +79,7 @@ Aucun éditeur que je connaisse ne le surpasse. Sa prise en mais est difficile, mais payante.

      -

      Je vous conseille de l’apprendre en 4 étapes :

      +

      Je vous conseille de l’apprendre en 4 étapes :

      1. La survie
      2. @@ -92,10 +95,10 @@ Apprendre vim sera difficile au début. Ça prendra du temps. Vous devrez vous entraîner. Apprendre vim ressemble beaucoup à apprendre un instrument de musique. -N’espérez pas être plus efficace avec vim qu’avec un autre éditeur avant au moins trois jours. +N’espérez pas être plus efficace avec vim qu’avec un autre éditeur avant au moins trois jours. En fait ça sera certainement plus 2 semaines que 3 jours.

        -

        1er Niveau – Survivre

        +

        1er Niveau – Survivre

        1. Installez vim
        2. @@ -103,13 +106,13 @@ En fait ça sera certainement plus 2 semaines que 3 jours.

        3. NE TOUCHEZ A RIEN! Lisez
        -

        Dans un éditeur normal, il suffit de taper sur une touche du clavier et la lettre s’affiche à l’écran. +

        Dans un éditeur normal, il suffit de taper sur une touche du clavier et la lettre s’affiche à l’écran. Pas ici. Vim est en mode Normal. Commençons par placer vim en mode Insert. Tapez sur la touche i.

        -

        Voilà, c’est magique. +

        Voilà, c’est magique. Vous pouvez tapez comme dans un éditeur standard. Pour repasser en mode Normal tapez sur la touche Echap.

        @@ -128,30 +131,30 @@ Voici les commandes de survie (toutes en mode Normal) :

        Récommandées :

          -
        • hjkl (optionnel) → se déplacer (←↓↑→). Souvenez vous j ressemble à une flèche vers le bas.
        • -
        • :help <commande> → Affiche l’aide pour <commande>. Vous pouvez aussi écrire :help pour atterir sur l’aide générale.
        • +
        • hjkl (optionnel) → se déplacer (←↓↑→). Souvenez vous j ressemble à une flèche vers le bas.
        • +
        • :help <commande> → Affiche l’aide pour <commande>. Vous pouvez aussi écrire :help pour atterir sur l’aide générale.

      Seulement 5 commandes. -Voilà, c’est tout pour un début. -Essayez d’éditer vos fichiers comme ça pendant une petite journée. +Voilà, c’est tout pour un début. +Essayez d’éditer vos fichiers comme ça pendant une petite journée. Lorsque ces commandes vous sembleront naturelles, -vous pourrez passer à l’étape d’après.

      +vous pourrez passer à l’étape d’après.

      Mais avant un petit mot sur le mode Normal. Dans un éditeur normal pour copier il faut utiliser une combinaison de touches (Ctrl-c). -En fait, lorsque vous appuyez sur la touche Ctrl, c’est un peu comme si toutes les touches du clavier avaient un autre usage. -Dans vim, lorsque vous êtes en mode Normal, c’est comme si vous mainteniez Ctrl enfoncé.

      +En fait, lorsque vous appuyez sur la touche Ctrl, c’est un peu comme si toutes les touches du clavier avaient un autre usage. +Dans vim, lorsque vous êtes en mode Normal, c’est comme si vous mainteniez Ctrl enfoncé.

      Quelques mots concernant les notations :

        -
      • Au lieu d’écrire Ctrl-λ, j’écrirai <C-λ>.
      • -
      • Les commandes qui commencent par : ont un retour à la ligne implicite à la fin. Par exemple lorsque que j’écris, :q celà signifi qu’il faut taper :, suivi de q, suivi de <Return>.
      • +
      • Au lieu d’écrire Ctrl-λ, j’écrirai <C-λ>.
      • +
      • Les commandes qui commencent par : ont un retour à la ligne implicite à la fin. Par exemple lorsque que j’écris, :q celà signifi qu’il faut taper :, suivi de q, suivi de <Return>.
      -

      2ème Niveau – Se sentir à son aise

      +

      2ème Niveau – Se sentir à son aise

      Vous connaissez les commandes de survie. Passons à des commandes pour être un peu plus à notre aise. @@ -159,7 +162,7 @@ Je vous suggère :

      1. -

        Les variantes de l’insertion

        +

        Les variantes de l’insertion

          @@ -189,7 +192,7 @@ Je vous suggère :

          • P → Coller avant. Souvenez vous, p colle après la position du curseur.
          • -
          • yy → Copier la ligne courante. C’est plus simple et équivalent à ddP
          • +
          • yy → Copier la ligne courante. C’est plus simple et équivalent à ddP
          @@ -210,9 +213,9 @@ Je vous suggère :

          • :e <path/to/file> → Ouvrir.
          • :w → Sauvegarder.
          • -
          • :saveas <path/to/file> → Sauvegarder sous …
          • +
          • :saveas <path/to/file> → Sauvegarder sous …
          • :x, ZZ ou :wq → Sauvegarder et quitter (:x sauvegarde seulement si nécessaire).
          • -
          • :q! → Quitter sans sauvegarder. De même :qa! quitte même si d’autres fichiers (buffers) ont des modifications non sauvegardées.
          • +
          • :q! → Quitter sans sauvegarder. De même :qa! quitte même si d’autres fichiers (buffers) ont des modifications non sauvegardées.
          • :bn (resp. :bp) → Affiche le fichier suivant (resp. précédent).
        @@ -220,13 +223,13 @@ Je vous suggère :

      Prenez le temps de bien intégrer ces commandes. -Une fois fait, vous devriez être capable de faire tout ce qu’on peut attendre d’un éditeur de texte classique.

      +Une fois fait, vous devriez être capable de faire tout ce qu’on peut attendre d’un éditeur de texte classique.

      -

      3ième Niveau – Meilleur. Plus fort. Plus rapide.

      +

      3ième Niveau – Meilleur. Plus fort. Plus rapide.

      -

      Bravo ! Si vous êtes arrivé jusqu’ici nous allons pouvoir commencer à apprendre les choses vraiment intéressantes. +

      Bravo ! Si vous êtes arrivé jusqu’ici nous allons pouvoir commencer à apprendre les choses vraiment intéressantes. Pour cette section, je vais seulement parler de commandes disponible dans vi et vim. -Vim est la contraction de “vi improved”, ou en Français, “vi amélioré”.

      +Vim est la contraction de “vi improved”, ou en Français, “vi amélioré”.

      Meilleur

      @@ -242,10 +245,10 @@ Vim est la contraction de “vi improved”, ou en Français, “vi
      • 2dd → Supprimera 2 lignes
      • -
      • 3p → copiera 3 fois d’affiler le texte copié
      • -
      • 100idesu [ESC] → écrira “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “
      • -
      • . → Juste après la dernière commande réécrira les 100 “desu “.
      • -
      • 3. → Écrira 3 “desu” et non pas 300. Bien vu n’est-ce pas ?
      • +
      • 3p → copiera 3 fois d’affiler le texte copié
      • +
      • 100idesu [ESC] → écrira “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “
      • +
      • . → Juste après la dernière commande réécrira les 100 “desu “.
      • +
      • 3. → Écrira 3 “desu” et non pas 300. Bien vu n’est-ce pas ?
      @@ -269,7 +272,7 @@ Ne sautez pas cette section.

      Par défaut les mots sont seulement composés de lettres (et du caractère souligné _). Appelons un MOT un ensemble de lettre séparé par des caractères blancs (espaces, tabulation). -Si vous voulez considérer des MOTS alors il suffit d’utiliser les majuscules.

      +Si vous voulez considérer des MOTS alors il suffit d’utiliser les majuscules.

      1. W → aller au début du MOT suivant
      2. @@ -290,15 +293,15 @@ Si vous voulez considérer des MOTS alors il suffit d’utiliser les majuscu -

        Croyez moi, ces trois dernières commandes valent de l’or. +

        Croyez moi, ces trois dernières commandes valent de l’or. Retenez les et vous gagnerez beaucoup de temps.

        Plus rapide

        -

        Vous vous souvenez que j’ai dit que les déplacements étaient très importants en vi. +

        Vous vous souvenez que j’ai dit que les déplacements étaient très importants en vi. Voilà pourquoi. -Une façon de travailler avec vim est de se dire que l’on fait des “phrases”. -Le verbe étant la commande et les compléments définissent la zone d’action. +Une façon de travailler avec vim est de se dire que l’on fait des “phrases”. +Le verbe étant la commande et les compléments définissent la zone d’action. De façon générale :

        <position de depart><commande><position d'arrivee>

        @@ -307,21 +310,21 @@ De façon générale :

        • 0 → Aller au début de la ligne,
        • -
        • y → copie à partir d’ici,
        • -
        • $ → jusqu’à la fin de cette ligne.
        • +
        • y → copie à partir d’ici,
        • +
        • $ → jusqu’à la fin de cette ligne.
        -

        On peut donc faire des choses comme ye, copie à partir de la position courante du curseur jusqu’à là fin du mot. -Mais aussi: y2/toto copie jusqu’à la seconde prochaine occurrence de “toto”.

        +

        On peut donc faire des choses comme ye, copie à partir de la position courante du curseur jusqu’à là fin du mot. +Mais aussi: y2/toto copie jusqu’à la seconde prochaine occurrence de “toto”.

        Ce qui est vrai pour y (yank → copier), -est aussi vrai pour d (delete → supprimer), v (sélection visuelle), gU (uppercase → majuscule),gu (lowercase → minuscule), etc…

        +est aussi vrai pour d (delete → supprimer), v (sélection visuelle), gU (uppercase → majuscule),gu (lowercase → minuscule), etc…

        -

        4ième Niveau – Les super pouvoirs de Vim

        +

        4ième Niveau – Les super pouvoirs de Vim

        -

        Jusqu’ici vous avez appris les commandes les plus courantes. +

        Jusqu’ici vous avez appris les commandes les plus courantes. Mais voici les killer features de vim. -Celles que je n’ai retrouvé que dans vim (ou presque).

        +Celles que je n’ai retrouvé que dans vim (ou presque).

        Déplacement sur la ligne : 0 ^ $ g_ f F t T , ;

        @@ -331,7 +334,7 @@ Celles que je n’ai retrouvé que dans vim (ou presque).

      3. ^ → aller au premier caractère de la ligne
      4. $ → aller à la dernière colonne de la ligne
      5. g_ → aller au dernier caractère de la ligne
      6. -
      7. fa → vous amène à la prochaine occurrence de a sur la ligne courante. , (resp. ;) recherche l’occurrence suivante (resp. précédente).
      8. +
      9. fa → vous amène à la prochaine occurrence de a sur la ligne courante. , (resp. ;) recherche l’occurrence suivante (resp. précédente).
      10. t, → vous amène juste avant le ,.
      11. 3fa → recherche la 3ième occurrence de a.
      12. F et T → comme f et t mais en arrière. @@ -339,16 +342,16 @@ Celles que je n’ai retrouvé que dans vim (ou presque).

        -

        Un truc pratique : dt" → supprime tout jusqu’au prochain ".

        +

        Un truc pratique : dt" → supprime tout jusqu’au prochain ".

        Selection de zone <action>a<object> ou <action>i<object>

        -

        Ces commandes sont utilisable seulement en mode visuel ou après un “opérateur”. +

        Ces commandes sont utilisable seulement en mode visuel ou après un “opérateur”. Mais elles sont très puissantes. Leur forme générale est:

        <action>a<objet> et <action>i<objet>

        -

        Où action peut être par exemple d (delete), y (yank), v (select in visual mode), etc… +

        Où action peut être par exemple d (delete), y (yank), v (select in visual mode), etc… Un objet peut être: w un mot, W un MOT (mot étendu), s une phrase, p un paragraphe. Mais aussi des caractère plus naturels comme ", ', ), }, ].

        Supposons que le curseur soit positionné sur le premier o dans (map (+) ("foo")).

        @@ -374,23 +377,23 @@ Typiquement: ^<C-V><C-d>I-- [ESC]

        • ^ → aller en début de ligne
        • <C-V> → Commencer la sélection du bloc
        • -
        • <C-d> → se déplacer vers le bas (pourrait être jjj ou % etc…)
        • +
        • <C-d> → se déplacer vers le bas (pourrait être jjj ou % etc…)
        • I-- [ESC] → écrit -- pour commenter le reste de la ligne.

        Rectangular blocks

        -

        Remarquez que sous windows, vous devez utiliser <C-q> plutôt que <C-v> si votre “presse papier” n’est pas vide.

        +

        Remarquez que sous windows, vous devez utiliser <C-q> plutôt que <C-v> si votre “presse papier” n’est pas vide.

        Complétion : <C-n> et <C-p>.

        -

        En mode Insert, commencez à écrire le début d’un mot déjà présent dans l’un des buffers (fichers) ouvert et tapes <C-p>. Magique. +

        En mode Insert, commencez à écrire le début d’un mot déjà présent dans l’un des buffers (fichers) ouvert et tapes <C-p>. Magique. Completion

        Macros : qa faire quelque chose q, @a, @@

        qa enregistre tout ce que vous faite et enregistre le tout dans le registre a. -Ensuite @a va rejouer la macro enregistrée dans le registre a comme si c’est vous qui tapiez au clavier. +Ensuite @a va rejouer la macro enregistrée dans le registre a comme si c’est vous qui tapiez au clavier. @@ est un raccourci pour rejouer la dernière macro exécutée.

        @@ -402,14 +405,14 @@ Sur une ligne contenant seulement un 1 tapez :

        qaYp<C-a>q

          -
        • qa → début de l’enregistrement.
        • +
        • qa → début de l’enregistrement.
        • Yp → copier cette ligne.
        • <C-a> → incrémente le nombre.
        • -
        • q → arrête d’enregistrer.
        • +
        • q → arrête d’enregistrer.
      13. @a → écrit un 2 sous le 1.
      14. -
      15. Écrivez 100@@. Cela va créer une liste de nombre croissants jusqu’à 103.
      16. +
      17. Écrivez 100@@. Cela va créer une liste de nombre croissants jusqu’à 103.
      18. @@ -433,7 +436,7 @@ Et une fois la sélection visuelle faite vous pouvez par exemple:

        • <C-v>
        • -
        • aller jusqu’à la ligne désirée (jjj ou <C-d> ou /pattern ou % etc…)
        • +
        • aller jusqu’à la ligne désirée (jjj ou <C-d> ou /pattern ou % etc…)
        • $ aller à la fin
        • A, écrire le texte, Echap.
        @@ -449,7 +452,7 @@ Voici les commandes principales :

        • :split → crée un split (:vsplit crée un split vertical)
        • -
        • <C-w><dir> → où dir est l’un de hjkl ou ←↓↑→ permet de changer de split.
        • +
        • <C-w><dir> → où dir est l’un de hjkl ou ←↓↑→ permet de changer de split.
        • <C-w>_ (resp. <C-w>|) → Maximise la taille du split (resp. split vertical)
        • <C-w>+ (resp. <C-w>-) → Agrandi (resp. diminue) le split
        @@ -459,17 +462,17 @@ Voici les commandes principales :

        Conclusion

        -

        Voilà, je vous ai donné 90% des commandes que j’utilise tous les jours. -N’essayez pas de toutes les apprendre en une journée. -Il faut le temps de s’habituer à chaque nouvelle commande. -Je vous conseille de ne pas apprendre plus d’une ou deux commandes par jour.

        +

        Voilà, je vous ai donné 90% des commandes que j’utilise tous les jours. +N’essayez pas de toutes les apprendre en une journée. +Il faut le temps de s’habituer à chaque nouvelle commande. +Je vous conseille de ne pas apprendre plus d’une ou deux commandes par jour.

        -

        Apprendre Vim est plus une question d’entraînement que de mémorisation. +

        Apprendre Vim est plus une question d’entraînement que de mémorisation. Heureusement vim est founi avec un très bon tutoriel et une excellente documentation. -Lancez vimtutor jusqu’à ce que vous vous sentiez à l’aise avec les commandes basiques. +Lancez vimtutor jusqu’à ce que vous vous sentiez à l’aise avec les commandes basiques. De plus, vous devriez aussi lire en détail la page suivate : :help usr_02.txt.

        -

        Ensuite vous découvrirez !, les folds, les registres, les plugins et tout un tas d’autres choses. +

        Ensuite vous découvrirez !, les folds, les registres, les plugins et tout un tas d’autres choses. Apprenez vim comme vous apprendriez le piano et vous devriez très bien vous en sortir.

        + + @@ -60,19 +63,19 @@
        -

        tlàl : Une méthode de gestion des mots de passes que j’utilise avec succès depuis quelques années.
        +

        tlàl : Une méthode de gestion des mots de passes que j’utilise avec succès depuis quelques années.
        sha1( mot_de_passe + nom_de_domaine )
        -Je ne mémorise qu’un seul mot de passe de très bonne qualité. -J’utilise des mots de passe différents sur tous les sites.

        +Je ne mémorise qu’un seul mot de passe de très bonne qualité. +J’utilise des mots de passe différents sur tous les sites.

        -

        Avant de commencer, je tiens à préciser qu’il s’agit d’une tentative de vous vendre mon appli iPhone ;-).

        +

        Avant de commencer, je tiens à préciser qu’il s’agit d’une tentative de vous vendre mon appli iPhone ;-).

        Vous êtes toujours là ? -Bon, d’accord, même si vous ne téléchargez pas mon application vous pouvez quand même utiliser ma méthode. +Bon, d’accord, même si vous ne téléchargez pas mon application vous pouvez quand même utiliser ma méthode. Elle est à la fois très sûre et simple à utiliser.

        Si vous souhaitez simplement utiliser le système sans essayer de comprendre comment ça marche derrière vous pouvez aller à la fin de cet article en cliquant ici.

        @@ -83,13 +86,13 @@ Elle est à la fois très sûre et simple à utiliser.

        Même les paranoïaques peuvent avoir des ennemis.

        -

        Imaginez que vous trouviez un très bon mot de passe. Vous l’utilisez sur gmail, amazon, PayPal, twitter, facebook… +

        Imaginez que vous trouviez un très bon mot de passe. Vous l’utilisez sur gmail, amazon, PayPal, twitter, facebook… Plus tard, vous découvrez un super petit jeu en ligne très sympa. Vous devez vous enregistrer pour y jouer. Le site vous demande votre email et un mot de passe. Quelques semaines/mois se passent. La machine qui héberge le jeu en ligne se fait attaquer. -Maintenant, l’attaquant du site web possède votre email avec ce mot de passe. +Maintenant, l’attaquant du site web possède votre email avec ce mot de passe. Il peut donc essayer votre mot de passe un peu partout. Sur PayPal par exemple.

        @@ -102,7 +105,7 @@ Sur PayPal par exemple.

        La méthode la plus courante est de se souvenir de plusieurs mot de passes différents. -En général, si vous avez bonne mémoire vous pouvez mémoriser jusqu’à 13 mots de passes. Certain de bonne qualité, d’autre moins.

        +En général, si vous avez bonne mémoire vous pouvez mémoriser jusqu’à 13 mots de passes. Certain de bonne qualité, d’autre moins.

        Que faire si vous utilisez plus de services que vous pouvez mémoriser de mots de passe ?

        @@ -115,21 +118,21 @@ mots de passes de la façon suivante :

      19. badonlinegame: P45sW0r|)badonlinegame
      20. -

        Malheureusement, si quelqu’un récupère votre mot de passe sur +

        Malheureusement, si quelqu’un récupère votre mot de passe sur badonlinegame, il peut facilement retrouvez vos autres mots de passe. Bien sûr, on peut imaginer des transformation de mots de passe de meilleure qualité. -Mais il est très difficile d’en trouver une suffisamment bonne.

        +Mais il est très difficile d’en trouver une suffisamment bonne.

        Fort heureusement, il existe une des fonctions bien connues dans le milieu de la sécurité informatique et qui résolvent précisément ce problème. -Il s’agit des fontions de hachages. -Il est difficile de retrouver le paramètre d’entrée d’une fonction de hachage à partir de son résultat. +Il s’agit des fontions de hachages. +Il est difficile de retrouver le paramètre d’entrée d’une fonction de hachage à partir de son résultat. Prenons un exemple :

        -

        Si quelqu’un possède 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63, +

        Si quelqu’un possède 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63, il va avoir de grande difficulté pour retrouver P45sW0r|).

        Choisisson la fonction de hashage sha1. -Connaissant celà, le mot de passe d’un site donné doit avoir la forme :

        +Connaissant celà, le mot de passe d’un site donné doit avoir la forme :

        Où :

        @@ -140,9 +143,9 @@ Connaissant celà, le mot de passe d’un site donné doit avoir la forme&nb

        Il faut aussi penser à certaines contraintes. -Certains site web veulent des mots de passe d’une certaine longueur, ni trop longs ni trop courts. -Que faire si vous voulez changez votre mot de passe ? Soit parce qu’il est compromis ou simplement parce qu’on vous impose de le changer. -C’est pouquoi pour chaque site on a besoin de quelques paramètres supplémentaires.

        +Certains site web veulent des mots de passe d’une certaine longueur, ni trop longs ni trop courts. +Que faire si vous voulez changez votre mot de passe ? Soit parce qu’il est compromis ou simplement parce qu’on vous impose de le changer. +C’est pouquoi pour chaque site on a besoin de quelques paramètres supplémentaires.

        • le nom de login ;
        • @@ -153,31 +156,31 @@ C’est pouquoi pour chaque site on a besoin de quelques paramètres supplé

          En pratique ?

          -

          Selon ma situation, voici les outils que j’ai fait et que j’utilise :

          +

          Selon ma situation, voici les outils que j’ai fait et que j’utilise :

          • Sur mon Mac :
              -
            • J’utilise le widget YPassword
            • -
            • Parfois, certains champs de mots passe interdisent la copie. Dans ce cas, j’utilise un petit utilitaire en AppleScript : ForcePaste.
            • +
            • J’utilise le widget YPassword
            • +
            • Parfois, certains champs de mots passe interdisent la copie. Dans ce cas, j’utilise un petit utilitaire en AppleScript : ForcePaste.
          • -
          • Sous mon Linux : J’utilise le script ypassword
          • -
          • Sur mon iPhone : J’utilise l’application YPassword
          • +
          • Sous mon Linux : J’utilise le script ypassword
          • +
          • Sur mon iPhone : J’utilise l’application YPassword
          • Sur tous les autres ordinateurs :

          Quelquesoit mon environnement de travail, tous mes mots de passes sont à un copier/coller. -Pour certain services, j’utilise des mots de passe de 40 caractères. -Actuellement j’utilise plutôt des mots de passes de 10 caractères. +Pour certain services, j’utilise des mots de passe de 40 caractères. +Actuellement j’utilise plutôt des mots de passes de 10 caractères. Avec des mots de passes plus petit, il est encore plus difficile pour un attaquant de retrouver mon mot de passe principal.

          -

          Je serai heureux de savoir ce que vous pensez de cette méthode. Alors n’hésitez pas à laisser un commentaire ou à m’envoyer un mail.

          +

          Je serai heureux de savoir ce que vous pensez de cette méthode. Alors n’hésitez pas à laisser un commentaire ou à m’envoyer un mail.

          @@ -294,7 +297,7 @@ Avec des mots de passes plus petit, il est encore plus difficile pour un attaqua
          Écrit le : 18/05/2011 - modifié le : 26/10/2011 + modifié le : 26/04/2012
          Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/SVG-and-m4-fractals/code/yesodlogo.m4 b/output/Scratch/fr/blog/SVG-and-m4-fractals/code/yesodlogo.m4 index 02ee411ab..39ab18ce6 100644 --- a/output/Scratch/fr/blog/SVG-and-m4-fractals/code/yesodlogo.m4 +++ b/output/Scratch/fr/blog/SVG-and-m4-fractals/code/yesodlogo.m4 @@ -1,4 +1,3 @@ - @@ -64,13 +67,13 @@
          -

          tlàl : Utiliser m4 pour accroître le pouvoir d’xslt et d’svg. Example cool, les fractales.

          +

          tlàl : Utiliser m4 pour accroître le pouvoir d’xslt et d’svg. Example cool, les fractales.

          -

          Lorsqu’xml fût inventé beaucoup pensaient que c’était l’avenir. +

          Lorsqu’xml fût inventé beaucoup pensaient que c’était l’avenir. Passer de fichiers plat à des fichiers structurés standardisés fût un grand progrès dans beaucoup de domaines. Cerain se mirent à voir du xml de partout. À tel point que les les format compatibles xml naquirent de toute part. @@ -80,87 +83,82 @@ Non seulement comme format de fichier, mais aussi comme format pour un langage d

          Malheureusement, xml fût fabriquer pour le transfert de données. Pas du tout pour être vu ou édité directement. -La triste vérité est qu’xml est verbeux et laid. -Dans un monde parfait, nous ne devrions avoir des programmes qui s’occupent de nous afficher correctement le xml pour nous épargner la peine de les voir directement. +La triste vérité est qu’xml est verbeux et laid. +Dans un monde parfait, nous ne devrions avoir des programmes qui s’occupent de nous afficher correctement le xml pour nous épargner la peine de les voir directement. Mais devinez quoi ? -Notre monde n’est pas parfait. +Notre monde n’est pas parfait. Beaucoup de programmeurs sont ainsi forcé de travailler -directement avec de l’xml.

          +directement avec de l’xml.

          -

          xml, n’est pas le seul cas de format mal utilisé que je connaisse. -Vous avez d’autres formats dans lesquels il serait très agréable d’ajouter des variables, des boucles, des fonctions…

          +

          xml, n’est pas le seul cas de format mal utilisé que je connaisse. +Vous avez d’autres formats dans lesquels il serait très agréable d’ajouter des variables, des boucles, des fonctions…

          Mais je suis là pour vous aider. -Si comme moi vous détestez xslt ou écrire de l’xml. -Je vais vous montrer une façon d’améliorer tout ça.

          +Si comme moi vous détestez xslt ou écrire de l’xml. +Je vais vous montrer une façon d’améliorer tout ça.

          Un exemple avec xslt

          Commençons avec le pire cas de langage xml que je connaisse : xslt. Tous les développeurs qui ont déjà dû écrire du xslt savent à quel point ce langage est horrible.

          -

          Pour réduire la “verbosité” de tels langages, il y a un moyen. +

          Pour réduire la “verbosité” de tels langages, il y a un moyen. m4. Oui, le préprocesseur utilisé par C et C++.

          Voici certains exemples :

            -
          • Les variables, au lieu d’écrire myvar = value, voici la version xslt :
          • +
          • Les variables, au lieu d’écrire myvar = value, voici la version xslt :
          -
          -<xsl:variable name="myvar" select="value"/>
          -
          +
          <xsl:variable name="myvar" select="value"/>
          +
          • Afficher quelquechose. Au lieu de print "Hello world!", xslt nous offre :
          -
          -<xsl:text 
          -    disable-output-escaping="yes"><![CDATA[Hello world!
          -]]></xsl:text>
          -
          +
          <xsl:text 
          +    disable-output-escaping="yes"><![CDATA[Hello world!
          +]]></xsl:text>
          +
            -
          • afficher la valeur d’une variable, au lieu de print myvar, nous avons droit à :
          • +
          • afficher la valeur d’une variable, au lieu de print myvar, nous avons droit à :
          -
          -<xslt:value-of select="myvar"/>
          -
          +
          <xslt:value-of select="myvar"/>
          +
            -
          • Essayez d’imaginer à quel point il est verbeux de déclarer une fonction dans ce langage.
          • +
          • Essayez d’imaginer à quel point il est verbeux de déclarer une fonction dans ce langage.

          La solution (m4 à la rescousse)

          -
          -<?xml version="1.0" standalone="yes"?> <!-- YES its xml -->
          -<!-- ← start a comment, then write some m4 directives:
          -
          -define(`ydef',`<xsl:variable name="$1" select="$2"/>')
          -define(`yprint',`<xsl:text disable-output-escaping="yes"><![CDATA[$1]]></xsl:text>')
          -define(`yshow',`<xsl:value-of select="$1"/>')
          -
          --->
          -<!-- Yes, xml sucks to be read -->
          -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          -<!-- And it sucks even more to edit -->
          -<xsl:template match="/">
          +
          <?xml version="1.0" standalone="yes"?> <!-- YES its xml -->
          +<!-- ← start a comment, then write some m4 directives:
          +
          +define(`ydef',`<xsl:variable name="$1" select="$2"/>')
          +define(`yprint',`<xsl:text disable-output-escaping="yes"><![CDATA[$1]]></xsl:text>')
          +define(`yshow',`<xsl:value-of select="$1"/>')
          +
          +-->
          +<!-- Yes, xml sucks to be read -->
          +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          +<!-- And it sucks even more to edit -->
          +<xsl:template match="/">
               ydef(myvar,value)
               yprint(Hello world!)
               yshow(myvar)
          -</xsl:template>
          -
          +</xsl:template> +

          Maintenant compilons simplement ce fichier :

          -
          -m4 myfile.m4 > myfile.xslt
          -
          +
          m4 myfile.m4 > myfile.xslt
          +

          Et vous pouvez profitez ! Maintenant xslt devient plus lisible et plus facile à éditer.

          @@ -168,14 +166,14 @@ m4 myfile.m4 > myfile.xslt

          À ses débuts, beaucoup pensaient que ce serait le nouveau Flash. Apparemment, ce devrait plutôt être canvas avec du javascript qui occupera cette place.

          -

          Tout d’abord, laissez moi vous montrer le résultat :

          +

          Tout d’abord, laissez moi vous montrer le résultat :

          -

          +

          Yesod logo made in SVG and m4 -Cliquez sur l’image pour voir le svg directement. Attention, si vous n’avez pas un ordinateur récent, ça risque de ramer. +Cliquez sur l’image pour voir le svg directement. Attention, si vous n’avez pas un ordinateur récent, ça risque de ramer.

          -

          Le positionnement du texte “esod” par rapport au “λ” renversé a été en jouant avec firebug. De cette façon je n’avais pas à regénérer pour tester.

          +

          Le positionnement du texte “esod” par rapport au “λ” renversé a été en jouant avec firebug. De cette façon je n’avais pas à regénérer pour tester.

          Faire une telle fractale revient à :

          @@ -187,67 +185,65 @@ Cliquez sur l’image pour voir le svg directement.
        • Arréter lorsque la récursion est assez profonde.
      -

      Si j’avais dû faire ça manuellement, il m’aurait fallu faire beaucoup de copier/coller dans mon svg. -Simplement parce que la transformation est toujours la même, mais je ne pouvais pas dire, utiliser la transformation appelée “titi”. -Plutôt que copier du xml, j’ai utilisé m4.

      +

      Si j’avais dû faire ça manuellement, il m’aurait fallu faire beaucoup de copier/coller dans mon svg. +Simplement parce que la transformation est toujours la même, mais je ne pouvais pas dire, utiliser la transformation appelée “titi”. +Plutôt que copier du xml, j’ai utilisé m4.

      Et voici le code commenté :

      -
      -
      -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
      -<!--
      -     M4 Macros
      -define(`YTRANSFORMONE', `scale(.43) translate(-120,-69) rotate(-10)')
      -define(`YTRANSFORMTWO', `scale(.43) translate(-9,-67.5) rotate(10)')
      -define(`YTRANSFORMTHREE', `scale(.43) translate(53,41) rotate(120)')
      -define(`YGENTRANSFORM', `translate(364,274) scale(3)')
      -define(`YTRANSCOMPLETE', `
      -    <g id="level_$1">
      -        <use style="opacity: .8" transform="YTRANSFORMONE" xlink:href="#level_$2" />
      -        <use style="opacity: .8" transform="YTRANSFORMTWO" xlink:href="#level_$2" />
      -        <use style="opacity: .8" transform="YTRANSFORMTHREE" xlink:href="#level_$2" />
      -    </g>
      -    <use transform="YGENTRANSFORM" xlink:href="#level_$1" />
      -')
      - -->
      -<svg 
      -    xmlns="http://www.w3.org/2000/svg" 
      -    xmlns:xlink="http://www.w3.org/1999/xlink"
      -    x="64" y="64" width="512" height="512" viewBox="64 64 512 512"
      -    id="svg2" version="1.1">
      -    <g id="level_0"> <!-- some group, if I want to add other elements -->
      -        <!-- the text "λ" -->
      -        <text id="lambda" 
      -            fill="#333" style="font-family:Ubuntu; font-size: 100px"
      -            transform="rotate(180)">λ</text>
      -    </g>
      -    <!-- the text "esod" -->
      -    <text 
      -        fill="#333" 
      -        style="font-family:Ubuntu; font-size: 28px; letter-spacing: -0.10em" 
      -        x="-17.3" 
      -        y="69" 
      -        transform="YGENTRANSFORM">esod</text>
      -    <!-- ROOT ELEMENT -->
      -    <use transform="YGENTRANSFORM" xlink:href="#level_0" />
      +
       
      -    YTRANSCOMPLETE(1,0) <!-- First recursion -->
      -    YTRANSCOMPLETE(2,1) <!-- deeper -->
      -    YTRANSCOMPLETE(3,2) <!-- deeper -->
      -    YTRANSCOMPLETE(4,3) <!-- even deeper -->
      -    YTRANSCOMPLETE(5,4) <!-- Five level seems enough -->
      -</svg>
      -
      -
      +
      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      +<!--
      +     M4 Macros
      +define(`YTRANSFORMONE', `scale(.43) translate(-120,-69) rotate(-10)')
      +define(`YTRANSFORMTWO', `scale(.43) translate(-9,-67.5) rotate(10)')
      +define(`YTRANSFORMTHREE', `scale(.43) translate(53,41) rotate(120)')
      +define(`YGENTRANSFORM', `translate(364,274) scale(3)')
      +define(`YTRANSCOMPLETE', `
      +    <g id="level_$1">
      +        <use style="opacity: .8" transform="YTRANSFORMONE" xlink:href="#level_$2" />
      +        <use style="opacity: .8" transform="YTRANSFORMTWO" xlink:href="#level_$2" />
      +        <use style="opacity: .8" transform="YTRANSFORMTHREE" xlink:href="#level_$2" />
      +    </g>
      +    <use transform="YGENTRANSFORM" xlink:href="#level_$1" />
      +')
      + -->
      +<svg 
      +    xmlns="http://www.w3.org/2000/svg" 
      +    xmlns:xlink="http://www.w3.org/1999/xlink"
      +    x="64" y="64" width="512" height="512" viewBox="64 64 512 512"
      +    id="svg2" version="1.1">
      +    <g id="level_0"> <!-- some group, if I want to add other elements -->
      +        <!-- the text "λ" -->
      +        <text id="lambda" 
      +            fill="#333" style="font-family:Ubuntu; font-size: 100px"
      +            transform="rotate(180)">λ</text>
      +    </g>
      +    <!-- the text "esod" -->
      +    <text 
      +        fill="#333" 
      +        style="font-family:Ubuntu; font-size: 28px; letter-spacing: -0.10em" 
      +        x="-17.3" 
      +        y="69" 
      +        transform="YGENTRANSFORM">esod</text>
      +    <!-- ROOT ELEMENT -->
      +    <use transform="YGENTRANSFORM" xlink:href="#level_0" />
       
      -

      et je l’ai compile en svg et ensuite en png avec :

      + YTRANSCOMPLETE(1,0) <!-- First recursion --> + YTRANSCOMPLETE(2,1) <!-- deeper --> + YTRANSCOMPLETE(3,2) <!-- deeper --> + YTRANSCOMPLETE(4,3) <!-- even deeper --> + YTRANSCOMPLETE(5,4) <!-- Five level seems enough --> +</svg> +
      -
      -m4 yesodlogo.m4 > yesodlogo.svg && convert yesodlogo.svg yesodlogo.png
      -
      +

      et je l’ai compile en svg et ensuite en png avec :

      -

      Le λ est dupliqué avec trois “transformations” différentes. Les transformations sont : YTRANSFORMONE, YTRANSFORMTWO et YTRANSFORMTHREE.

      +
      m4 yesodlogo.m4 > yesodlogo.svg && convert yesodlogo.svg yesodlogo.png
      +
      + +

      Le λ est dupliqué avec trois “transformations” différentes. Les transformations sont : YTRANSFORMONE, YTRANSFORMTWO et YTRANSFORMTHREE.

      Chaque transformation est une similarité (translation + rotation + zoom, ce qui est équivalent à juste rotation + zoom, mais bon).

      @@ -257,22 +253,22 @@ m4 yesodlogo.m4 > yesodlogo.svg Conclusion -

      Ce fut très amusant de faire une fractale en svg, mais la partie la plus intéressante était d’augmenter la puissance d’expressivité du langage en utilise un préprocesseur. -J’ai utilisé cette méthode avec xslt pour une vrai application par exemple. -On peut aussi utiliser m4 pour faire des includes d’autres fichiers. -Typiquement je l’ai utiliser pour les includes dans un format obscur. +

      Ce fut très amusant de faire une fractale en svg, mais la partie la plus intéressante était d’augmenter la puissance d’expressivité du langage en utilise un préprocesseur. +J’ai utilisé cette méthode avec xslt pour une vrai application par exemple. +On peut aussi utiliser m4 pour faire des includes d’autres fichiers. +Typiquement je l’ai utiliser pour les includes dans un format obscur. Mais vous pouvez aussi le considérer pour des includes dans du HTML. Par exemple pour fabriquer un site statique rapidement, m4 peut se révéler utile pour inclure un footer ou un menu sur toutes les pages par exemple. -J’ai aussi pensé que l’on pouvait utiliser m4 pour structurer des programmes comme brainfuck.

      +J’ai aussi pensé que l’on pouvait utiliser m4 pour structurer des programmes comme brainfuck.

      @@ -389,7 +385,7 @@ J’ai aussi pensé que l’on pouvait utiliser m4 pour structurer des p
      Écrit le : 20/10/2011 - modifié le : 16/11/2011 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/Typography-and-the-Web/index.html b/output/Scratch/fr/blog/Typography-and-the-Web/index.html index 7f5ac26aa..60cc78b18 100644 --- a/output/Scratch/fr/blog/Typography-and-the-Web/index.html +++ b/output/Scratch/fr/blog/Typography-and-the-Web/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -54,7 +57,7 @@
      -

      Screenshot of first in small caps with and without ligatures.

      +

      Screenshot of first in small caps with and without ligatures.;

      @@ -76,28 +79,28 @@ We can all create better web typography ourselves, today.»

      ou en français :

      -

      «Nous ne somme pas obligé d’attendre le développement des navigateurs. -Nous pouvons créer un web avec une meilleure typographie aujourd’hui.»

      +

      «Nous ne somme pas obligé d’attendre le développement des navigateurs. +Nous pouvons créer un web avec une meilleure typographie aujourd’hui.»

      -

      Comme quelqu’un qui a déjà essayé d’améliorer la typographie de son site web, et en particulier des ligatures, je crois que c’est faux.

      +

      Comme quelqu’un qui a déjà essayé d’améliorer la typographie de son site web, et en particulier des ligatures, je crois que c’est faux.

      -

      J’ai déjà écrit un système automatique qui détecte et ajoute des ligatures en utilisant des caractères unicode. -Cependant je n’ai jamais publié cette amélioration sur le web et voilà pourquoi :

      +

      J’ai déjà écrit un système automatique qui détecte et ajoute des ligatures en utilisant des caractères unicode. +Cependant je n’ai jamais publié cette amélioration sur le web et voilà pourquoi :

      -

      Tout d’abord, qu’est-ce qu’un ligature ?

      +

      Tout d’abord, qu’est-ce qu’un ligature ?

      -

      A ligature example

      +

      A ligature example;

      Quel est le problème des ligatures sur le web ? -Le premier c’est que vous ne pouvez pas chercher les mots qui contiennent ces ligatures. Par exemple essayez de chercher le mot “first”.

      +Le premier c’est que vous ne pouvez pas chercher les mots qui contiennent ces ligatures. Par exemple essayez de chercher le mot “first”.

      • first ← Pas de ligature, pas de problème.
      • r ← Une jolie ligature, mais introuvable avec une recherche (C-f).
      -

      Le second problème est le rendu. Par exemple, essayer d’utiliser un charactère de ligature en petites capitales :

      +

      Le second problème est le rendu. Par exemple, essayer d’utiliser un charactère de ligature en petites capitales :

      • @@ -108,47 +111,46 @@ Le premier c’est que vous ne pouvez pas chercher les mots qui contiennent
      -

      Voici une capture d’écran pour que vous voyez ce que je vois :

      +

      Voici une capture d’écran pour que vous voyez ce que je vois :

      -

      Screenshot of first in small caps with and without ligatures.

      +

      Screenshot of first in small caps with and without ligatures.;

      -

      Le navigateur est incapable de comprendre que le caractère de ligature “” doit être rendu comme fi lorsqu’il est en petites capitales. -Et une part du problème est que l’on peut décider d’écrire en petite majuscule dans le css.

      +

      Le navigateur est incapable de comprendre que le caractère de ligature “” doit être rendu comme fi lorsqu’il est en petites capitales. +Et une part du problème est que l’on peut décider d’écrire en petite majuscule dans le css.

      Comment par exemple utiliser un charactère de ligature unicode sur un site qui possède différents rendus via différentes css ?

      Comparons à LaTeX

      -

      LaTeX screenshot

      +

      LaTeX screenshot;

      -

      Si vous faites attention au détail, vous constaterez que le premier “first” contient une ligature. Bien entendu la deuxième ligne est affichée correctement. Le code que j’ai utilisé pour avoir ce rendu est simplement :

      +

      Si vous faites attention au détail, vous constaterez que le premier “first” contient une ligature. Bien entendu la deuxième ligne est affichée correctement. Le code que j’ai utilisé pour avoir ce rendu est simplement :

      -
      -\item first
      -\item {\sc first}
      -
      +
      \item first
      +\item {\sc first}
      +

      LaTeX a été suffisamment intelligent pour créer les ligatures si nécessaire.

      -

      La ligature “” est rare et n’est pas rendu par défaut par LaTeX. -Si vous voulez voir des ligatures rares, vous pouvez utiliser XƎLaTeX:

      +

      La ligature “” est rare et n’est pas rendu par défaut par LaTeX. +Si vous voulez voir des ligatures rares, vous pouvez utiliser XƎLaTeX:

      -

      XeLaTeX ligatures

      +

      XeLaTeX ligatures;

      -

      J’ai copié cette image de l’excellent article de Dario Taraborelli.

      +

      J’ai copié cette image de l’excellent article de Dario Taraborelli.

      Clairement il sera difficile aux navigateurs de corriger ces problèmes. Imaginez le nombre de petites exceptions.

      • Le texte est en petites capitales, je ne dois pas utiliser de ligatures.
      • -
      • Le mot courant contient un caractère de ligature, je ne dois pas chercher d’autre ligature dans ce mot.
      • -
      • La fonte n’a pas défini de caractère unicode pour la ligature, je ne dois pas l’utiliser.
      • +
      • Le mot courant contient un caractère de ligature, je ne dois pas chercher d’autre ligature dans ce mot.
      • +
      • La fonte n’a pas défini de caractère unicode pour la ligature, je ne dois pas l’utiliser.
      • Une commande javascript a modifé le CSS, je dois vérifier si je dois remplacer les ligatures par les deux caractères.
      • -
      • etc…
      • +
      • etc…
      -

      Dans tous les cas, si quelqu’un possède une solution je suis preneur !

      +

      Dans tous les cas, si quelqu’un possède une solution je suis preneur !

      @@ -257,7 +259,7 @@ Imaginez le nombre de petites exceptions.

      Écrit le : 02/02/2012 - modifié le : 02/02/2012 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/Yesod-excellent-ideas/index.html b/output/Scratch/fr/blog/Yesod-excellent-ideas/index.html index 4461d32ed..e147ce6f3 100644 --- a/output/Scratch/fr/blog/Yesod-excellent-ideas/index.html +++ b/output/Scratch/fr/blog/Yesod-excellent-ideas/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -63,8 +66,8 @@

      tlàl :

      Cela fait un moment que je suis la progression du framework yesod. -À mon humble avis on peut commencer à l’utiliser pour des applications sérieuses (comprendre en prod). -Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous parler de bonnes idées (parmi d’autres) introduites par yesod que je n’avais jamais vu ailleurs.

      +À mon humble avis on peut commencer à l’utiliser pour des applications sérieuses (comprendre en prod). +Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous parler de bonnes idées (parmi d’autres) introduites par yesod que je n’avais jamais vu ailleurs.

      @@ -72,61 +75,59 @@ Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous

      Types saufs

      -

      Commençons par une BD d’xkcd :

      +

      Commençons par une BD d’xkcd :

      SQL injection by a mom

      -

      Lorsque vous créez une application web, beaucoup de temps est passé à s’occuper de chaînes de caractères. -Des chaînes de caractère pour les URL, le HTML, le Javascript, les CSS, les requêtes SQL, etc… +

      Lorsque vous créez une application web, beaucoup de temps est passé à s’occuper de chaînes de caractères. +Des chaînes de caractère pour les URL, le HTML, le Javascript, les CSS, les requêtes SQL, etc… Pour éviter des utilisation malicieuses vous devez protéger chaque chaîne de caractère entre chaque étape. Par exemple supposons que vous entriez comme nom :

      -
      -Newton<script>alert("An apple fall")</script>
      -
      +
      Newton<script>alert("An apple fall")</script>
      +
      -

      Sans une protection correcte, le message “An apple fall” sera affiché à chaque fois que quelqu’un essayera d’accéder au nom de cet utilisateur. -Les “types saufs” sont le tonyglandil du web. -A chaque chaine de caractère, on lui associe un “type”. -A quoi sert cette chaîne de caractère ? Est-ce une URL ? Du javascript ? De l’HTML ? -Entre chaque passage d’une représentation à une autre, un transformation is faite par défaut.

      +

      Sans une protection correcte, le message “An apple fall” sera affiché à chaque fois que quelqu’un essayera d’accéder au nom de cet utilisateur. +Les “types saufs” sont le tonyglandil du web. +A chaque chaine de caractère, on lui associe un “type”. +A quoi sert cette chaîne de caractère ? Est-ce une URL ? Du javascript ? De l’HTML ? +Entre chaque passage d’une représentation à une autre, un transformation is faite par défaut.

      -

      Yesod fait de son mieux pour typer les objets manipulés et ainsi il fera ce qu’il faut pour ne pas mettre du script dans une URL par exemple.

      +

      Yesod fait de son mieux pour typer les objets manipulés et ainsi il fera ce qu’il faut pour ne pas mettre du script dans une URL par exemple.

      -
      <a href=@[AnotherPageR]>Go to the other page
      -
      +
      <a href=@[AnotherPageR]>Go to the other page
      +

      Comme AnotherPageR est une URL elle ne pourra contiendra pas (par défaut) de caractère dangereux comme par exemple :

      -
      -falselink"><script> bad_code(); </script><a href="pipo
      -
      +
      falselink"><script> bad_code(); </script><a href="pipo
      +

      Les widgets

      Les widgets de yesod sont différents des widgets Javascripts (ou java). -Pour yesod un widget est un ensemble de morceaux d’appli web. -Et si dans une page on veut utiliser plusieurs widgets, alors yesod s’occupe de tout. +Pour yesod un widget est un ensemble de morceaux d’appli web. +Et si dans une page on veut utiliser plusieurs widgets, alors yesod s’occupe de tout. Des exemples de widgets (au sens yesod) sont :

        -
      • Le «footer» d’une page web,
      • -
      • Le «header» d’une page web,
      • -
      • un bouton qui apparaît lorsque l’on «scrolle» vers le bas,
      • -
      • etc…
      • +
      • Le «footer» d’une page web,
      • +
      • Le «header» d’une page web,
      • +
      • un bouton qui apparaît lorsque l’on «scrolle» vers le bas,
      • +
      • etc…
      -

      Pour chacun de ces widgets vous pourriez avoir besoin d’

      +

      Pour chacun de ces widgets vous pourriez avoir besoin d’

        -
      • un peu d’HTML,
      • +
      • un peu d’HTML,
      • un peu de CSS et
      • un peu de javascript.
      -

      Certain morceau doivent être placés dans le «header» de la page et d’autre dans le «body».

      +

      Certain morceau doivent être placés dans le «header» de la page et d’autre dans le «body».

      -

      Vous pouvez déclarer un widget comme suit (je n’utilise pas la vrai syntaxe) :

      +

      Vous pouvez déclarer un widget comme suit (je n’utilise pas la vrai syntaxe) :

      htmlheader = ...
       cssheader = ...
      @@ -136,70 +137,66 @@ htmlbody = ...
       
       

      La vraie syntaxe est :

      -
      -toWidgetHeader cassiusFile "button.cassius"
      -toWidgetHeader juliusFile "button.julius"
      -toWidget       hamletFile "buttonTemplate.hamlet"
      -
      +
      toWidgetHeader cassiusFile "button.cassius"
      +toWidgetHeader juliusFile "button.julius"
      +toWidget       hamletFile "buttonTemplate.hamlet"
      +

      Veuillez aussi noté la convention Shakespearienne des noms. -Encore une bonne raison d’utiliser yesod.

      +Encore une bonne raison d’utiliser yesod.

      • Cassius & Lucius pour le CSS (très similaire à SASS et SCSS)
      • -
      • Julius pour le javascript (notons qu’il existe aussi un CoffeeScript qui traîne dans les sources de yesod)
      • -
      • Hamlet pour l’HTML (similaire à haml)
      • +
      • Julius pour le javascript (notons qu’il existe aussi un CoffeeScript qui traîne dans les sources de yesod)
      • +
      • Hamlet pour l’HTML (similaire à haml)

      Lorsque vous générez votre page, yesod se débrouille pour que tout fonctionne ensemble:

      -
      -myBigWidget =  menuWidget >> contentWidget >> footerWidget
      -
      +
      myBigWidget =  menuWidget >> contentWidget >> footerWidget
      +

      De plus, si vous utilisez 10 widgets avec un peu de CSS, yesod fabriquera un unique fichier CSS pour vous. Bien entendu si vous préférez avoir une dizaine de fichier CSS vous pouvez aussi le faire.

      -

      C’est juste génial !

      +

      C’est juste génial !

      Routage optimisé

      Dans un système de routage standard (à la ruby on rails par exemple) vous avez pour chaque entrée un couple: regexp → handler

      -

      La seule façon de découvrir la bonne règle est d’essayer de matcher l’url demandée à chaque expression régulière.

      +

      La seule façon de découvrir la bonne règle est d’essayer de matcher l’url demandée à chaque expression régulière.

      -

      Au lieu d’essayer chaque expression régulière, yesod regroupe et compile les routes pour les optimiser. +

      Au lieu d’essayer chaque expression régulière, yesod regroupe et compile les routes pour les optimiser. Bien entendu pour pouvoir profiter de cet avantage au mieux, il ne faut pas que deux routes interfèrent entres elles.

      -
      -/blog/2003  Date2003R
      +
      /blog/2003  Date2003R
       /blog/$DATE DateR
      -
      +

      Cette définition de route est invalide par défaut dans yesod. Si vous voulez vraiment vous pouvez le faire foncionner quand même, mais il me semble que ça doit être quasiment toujours une mauvaise idée.

      Il vaut mieux faire :

      -
      -/blog/$DATE DateR
      -
      +
      /blog/$DATE DateR
      +
      -

      et faire le test “est-ce que date = 2003 ?” dans le «handler».

      +

      et faire le test “est-ce que date = 2003 ?” dans le «handler».

      Pourquoi yesod?

        -
      1. La vitesse. Simplement incroyable, je ne pense pas qu’il existe quelque chose de plus rapide aujourd’hui. Regardez d’abord cet article puis celui-ci.
      2. -
      3. Haskell. C’est certainement le langage de programmation le plus difficile à apprendre que j’ai jamais rencontré. Mais aussi l’un des plus incroyables. Si vous voulez rencontrer tout un tas de notions que vous n’avez jamais croisées avant et faire exploser votre cerveau avec de nouvelles idées, alors apprenez Haskell.
      4. -
      5. Bonnes idées et communauté excellente. Cela fait quelques mois que je suis la progression de yesod. Et la vitesse à laquelle tout s’est déroulé est simplement incroyable. De plus les développeurs sont intelligents et super sympa.
      6. +
      7. La vitesse. Simplement incroyable, je ne pense pas qu’il existe quelque chose de plus rapide aujourd’hui. Regardez d’abord cet article puis celui-ci.
      8. +
      9. Haskell. C’est certainement le langage de programmation le plus difficile à apprendre que j’ai jamais rencontré. Mais aussi l’un des plus incroyables. Si vous voulez rencontrer tout un tas de notions que vous n’avez jamais croisées avant et faire exploser votre cerveau avec de nouvelles idées, alors apprenez Haskell.
      10. +
      11. Bonnes idées et communauté excellente. Cela fait quelques mois que je suis la progression de yesod. Et la vitesse à laquelle tout s’est déroulé est simplement incroyable. De plus les développeurs sont intelligents et super sympa.
      -

      Si vous êtes un “haskeller”, je pense que vous ne devriez pas avoir peur de la syntaxe particulière imposée par la façon standard de faire les choses avec yesod. +

      Si vous êtes un “haskeller”, je pense que vous ne devriez pas avoir peur de la syntaxe particulière imposée par la façon standard de faire les choses avec yesod. Il faut essayer un peu plus loin que les premiers tutoriaux du livre.

      -

      Je pense que yesod va dans la bonne direction d’un web plus sûr et plus rapide. Même si je pense que l’avenir sera que les serveurs devront être limités à faire serveur d’API (JSON ou XML ou n’importe quel autre mode de représentation d’objets).

      +

      Je pense que yesod va dans la bonne direction d’un web plus sûr et plus rapide. Même si je pense que l’avenir sera que les serveurs devront être limités à faire serveur d’API (JSON ou XML ou n’importe quel autre mode de représentation d’objets).

      -

      Yesod est juste incroyable. Dépassez les difficultés liées à l’apprentissage d’haskell et essayez le !

      +

      Yesod est juste incroyable. Dépassez les difficultés liées à l’apprentissage d’haskell et essayez le !

      @@ -316,7 +313,7 @@ Il faut essayer un peu plus loin que les premiers tutoriaux du livre.

      Écrit le : 04/10/2011 - modifié le : 26/10/2011 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/.gitignore b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/.gitignore index 579b41811..813f097ef 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/.gitignore +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/.gitignore @@ -1,4 +1,3 @@ - cabal-dev dist .static-cache diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Echo.hs b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Echo.hs index 650f2efd7..4ba6b6040 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Echo.hs +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Echo.hs @@ -1,4 +1,3 @@ - module Handler.Echo where import Import diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Mirror.hs b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Mirror.hs index 130816d6c..c7a006ad2 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Mirror.hs +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Mirror.hs @@ -1,4 +1,3 @@ - module Handler.Mirror where import Import diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/article.hamlet b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/article.hamlet index 354638ec7..6c32d426c 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/article.hamlet +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/article.hamlet @@ -1,3 +1,2 @@ -

      #{articleTitle article}
      #{articleContent article} diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/articles.hamlet b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/articles.hamlet index 71d6d1cb0..9ef45ac2c 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/articles.hamlet +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/articles.hamlet @@ -1,4 +1,3 @@ -

      Articles $if null articles -- Show a standard message if there is no article diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius index 83f57106e..b576178f7 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius @@ -1,4 +1,3 @@ - body { font-family: Helvetica, sans-serif; font-size: 18px; } diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/echo.hamlet b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/echo.hamlet index 22a4b5e2a..38745cf3b 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/echo.hamlet +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/echo.hamlet @@ -1,2 +1 @@ -

      #{theText} diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet index 233632918..c81d17042 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet @@ -1,4 +1,3 @@ -

      Enter your text diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/posted.hamlet b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/posted.hamlet index 0ee9df855..bf5ca7a62 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/posted.hamlet +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/posted.hamlet @@ -1,4 +1,3 @@ -

      You've just posted

      #{postedText}#{T.reverse postedText}


      diff --git a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/index.html b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/index.html index 274781c99..3d1189f38 100644 --- a/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/index.html +++ b/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -69,7 +72,7 @@

      tlàl : Un tutoriel pour yesod, un framework web Haskell. Vous ne devriez pas avoir besoin de savoir programmer en Haskell. Par contre je suis désolé pour les francophones, -mais je n’ai pas eu le courage de traduire cet article en Français.

      +mais je n’ai pas eu le courage de traduire cet article en Français.

      Table of content
      @@ -117,18 +120,18 @@ than C, C++ or Java for example. One of the best property of Haskell being:

      -

      “If your program compile it will be - very close to what the programmer intended”.

      +

      “If your program compile it will be + very close to what the programmer intended”.

      Haskell web frameworks handle parallel tasks perfectly. For example even better than node.js3.

      -

      Thousands of Agent Smith

      +

      Thousands of Agent Smith

      From the pure technical point of view, Haskell seems to be the perfect web development tool. -Weaknesses of Haskell certainly won’t be technical:

      +Weaknesses of Haskell certainly won’t be technical:

      • Hard to grasp Haskell
      • @@ -137,7 +140,7 @@ Weaknesses of Haskell certainly won’t be technical:

      • There is no heroku for Haskell (even if Greg Weber did it, it was more a workaround).
      -

      I won’t say these are not important drawbacks. +

      I won’t say these are not important drawbacks. But, with Haskell your web application will have both properties to absorb an impressive number of parallel request securely and to adapt to change.

      @@ -150,7 +153,7 @@ and to adapt to change.

    • Yesod
    • -

      I don’t think there is a real winner between these three framework. +

      I don’t think there is a real winner between these three framework. The choice I made for yesod is highly subjective. I just lurked a bit and tried some tutorials. I had the feeling yesod make a better job at helping newcomers. @@ -162,18 +165,18 @@ Of course I might be wrong since it is a matter of feeling.

      Why did I write this article? The yesod documentation and particularly the book are excellent. But I missed an intermediate tutorial. -This tutorial won’t explain all details. +This tutorial won’t explain all details. I tried to give a step by step of how to start from a five minute tutorial to an almost production ready architecture. Furthermore explaining something to others is a great way to learn. -If you are used to Haskell and Yesod, this tutorial won’t learn you much. +If you are used to Haskell and Yesod, this tutorial won’t learn you much. If you are completely new to Haskell and Yesod it might hopefully helps you. Also if you find yourself too confused by the syntax, it might helps to read this article

      -

      During this tutorial you’ll install, initialize and configure your first yesod project. +

      During this tutorial you’ll install, initialize and configure your first yesod project. Then there is a very minimal 5 minutes yesod tutorial to heat up and verify the awesomeness of yesod. -Then we will clean up the 5 minutes tutorial to use some “best practices”. +Then we will clean up the 5 minutes tutorial to use some “best practices”. Finally there will be a more standard real world example; a minimal blog system.

      Before the real start

      @@ -186,10 +189,9 @@ is to download the Haskell PlatformOnce done, you need to install yesod. Open a terminal session and do:

      -
      -~ cabal update
      -~ cabal install yesod cabal-dev
      -
      +
      ~ cabal update
      +~ cabal install yesod cabal-dev
      +

      There are few steps but it should take some time to finish.

      @@ -198,18 +200,16 @@ Open a terminal session and do:

      You are now ready to initialize your first yesod project. Open a terminal and type:

      -
      -~ yesod init
      -
      +
      ~ yesod init
      +

      Enter your name, choose yosog for the project name and enter Yosog for the name of the Foundation. Finally choose sqlite. Now, start the development cycle:

      -
      -~ cd yosog
      -~ cabal-dev install && yesod --dev devel
      -
      +
      ~ cd yosog
      +~ cabal-dev install && yesod --dev devel
      +

      This will compile the entire project. Be patient it could take a while the first time. Once finished a server is launched and you could visit it by clicking this link:

      @@ -222,9 +222,12 @@ Once finished a server is launched and you could visit it by clicking this link: Note: if something is messed up use the following command line inside the project directory. -
      -\rm -rf dist/* ; cabal-dev install && yesod --dev devel
      -
      + + +
      \rm -rf dist/* ; cabal-dev install && yesod --dev devel
      +
      + +
      @@ -240,23 +243,21 @@ but it is a good practice.

      Copy this .gitignore file into the yosog folder.

      -
      -
      -cabal-dev
      +
      +
      +
      cabal-dev
       dist
       .static-cache
       static/tmp
       *.sqlite3
      -
      -
      +

      Then initialize your git repository:

      -
      -~ git init .
      -~ git add .
      -~ git commit -a -m "Initial yesod commit"
      -
      +
      ~ git init .
      +~ git add .
      +~ git commit -a -m "Initial yesod commit"
      +

      We are almost ready to start.

      @@ -267,7 +268,7 @@ and a local web server listening the port 3000. If we modify a file inside this directory, yesod should try to recompile as fast as possible the site. Instead of explaining the role of every file, -let’s focus only on the important files/directories for this tutorial:

      +let’s focus only on the important files/directories for this tutorial:

      1. config/routes
      2. @@ -282,7 +283,7 @@ let’s focus only on the important files/directories for this tutorial:

      - + @@ -294,13 +295,13 @@ let’s focus only on the important files/directories for this tutorial:

      - +
      The written typeThe written type Its meaning
      config/routesis where you’ll configure the map url → Code.is where you’ll configure the map url → Code.
      Handler/
      config/modelsis where you’ll configure the persistent objects (database tables).is where you’ll configure the persistent objects (database tables).
      -

      During this tutorial we’ll modify other files as well, -but we won’t explore them in detail.

      +

      During this tutorial we’ll modify other files as well, +but we won’t explore them in detail.

      Also note, shell commands are executed in the root directory of your project instead specified otherwise.

      @@ -309,16 +310,16 @@ but we won’t explore them in detail.

      Echo

      To verify the quality of the security of the yesod framework, -let’s make a minimal echo application.

      +let’s make a minimal echo application.

      Goal:

      -

      Make a server that when accessed /echo/[some text] should return a web page containing “some text” inside an h1 bloc.

      +

      Make a server that when accessed /echo/[some text] should return a web page containing “some text” inside an h1 bloc.

      In a first time, we must declare the url of the form /echo/... are meaningful. -Let’s take a look at the file config/routes:

      +Let’s take a look at the file config/routes:

       /static StaticR Static getStatic
      @@ -346,23 +347,22 @@ I am not particularly fan of the big R notation but this is the standard convent
       Application.hs:31:1: Not in scope: `getEchoR'
       
      -

      Why? Simply because we didn’t written the code for the handler EchoR. +

      Why? Simply because we didn’t written the code for the handler EchoR. Edit the file Handler/Home.hs and append this:

      -
      -getEchoR :: String -> Handler RepHtml
      -getEchoR theText = do
      -    defaultLayout $ do
      +
      getEchoR :: String -> Handler RepHtml
      +getEchoR theText = do
      +    defaultLayout $ do
               [whamlet|<h1>#{theText}|]
      -
      +
      -

      Don’t worry if you find all of this a bit cryptic. +

      Don’t worry if you find all of this a bit cryptic. In short it just declare a function named getEchoR with one argument (theText) of type String. When this function is called, it return a Handler RepHtml whatever it is. But mainly this will encapsulate our expected result inside an html text.

      After saving the file, you should see yesod recompile the application. -When the compilation is finished you’ll see the message: Starting devel application.

      +When the compilation is finished you’ll see the message: Starting devel application.

      Now you can visit: http://localhost:3000/echo/Yesod%20rocks!

      @@ -384,18 +384,17 @@ A malicious user could not hide some bad script inside.

      The url string is put inside a url type. Then the interesting part in the url is put inside a String type. To pass from url type to String type some transformation are made. -For example, replace all “%20” by space characters. +For example, replace all “%20” by space characters. Then to show the String inside an html document, the string is put inside an html type. -Some transformations occurs like replace “<” by “&lt;”. +Some transformations occurs like replace “<” by “&lt;”. Thanks to yesod, this tedious job is done for us.

      -
      -"http://localhost:3000/echo/some%20text<a>" :: URL
      +
      "http://localhost:3000/echo/some%20text<a>" :: URL
                           ↓
      -              "some text<a>"                :: String
      +              "some text<a>"                :: String
                           ↓
      -          "some text &lt;a&gt;"             :: Html 
      -
      + "some text <a>" :: Html +

      Yesod is not only fast, it helps us to remain secure. It protects us from many common errors in other paradigms. @@ -410,42 +409,41 @@ We will clean up many details:

    • Use a general css (cleaner than the empty by default)
    • Dispatch handler code into different files
    • Use Data.Text instead of String
    • -
    • Put our “views”4 inside the template directory
    • +
    • Put our “views”4 inside the template directory

    Use a better css

    It is nice to note, the default template is based on html5 boilerplate. -Let’s change the default css. +Let’s change the default css. Add a file named default-layout.lucius inside the templates/ directory containing:

    -
    -
    -body {
    -    font-family: Helvetica, sans-serif; 
    -    font-size: 18px; }
    -#main {
    -    padding: 1em;
    -    border: #CCC solid 2px;
    -    border-radius: 5px;
    -    margin: 1em;
    -    width: 37em;
    -    margin: 1em auto;
    -    background: #F2F2F2;
    -    line-height: 1.5em;
    -    color: #333; }
    -.required { margin: 1em 0; }
    -.optional { margin: 1em 0; }
    -label { width: 8em; display: inline-block; }
    -input, textarea { background: #FAFAFA}
    -textarea { width: 27em; height: 9em;}
    -ul { list-style: square; }
    -a { color: #A56; }
    -a:hover { color: #C58; }
    -a:active { color: #C58; }
    -a:visited { color: #943; }
    -
    -
    + + +
    body {
    +    font-family: Helvetica, sans-serif; 
    +    font-size: 18px; }
    +#main {
    +    padding: 1em;
    +    border: #CCC solid 2px;
    +    border-radius: 5px;
    +    margin: 1em;
    +    width: 37em;
    +    margin: 1em auto;
    +    background: #F2F2F2;
    +    line-height: 1.5em;
    +    color: #333; }
    +.required { margin: 1em 0; }
    +.optional { margin: 1em 0; }
    +label { width: 8em; display: inline-block; }
    +input, textarea { background: #FAFAFA}
    +textarea { width: 27em; height: 9em;}
    +ul { list-style: square; }
    +a { color: #A56; }
    +a:hover { color: #C58; }
    +a:active { color: #C58; }
    +a:visited { color: #943; }
    +

    Personally I would prefer if such a minimal css was put with the scaffolding tool. I am sure somebody already made such a minimal css which give the impression @@ -454,20 +452,19 @@ But I digress.

    Separate Handlers

    -

    Generally you don’t want to have all your code inside a unique file. +

    Generally you don’t want to have all your code inside a unique file. This is why we will separate our handlers. In a first time create a new file Handler/Echo.hs containing:

    -
    -module Handler.Echo where
    +
    module Handler.Echo where
     
    -import Import
    +import Import
     
    -getEchoR :: String -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    +getEchoR :: String -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
             [whamlet|<h1>#{theText}|]
    -
    +

    Do not forget to remove the getEchoR function inside Handler/Home.hs.

    @@ -479,11 +476,10 @@ Just after Handler.Home, add:

    We must also declare this new Handler module inside Application.hs. -Just after the “import Handler.Home”, add:

    +Just after the “import Handler.Home”, add:

    -
    -import Handler.Echo
    -
    +
    import Handler.Echo
    +

    This is it.

    @@ -496,9 +492,8 @@ Just after the “import Handler.Home”, add:

    To declare it, add this import directive to Foundation.hs (just after the last one):

    -
    -import Data.Text
    -
    +
    import Data.Text
    +

    We have to modify config/routes and our handler accordingly. Replace #String by #Text in config/routes:

    @@ -509,18 +504,17 @@ Replace #String by #Text in config/routes

    And do the same in Handler/Echo.hs:

    -
    -
    -module Handler.Echo where
    +
     
    -import Import
    +
    module Handler.Echo where
     
    -getEchoR :: Text -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    +import Import
    +
    +getEchoR :: Text -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
             [whamlet|<h1>#{theText}|]
    -
    -
    +

    Use templates

    @@ -528,20 +522,18 @@ getEchoR theText = do We should put this part inside another file. Create the new file templates/echo.hamlet containing:

    -
    -
    -<h1> #{theText}
    -
    -
    + + +
    <h1> #{theText}
    +

    and modify the handler Handler/Echo.hs:

    -
    -getEchoR :: Text -> Handler RepHtml
    -getEchoR theText = do
    -    defaultLayout $ do
    -        $(widgetFile "echo")
    -
    +
    getEchoR :: Text -> Handler RepHtml
    +getEchoR theText = do
    +    defaultLayout $ do
    +        $(widgetFile "echo")
    +

    At this point, our web application is structured between different files. Handler are grouped, we use Data.Text and our views are in templates. @@ -549,13 +541,13 @@ It is the time to make a slightly more complex example.

    Mirror

    -

    Neo touching a mirror

    +

    Neo touching a mirror

    -

    Let’s make another minimal application. +

    Let’s make another minimal application. You should see a form containing a text field and a validation button. -When you enter some text (for example “Jormungad”) and validate, +When you enter some text (for example “Jormungad”) and validate, the next page present you the content and its reverse appended to it. -In our example it should return “JormungaddagnumroJ”.

    +In our example it should return “JormungaddagnumroJ”.

    First, add a new route:

    @@ -566,59 +558,56 @@ In our example it should return “JormungaddagnumroJ”.

    This time the path /mirror will accept GET and POST requests. Add the corresponding new Handler file:

    -
    -
    -module Handler.Mirror where
    +
     
    -import Import
    -import qualified Data.Text as T
    +
    module Handler.Mirror where
     
    -getMirrorR :: Handler RepHtml
    -getMirrorR = do
    -    defaultLayout $ do
    -        $(widgetFile "mirror")
    +import Import
    +import qualified Data.Text as T
     
    -postMirrorR :: Handler RepHtml
    -postMirrorR =  do
    -        postedText <- runInputPost $ ireq textField "content"
    -        defaultLayout $ do
    -            $(widgetFile "posted")
    -
    -
    +getMirrorR :: Handler RepHtml +getMirrorR = do + defaultLayout $ do + $(widgetFile "mirror") -

    Don’t forget to declare it inside yosog.cabal and Application.hs.

    +postMirrorR :: Handler RepHtml +postMirrorR = do + postedText <- runInputPost $ ireq textField "content" + defaultLayout $ do + $(widgetFile "posted") +
    + +

    Don’t forget to declare it inside yosog.cabal and Application.hs.

    We will need to use the reverse function provided by Data.Text which explain the additional import.

    -

    The only new thing here is the line that get the POST parameter named “content”. +

    The only new thing here is the line that get the POST parameter named “content”. If you want to know more detail about it and form in general you can take look at the yesod book.

    Create the two corresponding templates:

    -
    -
    -<h1> Enter your text
    -<form method=post action=@{MirrorR}>
    -    <input type=text name=content>
    -    <input type=submit>
    -
    -
    + -
    -
    -<h1>You've just posted
    -<p>#{postedText}#{T.reverse postedText}
    -<hr>
    -<p><a href=@{MirrorR}>Get back
    -
    -
    +
    <h1> Enter your text
    +<form method=post action=@{MirrorR}>
    +    <input type=text name=content>
    +    <input type=submit>
    +
    + + + +
    <h1>You've just posted
    +<p>#{postedText}#{T.reverse postedText}
    +<hr>
    +<p><a href=@{MirrorR}>Get back
    +

    And that is all. -This time, we won’t need to clean up. +This time, we won’t need to clean up. We may have used another way to generate the form -but we’ll see this in the next section.

    +but we’ll see this in the next section.

    Just try it by clicking here.

    @@ -662,24 +651,23 @@ If you forget it, there will be an error.

    After the route and the model, we write the handler. First, declare a new Handler module. Add import Handler.Blog inside Application.hs and add it into yosog.cabal. -Let’s write the content of Handler/Blog.hs. +Let’s write the content of Handler/Blog.hs. We start by declaring the module and by importing some block necessary to handle Html in forms.

    -
    -module Handler.Blog
    +
    module Handler.Blog
         ( getBlogR
         , postBlogR
         , getArticleR
         )
    -where
    +where
     
    -import Import
    +import Import
     
    --- to use Html into forms
    -import Yesod.Form.Nic (YesodNic, nicHtmlField)
    -instance YesodNic App
    -
    +-- to use Html into forms +import Yesod.Form.Nic (YesodNic, nicHtmlField) +instance YesodNic App +

    Remark: it is a best practice to add the YesodNic instance inside Foundation.hs. I put this definition here to make things easier but you should see a warning about this orphan instance. @@ -688,58 +676,55 @@ To put the include inside Foundation.hs is left as an exercice to the reader.Hint: Do not forget to put YesodNic and nicHtmlField inside the exported objects of the module.

    -
    -entryForm :: Form Article
    -entryForm = renderDivs $ Article
    -    <$> areq   textField "Title" Nothing
    -    <*> areq   nicHtmlField "Content" Nothing
    -
    +
    entryForm :: Form Article
    +entryForm = renderDivs $ Article
    +    <$> areq   textField "Title" Nothing
    +    <*> areq   nicHtmlField "Content" Nothing
    +

    This function defines a form for adding a new article. -Don’t pay attention to all the syntax. +Don’t pay attention to all the syntax. If you are curious you can take a look at Applicative Functor. You just have to remember areq is for required form input. Its arguments being: areq type label default_value.

    -
    --- The view showing the list of articles
    -getBlogR :: Handler RepHtml
    -getBlogR = do
    -    -- Get the list of articles inside the database.
    -    articles <- runDB $ selectList [] [Desc ArticleTitle]
    -    -- We'll need the two "objects": articleWidget and enctype
    -    -- to construct the form (see templates/articles.hamlet).
    +
    -- The view showing the list of articles
    +getBlogR :: Handler RepHtml
    +getBlogR = do
    +    -- Get the list of articles inside the database.
    +    articles <- runDB $ selectList [] [Desc ArticleTitle]
    +    -- We'll need the two "objects": articleWidget and enctype
    +    -- to construct the form (see templates/articles.hamlet).
         (articleWidget, enctype) <- generateFormPost entryForm
    -    defaultLayout $ do
    -        $(widgetFile "articles")
    -
    + defaultLayout $ do + $(widgetFile "articles") +

    This handler should display a list of articles. We get the list from the DB and we construct the form. Just take a look at the corresponding template:

    -
    -
    -<h1> Articles
    +
    +
    +
    <h1> Articles
     $if null articles
         -- Show a standard message if there is no article
    -    <p> There are no articles in the blog
    +    <p> There are no articles in the blog
     $else
         -- Show the list of articles
    -    <ul>
    -        $forall Entity articleId article <- articles
    -            <li> 
    -                <a href=@{ArticleR articleId} > #{articleTitle article}
    -<hr>
    -  <form method=post enctype=#{enctype}>
    -    ^{articleWidget}
    -    <div>
    -        <input type=submit value="Post New Article">
    -
    -
    + <ul> + $forall Entity articleId article <- articles + <li> + <a href=@{ArticleR articleId} > #{articleTitle article} +<hr> + <form method=post enctype=#{enctype}> + ^{articleWidget} + <div> + <input type=submit value="Post New Article"> +

    You should remark we added some logic inside the template. -There is a test and a “loop”.

    +There is a test and a “loop”.

    Another very interesting part is the creation of the form. The articleWidget was created by yesod. @@ -750,20 +735,19 @@ But we have to create the submit button.

    Get back to Handler/Blog.hs.

    -
    --- we continue Handler/Blog.hs
    -postBlogR :: Handler RepHtml
    -postBlogR = do
    +
    -- we continue Handler/Blog.hs
    +postBlogR :: Handler RepHtml
    +postBlogR = do
         ((res,articleWidget),enctype) <- runFormPost entryForm
    -    case res of 
    -         FormSuccess article -> do 
    +    case res of 
    +         FormSuccess article -> do 
                 articleId <- runDB $ insert article
    -            setMessage $ toHtml $ (articleTitle article) <> " created"
    -            redirect $ ArticleR articleId 
    -         _ -> defaultLayout $ do
    -                setTitle "Please correct your entry form"
    -                $(widgetFile "articleAddError")
    -
    + setMessage $ toHtml $ (articleTitle article) <> " created" + redirect $ ArticleR articleId + _ -> defaultLayout $ do + setTitle "Please correct your entry form" + $(widgetFile "articleAddError") +

    This function should be used to create a new article. We handle the form response. @@ -779,47 +763,43 @@ If things goes right:

    Here is the content of the error Page:

    -
    -<form method=post enctype=#{enctype}>
    +
    <form method=post enctype=#{enctype}>
         ^{articleWidget}
    -    <div>
    -        <input type=submit value="Post New Article">
    -
    + <div> + <input type=submit value="Post New Article"> +

    Finally we need to display an article:

    -
    -getArticleR :: ArticleId -> Handler RepHtml
    -getArticleR articleId = do
    +
    getArticleR :: ArticleId -> Handler RepHtml
    +getArticleR articleId = do
         article <- runDB $ get404 articleId
    -    defaultLayout $ do
    +    defaultLayout $ do
             setTitle $ toHtml $ articleTitle article
    -        $(widgetFile "article")
    -
    + $(widgetFile "article") +

    The get404 function try to do a get on the DB. If it fails it return a 404 page. The rest should be clear. Here is the content of templates/article.hamlet:

    -
    -
    -<h1> #{articleTitle article}
    -<article> #{articleContent article}
    -
    -
    + + +
    <h1> #{articleTitle article}
    +<article> #{articleContent article}
    +

    The blog system is finished. Just for fun, you can try to create an article with the following content:

    -
    -<p>A last try to <em>cross script</em> 
    -   and <em>SQL injection</em></p>
    -<p>Here is the first try: 
    -   <script>alert("You loose");</script></p>
    -<p> And Here is the last </p>
    -"); DROP TABLE ARTICLE;;
    -
    +
    <p>A last try to <em>cross script</em> 
    +   and <em>SQL injection</em></p>
    +<p>Here is the first try: 
    +   <script>alert("You loose");</script></p>
    +<p> And Here is the last </p>
    +"); DROP TABLE ARTICLE;;
    +

    Conclusion

    @@ -830,11 +810,11 @@ I made it very minimal.

    you should take a look at the recent i18n blog tutorial. It will be obvious I inspired my own tutorial on it. -You’ll learn in a very straightforward way how easy it is to use authorizations, +You’ll learn in a very straightforward way how easy it is to use authorizations, Time and internationalization.

    -

    If, on the other hand you don’t know Haskell. -Then you shouldn’t jump directly to web programming. +

    If, on the other hand you don’t know Haskell. +Then you shouldn’t jump directly to web programming. Haskell is a very complex and unusual language. My advice to go as fast as possible in using Haskell for web programming is:

    @@ -853,7 +833,7 @@ My advice to go as fast as possible in using Haskell for web programming is:

  • Use hlint as soon as possible to get good habits.
  • -

    As you should see, if you don’t already know Haskell, +

    As you should see, if you don’t already know Haskell, the path is long but I guaranty you it will be very rewarding!

    ps: You can download the source of this yesod blog tutorial at @@ -870,7 +850,7 @@ the path is long but I guaranty you it will be very rewarding!

    If you are curious, you can search about the Fibonacci node.js troll. Without any tweaking, Haskell handled this problem perfectly. I tested it myself using yesod instead of Snap.

  • -

    By view I mean yesod widget’s hamlet, lucius and julius files.

    +

    By view I mean yesod widget’s hamlet, lucius and julius files.

  • @@ -986,7 +966,7 @@ the path is long but I guaranty you it will be very rewarding!

    Écrit le : 15/01/2012 - modifié le : 11/04/2012 + modifié le : 26/04/2012
    Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/feed/feed.xml b/output/Scratch/fr/blog/feed/feed.xml index bbb1520bc..74d1f180d 100644 --- a/output/Scratch/fr/blog/feed/feed.xml +++ b/output/Scratch/fr/blog/feed/feed.xml @@ -25,7 +25,7 @@ <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Un tutoriel très court mais très dense pour apprendre Haskell.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Un tutoriel très court mais très dense pour apprendre Haskell.</p> <blockquote> <center><hr style="width:30%;float:left;border-color:#CCCCD0;margin-top:1em" /><span class="sc"><b>Table of Content</b></span><hr style="width:30%;float:right;border-color:#CCCCD0;margin-top:1em" /></center> @@ -38,9 +38,9 @@ <li><a href="#introduction">Introduction</a> <ul> <li><a href="#install">Install</a></li> - <li><a href="#don-t-be-afraid">Don&rsquo;t be afraid</a></li> + <li><a href="#don-t-be-afraid">Don’t be afraid</a></li> <li><a href="#very-basic-haskell">Very basic Haskell</a> - ...</li></ul></li></ul></div></hr></center></blockquote></div></p> + </li></ul></li></ul></div></hr></center></blockquote></div></p> tag:yannesposito.com,2012-02-02:/Scratch/fr/blog/Typography-and-the-Web/ @@ -52,30 +52,30 @@ yannesposito.com - <p><img alt="Screenshot of first in small caps with and without ligatures." src="/Scratch/img/blog/Typography-and-the-Web/first_sc_screenshot.png" /></p> + <p><img alt="Screenshot of first in small caps with and without ligatures." src="/Scratch/img/blog/Typography-and-the-Web/first_sc_screenshot.png" />;</p> <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> La typography sur le web est pourrie et nous ne somme pas près de voir ce problème réparé.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> La typography sur le web est pourrie et nous ne somme pas près de voir ce problème réparé.</p> </div> -<p>Je suis tombé sur ce site: <a href="http://opentypography.org/">open typography</a>. Leur message principal est&nbsp;:</p> +<p>Je suis tombé sur ce site: <a href="http://opentypography.org/">open typography</a>. Leur message principal est&nbsp;:</p> <blockquote> - <p>«There is no reason to wait for browser development to catch up. -We can all create better web typography ourselves, today.»</p> + <p>«There is no reason to wait for browser development to catch up. +We can all create better web typography ourselves, today.»</p> </blockquote> -<p>ou en français&nbsp;:</p> +<p>ou en français&nbsp;:</p> <blockquote> - <p>«Nous ne somme pas obligé d&rsquo;attendre le développement des navigateurs. -Nous pouv...</p></blockquote></p> + <p>«Nous ne somme pas obligé d’attendre le développement des navigateurs. +Nous pouvons créer un web a...</p></blockquote></p> tag:yannesposito.com,2012-01-15:/Scratch/fr/blog/Yesod-tutorial-for-newbies/ @@ -93,12 +93,12 @@ Nous pouv...</p></blockquote></p> <div class="intro"> -<p><em>mise à jour</em>: mise à jour pour la version 0.10 de yesod.</p> +<p><em>mise à jour</em>: mise à jour pour la version 0.10 de yesod.</p> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Un tutoriel pour yesod, un framework web Haskell. +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Un tutoriel pour yesod, un framework web Haskell. Vous ne devriez pas avoir besoin de savoir programmer en Haskell. -Par contre je suis désolé pour les francophones, -mais je n&rsquo;ai pas eu le courage de traduire cet article en Français.</p> +Par contre je suis désolé pour les francophones, +mais je n’ai pas eu le courage de traduire cet article en Français.</p> <blockquote> <center><span class="sc"><b>Table of content</b></span></center> @@ -106,11 +106,11 @@ mais je n&rsquo;ai pas eu le courage de traduire cet article en Françai <ul id="markdown-toc"> <li><a href="#before-the-real-start">Before the real start</a> <ul> <li><a href="#install">Install</a></li> - <li><a href="#initialize">In...</a></li></ul></li></ul></blockquote></div></p> + <li><a href="#initialize">Initialize</a></li></ul></li></ul></blockquote></div></p> tag:yannesposito.com,2011-10-20:/Scratch/fr/blog/SVG-and-m4-fractals/ - Accroître le pouvoir des languages déficients. + Accroître le pouvoir des languages déficients. 2011-10-20T09:39:36Z 2011-10-20T09:39:36Z @@ -124,21 +124,21 @@ mais je n&rsquo;ai pas eu le courage de traduire cet article en Françai <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Utiliser m4 pour accroître le pouvoir d&rsquo;<span class="sc">xslt</span> et d&rsquo;<span class="sc">svg</span>. Example cool, les fractales.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Utiliser m4 pour accroître le pouvoir d’<span class="sc">xslt</span> et d’<span class="sc">svg</span>. Example cool, les fractales.</p> </div> -<p>Lorsqu&rsquo;<span class="sc">xml</span> fût inventé beaucoup pensaient que c&rsquo;était l&rsquo;avenir. -Passer de fichiers plat à des fichiers structurés standardisés fût un grand progrès dans beaucoup de domaines. -Cerain se mirent à voir du <span class="sc">xml</span> de partout. -À tel point que les les format compatibles <span class="sc">xml</span> naquirent de toute part. -Non seulement comme format de fichie...</p></p> +<p>Lorsqu’<span class="sc">xml</span> fût inventé beaucoup pensaient que c’était l’avenir. +Passer de fichiers plat à des fichiers structurés standardisés fût un grand progrès dans beaucoup de domaines. +Cerain se mirent à voir du <span class="sc">xml</span> de partout. +À tel point que les les format compatibles <span class="sc">xml</span> naquirent de toute part. +Non seulement comme format de fichier, mais aussi comme format pour un langage ...</p></p> tag:yannesposito.com,2011-10-04:/Scratch/fr/blog/Yesod-excellent-ideas/ - Les idées de yesod + Les idées de yesod 2011-10-04T08:18:59Z 2011-10-04T08:18:59Z @@ -152,11 +152,11 @@ Non seulement comme format de fichie...</p></p> <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span></p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span></p> <p>Cela fait un moment que je suis la progression du <a href="http://www.yesodweb.com">framework yesod</a>. -À mon humble avis on peut commencer à l&rsquo;utiliser pour des applications sérieuses (comprendre en prod). -Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous parler de bonnes idées (parmi d&rsquo;autres) introduites par yesod que je n&rsquo;avais jamais vu ailleurs.</p> +À mon humble avis on peut commencer à l’utiliser pour des applications sérieuses (comprendre en prod). +Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous parler de bonnes idées (parmi d’autres) introduites par yesod que je n’avais jamais vu ailleurs.</p> </div> @@ -164,13 +164,13 @@ Avant de vous dire pourquoi vous devriez aussi le considérer, je préf& <h2 id="types-saufs">Types saufs</h2> -<p>Commençons par une BD d&rsquo;<a href="http://xkcd.com">xkcd</a>&nbsp;:</p> +<p>Commençons par une BD d’<a href="http://xkcd.com">xkcd</a>&nbsp;:</p> <p></p></p> tag:yannesposito.com,2011-09-28:/Scratch/fr/blog/Higher-order-function-in-zsh/ - Fonctions d'ordre supérieur en zsh + Fonctions d'ordre supérieur en zsh 2011-09-28T13:15:23Z 2011-09-28T13:15:23Z @@ -184,26 +184,28 @@ Avant de vous dire pourquoi vous devriez aussi le considérer, je préf& <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> des fonctions d&rsquo;ordres supérieurs en zsh.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> des fonctions d’ordres supérieurs en zsh.</p> </div> -<p>Tout d&rsquo;abord, pourquoi c&rsquo;est important d&rsquo;avoir ces fonctions. -Plus je programmais avec zsh plus j&rsquo;essayais d&rsquo;avoir un style fonctionnel.</p> +<p>Tout d’abord, pourquoi c’est important d’avoir ces fonctions. +Plus je programmais avec zsh plus j’essayais d’avoir un style fonctionnel.</p> -<p>Le minimum pour pouvoir avoir du code plus lisible c&rsquo;est de posséder les fonctions <code>map</code>, <code>filter</code> et <code>fold</code>.</p> +<p>Le minimum pour pouvoir avoir du code plus lisible c’est de posséder les fonctions <code>map</code>, <code>filter</code> et <code>fold</code>.</p> <p>Voici pourquoi avec une comparaison. -Commençons par un programme qui converti tous les gif en png dans plusieurs répertoires projets contenant tous des répertoires resources. +Commençons par un programme qui converti tous les gif en png dans plusieurs répertoires projets contenant tous des répertoires resources. Avant&nbsp;:</p> -</p> +<p>Avant ⇒</p> + +<pre><code class="zsh"># for each ...</code></pre></p> tag:yannesposito.com,2011-09-28:/Scratch/fr/blog/programming-language-experience/ - Mon expérience avec les languages de programmation + Mon expérience avec les languages de programmation 2011-09-28T10:21:41Z 2011-09-28T10:21:41Z @@ -216,21 +218,23 @@ Avant&nbsp;:</p> <div class="intro"> -<span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Mon avis court et hautement subjectif concernant les différents languages de programmations que j&rsquo;ai utilisé. +<span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Mon avis court et hautement subjectif concernant les différents languages de programmations que j’ai utilisé. </div> <h3 id="basic"><code>BASIC</code></h3> -<p><img alt="Title image" src="/Scratch/img/blog/programming-language-experience/basic.gif" class="left" /></p> +<p><img alt="Title image" src="/Scratch/img/blog/programming-language-experience/basic.gif" class=" left" /></p> <p>Ah&nbsp;! Le language de mes premiers programmes&nbsp;! Je devais avoir 10-11 ans. -Sous <code>MO5</code>, <code>Amstrad CPC 6128</code> et même <code>Atari STe</code>. +Sous <code>MO5</code>, <code>Amstrad CPC 6128</code> et même <code>Atari STe</code>. Le langage des <code>GOTO</code>s. -Je suis empleint de nostalgie rien que d&rsquo;y penser. -C&rsquo;est à peu prêt le seul intérêt de ce...</p></p></p> +Je suis empleint de nostalgie rien que d’y penser. +C’est à peu prêt le seul intérêt de ce langage.</p> + +<p>Aujourd’...</p></p></p> tag:yannesposito.com,2011-08-25:/Scratch/fr/blog/Learn-Vim-Progressively/ @@ -242,27 +246,29 @@ C&rsquo;est à peu prêt le seul intérêt de ce...</p> yannesposito.com - <p><img alt="Über leet use vim!" src="/Scratch/img/blog/Learn-Vim-Progressively/uber_leet_use_vim.jpg" /></p> + <p><img alt="Über leet use vim!" src="/Scratch/img/blog/Learn-Vim-Progressively/uber_leet_use_vim.jpg" /></p> <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Vous désirez apprendre vim (le meilleur editeur de texte connu à ce jour) le plus rapidement possible. Voici mes conseils pour vous aider. Commencez à apprendre le minimum vital, puis apprenez doucement de nouvelles commandes.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Vous désirez apprendre vim (le meilleur editeur de texte connu à ce jour) le plus rapidement possible. Voici mes conseils pour vous aider. Commencez à apprendre le minimum vital, puis apprenez doucement de nouvelles commandes.</p> </div> -<p><a href="http://www.vim.org">Vim</a> ou l&rsquo;éditeur qui vallait 3 milliards&nbsp;:</p> +<p><a href="http://www.vim.org">Vim</a> ou l’éditeur qui vallait 3 milliards&nbsp;:</p> <blockquote> <p>Meilleur, plus fort, plus rapide.</p> </blockquote> -<p>Apprenez <a href="http://www.vim.org">vim</a> et ce sera votre dernier éditeur. -Aucun éditeur que je connaisse ne le surpasse. -Sa prise en mais est difficile, mais p...</p></p> +<p>Apprenez <a href="http://www.vim.org">vim</a> et ce sera votre dernier éditeur. +Aucun éditeur que je connaisse ne le surpasse. +Sa prise en mais est difficile, mais payante.</p> + +</p> tag:yannesposito.com,2011-08-17:/Scratch/fr/blog/A-more-convenient-diff/ @@ -274,21 +280,27 @@ Sa prise en mais est difficile, mais p...</p></p> yannesposito.com - <p><code>diff</code> est un utilitaire très pratique, mais il n&rsquo;est pas facile à lire pour nous, les Hommes.</p> + <p><code>diff</code> est un utilitaire très pratique, mais il n’est pas facile à lire pour nous, les Hommes.</p> -<p>C&rsquo;est pourquoi, lorsque vous utilisez <code>git</code>, il vous montre un formatage plus agréable avec des couleurs.</p> +<p>C’est pourquoi, lorsque vous utilisez <code>git</code>, il vous montre un formatage plus agréable avec des couleurs.</p> -<p>Voici le script que j&rsquo;utilise lorsque je veux avoir un <code>diff</code> à la git.</p> +<p>Voici le script que j’utilise lorsque je veux avoir un <code>diff</code> à la git.</p> -<div class="code"><div class="file"><a href="/Scratch/fr/blog/A-more-convenient-diff/code/ydiff"> &#x27A5; ydiff </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Comment"><span class="Comment">#</span>!/usr/bin/env zsh</span> +<div class="codefile"><a href="/Scratch/fr/blog/A-more-convenient-diff/code/ydiff">&#x27A5; ydiff</a></div> -<span class="Comment"><span class="Comment">#</span> Load colors helpers</span> -autoload -U colors <span class="Keyword">&amp;&amp;</span> colors +<pre><code class="zsh">#!/usr/bin/env zsh + +# Load colors helpers +autoload -U colors &amp;&amp; colors function colorize_diff { - <span class="Keyword">...</span></pre></div></div> + while read line; do + case ${line[0]} in + +) print -n $fg[green];; + -) print -n $fg[red];; + @) # Display in cyan the @@ positions @@ + if [[ ${line[1]} = '@' ]]; then + line=$(print $line | perl -pe 's...</code></pre> tag:yannesposito.com,2011-07-10:/Scratch/fr/blog/Haskell-Mandelbrot/ @@ -300,13 +312,18 @@ function colorize_diff { yannesposito.com - <p>Voici le code &ldquo;obfusqué&rdquo;&nbsp;:</p> + <p>Voici le code “obfusqué”&nbsp;:</p> -<div class="code"><div class="file"><a href="/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs"> &#x27A5; animandel.hs </a></div><div class="withfile"> -<pre class="twilight"> -a=27;b=79;c=<span class="Constant">C</span>(-2.0,-1.0);d=<span class="Constant">C</span>(1.0,1.0);e=<span class="Constant">C</span>(-2.501,-1.003) -<span class="Keyword">newtype</span> <span class="Constant">C</span> = <span class="Constant">C</span> (<span class="Constant">Double</span>,<span class="Constant">Double</span>) <span class="Keyword">deriving</span> (<span class="Constant">Show</span>,<span class="Constant">Eq</span>) -<span class="Keyword">instance</span> <span class="Constant">Num</span> <span class="Constant">C</span> <span class="Keyword">where...</span></pre></div></div> +<div class="codefile"><a href="/Scratch/fr/blog/Haskell-Mandelbrot/code/animandel.hs">&#x27A5; animandel.hs</a></div> + +<pre><code class="haskell">a=27;b=79;c=C(-2.0,-1.0);d=C(1.0,1.0);e=C(-2.501,-1.003) +newtype C = C (Double,Double) deriving (Show,Eq) +instance Num C where C(x,y)*C(z,t)=C(z*x-y*t,y*z+x*t);C(x,y)+C(z,t)=C(x+z,y+t);abs(C(x,y))=C(sqrt(x*x+y*y),0.0) +r(C(x,y))=x;i(C(x,y))=y +f c z 0=0;f c z n=if(r(abs(z))&gt;2)then n else f c ((z*z)+c) (n-1) +h j k = map (\z-&gt;(f (C z) (C(0,0)) 32,(fst z&gt;l - q/2))) [(x,y)|y&lt;-[p,(p+((o-p)/a))..o],x&lt;-[m,(m + q)..l]] where o=i k;p=i j;m=r j;l=r k;q=(l-m)/b +u j k = concat $ map v $ h j k where v (i,p)=(" .,`'°\":;-+oO0123456789=!%*§&amp;$@#"!!i):rst p;rst True="\n";rst False="" +main = putStrLn $ im...</code></pre> tag:yannesposito.com,2011-05-18:/Scratch/fr/blog/Password-Management/ @@ -324,23 +341,24 @@ a=27;b=79;c=<span class="Constant">C</span>(-2.0,-1.0);d=<span cl <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Une méthode de gestion des mots de passes que j&rsquo;utilise avec succès depuis quelques années.<br /> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Une méthode de gestion des mots de passes que j’utilise avec succès depuis quelques années.<br /> <strong><code>sha1( mot_de_passe + nom_de_domaine )</code></strong><br /> -Je ne mémorise qu&rsquo;un seul mot de passe de très bonne qualité. -J&rsquo;utilise des mots de passe différents sur tous les sites.</p> +Je ne mémorise qu’un seul mot de passe de très bonne qualité. +J’utilise des mots de passe différents sur tous les sites.</p> </div> -<p>Avant de commencer, je tiens à préciser qu&rsquo;il s&rsquo;agit d&rsquo;une tentative de vous vendre mon appli iPhone&nbsp;;-).</p> +<p>Avant de commencer, je tiens à préciser qu’il s’agit d’une tentative de vous vendre mon appli iPhone&nbsp;;-).</p> -<p>Vous êtes toujours là&nbsp;? -Bon, d&rsquo;accord, même si vous ne téléchargez pas mon application vous pouvez quand même utili...</p></p></div></p> +<p>Vous êtes toujours là&nbsp;? +Bon, d’accord, même si vous ne téléchargez pas mon application vous pouvez quand même utiliser ma méthode. +Elle est à la fois très sûre et simple à ut...</p></p></div></p> tag:yannesposito.com,2011-04-20:/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/ - Hébergement github + Hébergement github 2011-04-20T15:22:15Z 2011-04-20T15:22:15Z @@ -350,7 +368,7 @@ Bon, d&rsquo;accord, même si vous ne téléchargez pas mon appl <p><img alt="Title image" src="/Scratch/img/blog/2011-04-20-Now-hosted-on-github/main.png" /></p> -<p>J&rsquo;héberge mon site sur github à partir d&rsquo;aujourd&rsquo;hui.</p> +<p>J’héberge mon site sur github à partir d’aujourd’hui.</p> </p> @@ -369,9 +387,9 @@ Bon, d&rsquo;accord, même si vous ne téléchargez pas mon appl <div class="encadre"> -<p><em>Mise à jour&nbsp;:</em> Je pense que je vais finallement changer d&rsquo;avis. +<p><em>Mise à jour&nbsp;:</em> Je pense que je vais finallement changer d’avis. Pourquoi&nbsp;? -Tout d&rsquo;abord, je viens de découvrir un convertisseur javascript vers coffeescript, ensuite Denis Knauf m&rsquo;a laissé un commentaire et m&rsquo;a appris l&rsquo;existence d&rsquo;une fonction <code>CoffeeScript.eval</code>. De plus, il faut voir CoffeeScript comme javascript avec une syntaxe similaire à Ruby et pas comme un langage similaire à Ruby.</p> +Tout d’abord, je viens de découvrir un convertisseur javascript vers coffeescript, ensuite Denis Knauf m’a laissé un commentaire et m’a appris l’existence d’une fonction <code>CoffeeScript.eval</code>. De plus, il faut voir CoffeeScript comme javascript avec une syntaxe similaire à Ruby et pas comme un langage similaire à Ruby.</p> </div> @@ -381,11 +399,11 @@ Tout d&rsquo;abord, je viens de découvrir un convertisseur javascript v <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Qu&rsquo;est-ce qui n&rsquo;allait pas avec Coffeescript? La meta-programm...</p></div></p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Qu’est-ce qui n’allait pas avec Coffeescript? La meta-programmation, il faut le “vendre” aux autres, une nouvelle éta...</p></div></p> tag:yannesposito.com,2011-01-01:/Scratch/fr/blog/2011-01-03-Happy-New-Year/ - Bonne et heureuse année + Bonne et heureuse année 2011-01-01T06:55:54Z 2011-01-01T06:55:54Z @@ -393,19 +411,19 @@ Tout d&rsquo;abord, je viens de découvrir un convertisseur javascript v yannesposito.com - <p>Bonne et heureuse année&nbsp;!</p> + <p>Bonne et heureuse année&nbsp;!</p> -<p>J&rsquo;étais très occupé ces derniers mois. +<p>J’étais très occupé ces derniers mois. Maintenant il me semble que je vais pouvoir faire revivre ce blog.</p> -<p>J&rsquo;ai fait un outil qui permet d&rsquo;écrire des livre en utilisant une syntaxe proche de markdown. -C&rsquo;est un markdown avec des macros (essentiel pour les textes longs). -De plus le système gère la génération de pages HTML ainsi que du PDF engendré avec du XeLaTeX. -Je n&rsquo;en ai pas encore terminé avec ça. Mais si je tarde trop, je communiquerai lorsque j&rsquo;aurai fini le minimum.</p> +<p>J’ai fait un outil qui permet d’écrire des livre en utilisant une syntaxe proche de markdown. +C’est un markdown avec des macros (essentiel pour les textes longs). +De plus le système gère la génération de pages HTML ainsi que du PDF engendré avec du XeLaTeX. +Je n’en ai pas encore terminé avec ça. Mais si je tarde trop, je communiquerai lorsque j’aurai fini le minimum.</p> -<p>J&rsquo;ai écrit un framework MVC pour application javascript simple mais néanmoins très rapide.</p> +<p>J’ai écrit un framework MVC pour application javascript simple mais néanmoins très rapide.</p> -<p>Meilleurs vœux à tous&nbsp;!</p> +<p>Meilleurs vœux à tous&nbsp;!</p> @@ -422,14 +440,14 @@ Je n&rsquo;en ai pas encore terminé avec ça. Mais si je tarde trop <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> J&rsquo;ai fait un système simple de macros pour mon blog. Par exemple, il me suffit d&rsquo;écrire %<span></span>latex et ça affiche <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> J’ai fait un système simple de macros pour mon blog. Par exemple, il me suffit d’écrire %<span></span>latex et ça affiche <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>.</p> </div> -<p>J&rsquo;ai ajouter un système de macro pour mon système de blog. -Lorsqu&rsquo;on est habitué à <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T</span></p> +<p>J’ai ajouter un système de macro pour mon système de blog. +Lorsqu’on est habitué à <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T</span></p> tag:yannesposito.com,2010-10-14:/Scratch/fr/blog/2010-10-14-Fun-with-wav/ @@ -445,23 +463,24 @@ Lorsqu&rsquo;on est habitué à <span style="text-transform: uppe <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Je me suis amusé à lire un fichier <code>wav</code>. Le <code>C</code> fut le langage le mieux adapté à ce traitement. Bien meilleur que Ruby par exemple.</p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Je me suis amusé à lire un fichier <code>wav</code>. Le <code>C</code> fut le langage le mieux adapté à ce traitement. Bien meilleur que Ruby par exemple.</p> -<p>edit: Je voulais que ce programme fonctionne sur une machine spécifique. En aucun cas je ne pensais publier ce code pour une utilisation autre que celle-ci.</p> +<p>edit: Je voulais que ce programme fonctionne sur une machine spécifique. En aucun cas je ne pensais publier ce code pour une utilisation autre que celle-ci.</p> </div> -<p>J&rsquo;ai eu besoin de calculer la somme des valeurs absolue des données d&rsquo;un fichier <code>wav</code>. -Pour des raison d&rsquo;efficacité (et aussi de fun), j&rsquo;ai fait le programme en <code>C</code>.</p> +<p>J’ai eu besoin de calculer la somme des valeurs absolue des données d’un fichier <code>wav</code>. +Pour des raison d’efficacité (et aussi de fun), j’ai fait le programme en <code>C</code>.</p> -<p>Celà faisait longtemps que je n&rsquo;avais pas programmé en <code>C</code>. -De mémoire il était peu aisé de manipuler des ...</p> +<p>Celà faisait longtemps que je n’avais pas programmé en <code>C</code>. +De mémoire il était peu aisé de manipuler des fichiers. +Mais je dois concéder que j’ai été...</p> tag:yannesposito.com,2010-10-10:/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ - Sécurisez vos emails + Sécurisez vos emails 2010-10-10T16:39:00Z 2010-10-10T16:39:00Z @@ -469,28 +488,28 @@ De mémoire il était peu aisé de manipuler des ...</p>yannesposito.com - <p><img alt="Title image" src="/Scratch/img/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/main.png" class="clean" /></p> + <p><img alt="Title image" src="/Scratch/img/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/main.png" class="clean " /></p> <div class="intro"> -<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> <em>avec un Mac</em> </p> +<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> <em>avec un Mac</em> </p> <ul> - <li>Récupérez un certificat signé par une AC: <a href="http://www.instantssl.com/ssl-certificate-products/free-email-certificate.html">cliquez ici pour un certificat gratuit</a>&nbsp;;</li> + <li>Récupérez un certificat signé par une AC: <a href="http://www.instantssl.com/ssl-certificate-products/free-email-certificate.html">cliquez ici pour un certificat gratuit</a>&nbsp;;</li> <li>ouvrez le fichier&nbsp;;</li> - <li>supprimer le fichier en mode sécurisé&nbsp;;</li> - <li>utilisez Mail plutôt que l&rsquo;interface web de gmail.</li> + <li>supprimer le fichier en mode sécurisé&nbsp;;</li> + <li>utilisez Mail plutôt que l’interface web de gmail.</li> </ul> </div> -<p>J&rsquo;ai (re)découvert comment adoptez la norme S/MIME. -J&rsquo;ai été surpris de voir à quel point ce fut aisé. -Il y a seulement quelques années c&rs...</p></p> +<p>J’ai (re)découvert comment adoptez la norme S/MIME. +J’ai été surpris de voir à quel point ce fut aisé. +Il y a seulement quelques années c’était bien plus difficile à accom...</p></p> tag:yannesposito.com,2010-10-06:/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/ @@ -502,18 +521,19 @@ Il y a seulement quelques années c&rs...</p></p> yannesposito.com - <p>Vous avez pu constater que j&rsquo;ai modifié le design de mon blog. -Maintenant il doit être beaucoup plus léger qu&rsquo;avant. -Je n&rsquo;utilise plus de CSS3 et beaucoup moins de javascript. -Bien entendu, même avant, mes pages étaient parfaitement lisibles sans javascript. -Mais, je me suis aperçu que les systèmes de CSS3 sont loin d&rsquo;être au point. -J&rsquo;utilisait des gradient en CSS3, ainsi que des ombres sous le texte. Ça avait un rendu très sympa. Sauf&hellip; -Ce n&rsquo;était pas compatible ie6, sous Chrome le rendu était d&rsquo;une lenteur incroyable. -J&rsquo;ai donc décidé de faire un site à minima. -Je voulais qu&rsquo;il soit joli <em>et</em> le plus simple possible pour assurer sa compatibilité. -Les règles que je me suis fixées sont donc:</p> + <p>Vous avez pu constater que j’ai modifié le design de mon blog. +Maintenant il doit être beaucoup plus léger qu’avant. +Je n’utilise plus de CSS3 et beaucoup moins de javascript. +Bien entendu, même avant, mes pages étaient parfaitement lisibles sans javascript. +Mais, je me suis aperçu que les systèmes de CSS3 sont loin d’être au point. +J’utilisait des gradient en CSS3, ainsi que des ombres sous le texte. Ça avait un rendu très sympa. Sauf… +Ce n’était pas compatible ie6, sous Chrome le rendu était d’une lenteur incroyable. +J’ai donc décidé de faire un site à minima. +Je voulais qu’il soit joli <em>et</em> le plus simple possible pour assurer sa compatibilité. +Les règles que je me suis fixées sont donc:</p> - +<ul> + <li>pas d’élément CSS qui commence par <code>-moz</code> ou <code>-webkit</code></li></ul> tag:yannesposito.com,2010-09-02:/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/ @@ -525,16 +545,18 @@ Les règles que je me suis fixées sont donc:</p> yannesposito.com - <p>Vous pouvez remarquer qu&rsquo;à la fin de chaque page je donne une date de dernière modification. -Précédemment cette date était calculée en utilisant la date du fichier. -Mais il arrive fréquemment que je fasse un <code>touch</code> d&rsquo;un fichier pour engendrer tout le site de nouveau. -Donc la date n&rsquo;est pas nécessairement la <em>vraie</em> de modification du contenue.</p> + <p>Vous pouvez remarquer qu’à la fin de chaque page je donne une date de dernière modification. +Précédemment cette date était calculée en utilisant la date du fichier. +Mais il arrive fréquemment que je fasse un <code>touch</code> d’un fichier pour engendrer tout le site de nouveau. +Donc la date n’est pas nécessairement la <em>vraie</em> de modification du contenue.</p> -<p>J&rsquo;utilise <a href="http://git-scm.org">git</a> pour <em>versionner</em> mon site web. -Et cet outil me permet de récupérer la dernière date de <em>vraie</em> modification d&rsquo;un fichier. -Voici comment je m&rsquo;y prend avec <a href="http://nanoc.stoneship.org">nanoc</a>&nbsp;:</p> +<p>J’utilise <a href="http://git-scm.org">git</a> pour <em>versionner</em> mon site web. +Et cet outil me permet de récupérer la dernière date de <em>vraie</em> modification d’un fichier. +Voici comment je m’y prend avec <a href="http://nanoc.stoneship.org">nanoc</a>&nbsp;:</p> -<div class="code"><div class="file"></div></div> +<div class="codefile"><a href="/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/code/gitmtime.rb">&#x27A5; gitmtime.rb</a></div> + + tag:yannesposito.com,2010-09-02:/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/ @@ -546,20 +568,27 @@ Voici comment je m&rsquo;y prend avec <a href="http://nanoc.stoneship.org yannesposito.com - <p>Allons directement à l&rsquo;essentiel&nbsp;: -voici deux fonctions à intégrer à votre application iPhone pour afficher l&rsquo;encodage en base64 ou en hexadecimal du hash sha1 d&rsquo;un string en Objective-C pour iPhone.</p> + <p>Allons directement à l’essentiel&nbsp;: +voici deux fonctions à intégrer à votre application iPhone pour afficher l’encodage en base64 ou en hexadecimal du hash sha1 d’un string en Objective-C pour iPhone.</p> -<p>Pour l&rsquo;usage c&rsquo;est très simple, copiez le code dans la classe de votre choix. +<p>Pour l’usage c’est très simple, copiez le code dans la classe de votre choix. Puis&nbsp;:</p> -<pre class="twilight"> -<span class="CCCPreprocessorLine">#<span class="CCCPreprocessorDirective">import</span> <span class="String"><span class="String">&lt;</span>CommonCrypto/CommonDigest.h<span class="String">&gt;</span></span></span> +<pre><code class="objective-c">#import &lt;CommonCrypto/CommonDigest.h&gt; ... -<span class="Support">NSString</span> *b64_hash = [<span class="Variable">self</span> <span class="SupportFunction">b64_sha1<span class="SupportFunction">:</span></span><span class="String"></span></pre> +NSString *b64_hash = [self b64_sha1:@"some NSString to be sha1'ed"]; +... +NSString *hex_hash = [self hex_sha1:@"some NSString to be sha1'ed"]; +</code></pre> + +<p>L’algorithme pour l’encodage en <code>base64</code> doit être programmé sur iPhone. +Il n’y a pas de librairie officielle qui s’occupe de ça.</p> + +<div class="codefile"></div> tag:yannesposito.com,2010-08-31:/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ - Envoyer un mail en ligne de commande avec un fichier attaché + Envoyer un mail en ligne de commande avec un fichier attaché 2010-08-31T08:16:04Z 2010-08-31T08:16:04Z @@ -567,20 +596,21 @@ Puis&nbsp;:</p> yannesposito.com - <p>J&rsquo;ai dû envoyer un mail en ligne de commande récemment. -Quelle ne fût pas ma surprise lorsque je constatais que ce n&rsquo;était vraiment pas évident. -Je n&rsquo;avais ni <code>pine</code> ni <code>mutt</code>. Seulement <code>mail</code> et <code>mailx</code>.</p> + <p>J’ai dû envoyer un mail en ligne de commande récemment. +Quelle ne fût pas ma surprise lorsque je constatais que ce n’était vraiment pas évident. +Je n’avais ni <code>pine</code> ni <code>mutt</code>. Seulement <code>mail</code> et <code>mailx</code>.</p> -<p>Ce qu&rsquo;on trouve sur internet pour envoyer un mail avec fichier attaché c&rsquo;est ça&nbsp;:</p> +<p>Ce qu’on trouve sur internet pour envoyer un mail avec fichier attaché c’est ça&nbsp;:</p> -<pre class="twilight"> -uuencode fic.jpg fic.jpg <span class="Keyword">|</span> mail -s <span class="String"><span class="String">'</span>Subject<span class="String">'</span></span> -</pre> +<pre><code class="zsh">uuencode fic.jpg fic.jpg | mail -s 'Subject' +</code></pre> -<p>Bon, alors, bête et discipliné j&rsquo;ai essayé. -Et bien, ça marche <em>presque</em> tout le temps. -Pour mon fichier ça n&rsquo;a pas marché du tout. -Je l&rsquo;ai compressé au format <code>.gz</code>, </p> +<p>Bon, alors, bête et discipliné j’ai essayé. +Et bien, ça marche <em>presque</em> tout le temps. +Pour mon fichier ça n’a pas marché du tout. +Je l’ai compressé au format <code>.gz</code>, <code>.bz2</code> et <code>.zip</code>. +Avec le format <code>.bz2</code> le mail reçu avait bien un fichier attaché. +Mais avec les formats <code>.gz</code> et <code>.zi...</code></p> tag:yannesposito.com,2010-08-23:/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/ @@ -594,18 +624,18 @@ Je l&rsquo;ai compressé au format <code>.gz</code>, </p& <h1 class="first" id="maintenant-sur-herokuhttpherokucom">Maintenant sur <a href="http://heroku.com">Heroku</a></h1> -<p>J&rsquo;ai changé mon hébergeur. Mobileme n&rsquo;est absolument pas adapté à la diffusion de mon blog. C&rsquo;est pourquoi je suis passé à <a href="http://heroku.com">Heroku</a>.</p> +<p>J’ai changé mon hébergeur. Mobileme n’est absolument pas adapté à la diffusion de mon blog. C’est pourquoi je suis passé à <a href="http://heroku.com">Heroku</a>.</p> -<p>Mais comme vous devez le savoir mon blog est un site complètement statique. -J&rsquo;utilise <a href="http://nanoc.stoneship.org/">nanoc</a> pour l&rsquo;engendrer. -Avoir un site statique amène beaucoup d&rsquo;avantages par rapport à un site dynamique. Surtout en terme de sécurité. +<p>Mais comme vous devez le savoir mon blog est un site complètement statique. +J’utilise <a href="http://nanoc.stoneship.org/">nanoc</a> pour l’engendrer. +Avoir un site statique amène beaucoup d’avantages par rapport à un site dynamique. Surtout en terme de sécurité. Voici comment configurer un site statique sur heroku.</p> -<p>La racine de mes fichiers est &lsquo;/output&rsquo;. Vous devez simplement créer deux fichiers. Un fichier <code>config.ru</code></p> +<p>La racine de mes fichiers est ‘/output’. Vous devez simplement créer deux fichiers. Un fichier <code>config.ru</code><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>&...</p> tag:yannesposito.com,2010-08-11:/Scratch/fr/blog/2010-07-09-Indecidabilities/ - Indécidabilités (partie 1) + Indécidabilités (partie 1) 2010-08-11T08:04:31Z 2010-08-11T08:04:31Z @@ -613,13 +643,13 @@ Voici comment configurer un site statique sur heroku.</p> yannesposito.com - <p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Je crée un mode mathématique simple pour parler de différents types d&rsquo;<em>indécidabilités</em>&nbsp;:</p> + <p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Je crée un mode mathématique simple pour parler de différents types d’<em>indécidabilités</em>&nbsp;:</p> <ul> - <li>indécidabilité due aux erreurs d&rsquo;observation&nbsp;;</li> - <li>grandes erreurs résultant de petites erreurs de mesure&nbsp;;</li> - <li>indécidabilité fractales&nbsp;;</li> - <li>indécidabilité logique.</li> + <li>indécidabilité due aux erreurs d’observation&nbsp;;</li> + <li>grandes erreurs résultant de petites erreurs de mesure&nbsp;;</li> + <li>indécidabilité fractales&nbsp;;</li> + <li>indécidabilité logique.</li> </ul> @@ -632,20 +662,21 @@ Voici comment configurer un site statique sur heroku.</p> <div class="corps"> -<h1 class="first" id="les-indcidabilits">Les indécidabilités</h1> +<h1 class="first" id="les-indcidabilits">Les indécidabilités</h1> <div class="intro"> -<p>Si le monde a été fabriqué par un démiurge, on peut dire que celui-ci devait avoir le sens de l&rsquo;humour. -Et le récit que je vais faire va vous en fournir la preuve. -Je vais me mettre à sa place. -Je vais créer u...</p> +<p>Si le monde a été fabriqué par un démiurge, on peut dire que celui-ci devait avoir le sens de l’humour. +Et le récit que je vais faire va vous en fournir la preuve. +Je vais me mettre à sa place. +Je vais créer un monde simplifié. +Un monde régi exacteme...</p> tag:yannesposito.com,2010-07-31:/Scratch/fr/blog/2010-07-31-New-style-after-holidays/ - Nouveau style après les vacances + Nouveau style après les vacances 2010-07-31T21:59:10Z 2010-07-31T21:59:10Z @@ -653,11 +684,11 @@ Je vais créer u...</p> yannesposito.com - <p>Avant les vacances beaucoup d&rsquo;utilisateurs se sont plaints de la lenteur de rendu de mon site. -Il s&rsquo;agit notamment de problèmes avec Chrome en particulier. -Mais pour éviter tout problème. -J&rsquo;ai complètement modifié le style de mon site web. -Il est inspiré du style de l&rsquo;application iBooks<small>&copy;</small> sur iPhone<small>&copy;</small>.</p> + <p>Avant les vacances beaucoup d’utilisateurs se sont plaints de la lenteur de rendu de mon site. +Il s’agit notamment de problèmes avec Chrome en particulier. +Mais pour éviter tout problème. +J’ai complètement modifié le style de mon site web. +Il est inspiré du style de l’application iBooks<small>&copy;</small> sur iPhone<small>&copy;</small>.</p> <p>Dites moi ce que vous pensez de ce nouveau design.</p> @@ -672,19 +703,21 @@ Il est inspiré du style de l&rsquo;application iBooks<small>& yannesposito.com - <p>Beaucoup d&rsquo;utilisateurs de <a href="http://reddit.com">Reddit</a> m&rsquo;ont rapporté que mon site était très long à charger et à <em>scroller</em>. -Ils pensaient qu&rsquo;il s&rsquo;agissait d&rsquo;un problème dû aux ombres que j&rsquo;applique sur le texte. -J&rsquo;étais un peu surpris puisque je fais mes tests sur une machine vraiment très lente et je n&rsquo;avais jamais détecté ces problèmes. -En réalité, ce qui ralenti le rendu de ce site est par ordre d&rsquo;importance&nbsp;:</p> + <p>Beaucoup d’utilisateurs de <a href="http://reddit.com">Reddit</a> m’ont rapporté que mon site était très long à charger et à <em>scroller</em>. +Ils pensaient qu’il s’agissait d’un problème dû aux ombres que j’applique sur le texte. +J’étais un peu surpris puisque je fais mes tests sur une machine vraiment très lente et je n’avais jamais détecté ces problèmes. +En réalité, ce qui ralenti le rendu de ce site est par ordre d’importance&nbsp;:</p> <ol> - <li>Les dégradés sur Chrome (pas dans Safari sur Mac)</li> + <li>Les dégradés sur Chrome (pas dans Safari sur Mac)</li> <li>les <em>box shadows</em> sur Firefox</li> </ol> -<h2 id="les-dgrads">les dégradés</h2> +<h2 id="les-dgrads">les dégradés</h2> -<p>Sur Safari il n&rsquo;y a absolument aucun problème avec les dégradés. Par contre sur Chrome sous Linux le si...</p> +<p>Sur Safari il n’y a absolument aucun problème avec les dégradés. Par contre sur Chrome sous Linux le site devient quasiment inutilisable.</p> + +<p>Safari et Chrome utilisent <em>webkit</em></p> tag:yannesposito.com,2010-07-05:/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/ @@ -700,13 +733,13 @@ En réalité, ce qui ralenti le rendu de ce site est par ordre d&rsq <div class="intro"> -<p><abbr title="Trop long à lire">tlàl</abbr>:</p> +<p><abbr title="Trop long à lire">tlàl</abbr>:</p> <ul> - <li>J&rsquo;ai essayé de faire une version de <a href="http://yannesposito.com/Softwares/YPassword.html">YPassword</a> en jQuery et avec Cappuccino.</li> - <li>Cappuccino est très bien sur les navigateurs non mobile mais l&rsquo;application pèse 1.4Mo et n&rsquo;est pas compatible avec l&rsquo;iPhone.</li> - <li>la version jQuery n&rsquo;est pas aussi jolie que la version réalisée avec Cappuccino mais elle pèse seulement 106Ko et est compatible avec l&rsquo;iPhone.</li> - <li>J&rsquo;essayerai Dashcode 3</li> + <li>J’ai essayé de faire une version de <a href="http://yannesposito.com/Softwares/YPassword.html">YPassword</a> en jQuery et avec Cappuccino.</li> + <li>Cappuccino est très bien sur les navigateurs non mobile mais l’application pèse 1.4Mo et n’est pas compatible avec l’iPhone.</li> + <li>la version jQuery n’est pas aussi jolie que la version réalisée avec Cappuccino mais elle pèse seulement 106Ko et est compatible avec l’iPhone.</li> + <li>J’essayerai Dashcode 3</li> </ul> @@ -718,7 +751,7 @@ En réalité, ce qui ralenti le rendu de ce site est par ordre d&rsq <div class="intro"> -<p>Avant de commencer, je dois dire que je sais que Cappuccino et jQuery ne sont pas plus comparable que Cocoa et la <em>standard library</em> en...</p></div></hr> +<p>Avant de commencer, je dois dire que je sais que Cappuccino et jQuery ne sont pas plus comparable que Cocoa et la <em>standard library</em> en C++. L’un est fait pour créer des interfaces util...</p></div></hr> tag:yannesposito.com,2010-06-19:/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/ @@ -730,14 +763,28 @@ En réalité, ce qui ralenti le rendu de ce site est par ordre d&rsq yannesposito.com - <p>Voici une façon simple et rapide pour faire des popups avec jQuery.</p> + <p>Voici une façon simple et rapide pour faire des popups avec jQuery.</p> -<div class="code"><div class="file"><a href="/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js"> &#x27A5; essai.js </a></div><div class="withfile"> -<pre class="twilight"> -<span class="Comment"><span class="Comment">//</span> --- code popup ---</span> -<span class="Storage">function</span> <span class="Entity">openPopup</span>() { - <span class="Keyword">$</span>(<span class="Variable">this</span>).clone(<span class="Constant">false</span>).appendTo(<span class="Keyword">$</span>(<span class="String"><span class="String">&quot;</span>#_code<span class="String">&quot;</span></span>)); - <span class="Keyword">$</span>(<span class="String"><span class="String">&quot;</span>#_code</span></pre></div></div> +<div class="codefile"><a href="/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/code/essai.js">&#x27A5; essai.js</a></div> + +<pre><code class="javascript">// --- code popup --- +function openPopup() { + $(this).clone(false).appendTo($("#_code")); + $("#_code").show(); +} + +function closePopup() { + $("#_code").html(""); + $("#_code").hide(); +} + +function initCode() { + $(".code").click(openPopup); + $(".code").css({cursor: "pointer"}); + $('body').append('&lt;div id="_code"&gt;&lt;/div&gt;'); + $('#_code').css( { 'text-align': "justify", position: "fixed", + left:0, top:0, width: "100%", height: "100%", + "background-color": "rgba(0, 0, 0, 0.8)", 'z-i...</code></pre> tag:yannesposito.com,2010-06-17:/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/ @@ -749,16 +796,23 @@ En réalité, ce qui ralenti le rendu de ce site est par ordre d&rsq yannesposito.com - <p>Voici un moyen très simple de ne plus être comptabilisé dans les visites de son propre site. -Tout d&rsquo;abord, vous devriez jeter un coup d&rsquo;œil sur comment <a href="/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics">je gère les systèmes de récupération de statistiques</a>. + <p>Voici un moyen très simple de ne plus être comptabilisé dans les visites de son propre site. +Tout d’abord, vous devriez jeter un coup d’œil sur comment <a href="/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics">je gère les systèmes de récupération de statistiques</a>. Je centralise tout dans un seul fichier javascript ce qui facilite le travail.</p> -<p>Cette méthode nécessite l&rsquo;utilisation de <code>jquery-cookie</code>.</p> +<p>Cette méthode nécessite l’utilisation de <code>jquery-cookie</code>.</p> -<p>Avant de comptabiliser les visites, je vérifie que la clé <code>admin</code> n&rsquo;est pas utilisée dans mes cookies.</p> +<p>Avant de comptabiliser les visites, je vérifie que la clé <code>admin</code> n’est pas utilisée dans mes cookies.</p> -<pre class="twilight"> - <span class="Storage">var</span> admin <span class="Keyword">=</span> <span class="Keyword">$</span>.<span class="SupportConstant">cookie</span>(</pre> +<pre><code class="javascript"> var admin = $.cookie('admin'); + if (! admin) { + // put your analytics code here + } else { + console.log("[WARNING] you're HIDDEN to analytics"); + } +</code></pre> + +<p>et il...</p> tag:yannesposito.com,2010-06-17:/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/ @@ -770,16 +824,26 @@ Je centralise tout dans un seul fichier javascript ce qui facilite le travail.&l yannesposito.com - <p>Voici comment analyser tous les clics que font vos utilisateurs sur votre blog en incluant google analytics de façon asynchrone.</p> + <p>Voici comment analyser tous les clics que font vos utilisateurs sur votre blog en incluant google analytics de façon asynchrone.</p> -<p>Dans le html, il faut utiliser <a href="http://jquery.com">jQuery</a> et un fichier que j&rsquo;ai appelé <code>yga.js</code>&nbsp;:</p> +<p>Dans le html, il faut utiliser <a href="http://jquery.com">jQuery</a> et un fichier que j’ai appelé <code>yga.js</code>&nbsp;:</p> -<pre class="twilight"> -<span class="EmbeddedSource"> <span class="EmbeddedSource">&lt;</span><span class="MetaTagInline">script</span> <span class="MetaTagInline">type</span>=<span class="String"><span class="String">&quot;</span>text/javascript<span class="String">&quot;</span></span> <span class="MetaTagInline">src</span>=<span class="String"><span class="String">&quot;</span>jquery.js<span class="String">&quot;</span></span><span class="EmbeddedSource">&gt;</span><span class="EmbeddedSource">&lt;/</span></span></pre> +<pre><code class="html"> &lt;script type="text/javascript" src="jquery.js"&gt;&lt;/script&gt; + &lt;script type="text/javascript" src="yga.js"&gt;&lt;/script&gt; +</code></pre> + +<p>Voici le contenu du fichier <code>yga.js</code>&nbsp;:</p> + +<div class="codefile"><a href="/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/code/yga.js">&#x27A5; yga.js</a></div> + +<pre><code class="javascript">$(document).ready( function() { + // add an event to all link for google analytics + $('a').click(function () { +...</code></pre> tag:yannesposito.com,2010-06-15:/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/ - Récupérez mon système de blog + Récupérez mon système de blog 2010-06-15T08:56:32Z 2010-06-15T08:56:32Z @@ -787,21 +851,22 @@ Je centralise tout dans un seul fichier javascript ce qui facilite le travail.&l yannesposito.com - <p>J&rsquo;ai publié une version <em>light</em> de mon système de blog hier soir. Par <em>light</em> il faut comprendre avec un CSS plus épuré et plus portable (sans les bords ronds). -Vous pouvez le récupérer sur <a href="http://github.com/yogsototh/nanoc3_blog">github.com</a>.</p> + <p>J’ai publié une version <em>light</em> de mon système de blog hier soir. Par <em>light</em> il faut comprendre avec un CSS plus épuré et plus portable (sans les bords ronds). +Vous pouvez le récupérer sur <a href="http://github.com/yogsototh/nanoc3_blog">github.com</a>.</p> -<p>Que pouvez-vous attendre de ce système de blog&nbsp;?</p> +<p>Que pouvez-vous attendre de ce système de blog&nbsp;?</p> <ul> - <li>Tous les avantages liés à <a href="http://nanoc.stoneship.org">nanoc</a>&nbsp;;</li> - <li>Facilité de la gestion de plusieurs langues&nbsp;;</li> + <li>Tous les avantages liés à <a href="http://nanoc.stoneship.org">nanoc</a>&nbsp;;</li> + <li>Facilité de la gestion de plusieurs langues&nbsp;;</li> <li>coloration syntaxique des codes sources pour la plupart des languages&nbsp;;</li> - <li>commentaires gérés avec <a href="http://intensedebate.org">intenseDebate</a> de façon asynchrone&nbsp;;</li> - <li>très portable avec ou sans javascript, XHTML Strict 1.0 / CSS3&nbsp;;</li></ul> + <li>commentaires gérés avec <a href="http://intensedebate.org">intenseDebate</a> de façon asynchrone&nbsp;;</li> + <li>très portable avec ou sans javascript, XHTML Strict 1.0 / CSS3&nbsp;;</li> + <li>écrivez v...</li></ul> tag:yannesposito.com,2010-06-14:/Scratch/fr/blog/2010-06-14-multi-language-choices/ - choix liés à l'écriture dans plusieurs langues + choix liés à l'écriture dans plusieurs langues 2010-06-14T14:14:57Z 2010-06-14T14:14:57Z @@ -809,13 +874,13 @@ Vous pouvez le récupérer sur <a href="http://github.com/yogsototh/n yannesposito.com - <p>Je traduis la plupart de mes articles pour qu&rsquo;ils soient disponibles en français et en anglais. -La façon que l&rsquo;on m&rsquo;a conseillé était d&rsquo;avoir un fichier par langue. En général ça donne ça.</p> + <p>Je traduis la plupart de mes articles pour qu’ils soient disponibles en français et en anglais. +La façon que l’on m’a conseillé était d’avoir un fichier par langue. En général ça donne ça.</p> <pre class="twilight"> Bonjour, -voici un exemple de texte en français. +voici un exemple de texte en français. [image](url) </pre> @@ -826,12 +891,12 @@ here is an example of english text. [image](url) </pre> -<p>Cette façon de traduire vous impose une certaine façon de traduire. -D&rsquo;abord écrire entièrement le texte dans une langue, +<p>Cette façon de traduire vous impose une certaine façon de traduire. +D’abord écrire entièrement le texte dans une langue, puis copier le fichier et enfin retraduire dans une nouvelle langue.</p> -<p>Le problème, c&rsquo;est que très souvent, les articles ont des parties communes non négligeables. Par exemple, les images, les codes sources, etc&hellip; -Lorsque je m&...</p> +<p>Le problème, c’est que très souvent, les articles ont des parties communes non négligeables. Par exemple, les images, les codes sources, etc… +Lorsque je m’aperçoit que j’ai fait une erreur dans ces parties communes...</p> tag:yannesposito.com,2010-05-24:/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ @@ -847,28 +912,29 @@ Lorsque je m&...</p> <div class="intro"> -<p><abbr title="Trop Long À Lire"><span class="sc">tlàl</span></abbr>&nbsp;:</p> +<p><abbr title="Trop Long À Lire"><span class="sc">tlàl</span></abbr>&nbsp;:</p> <ul> - <li>J&rsquo;ai essayé de programmer un simple filtre&nbsp;;</li> - <li>J&rsquo;ai été bloqué pendant deux jours&nbsp;;</li> - <li>J&rsquo;ai arrêté de penser comme un robot&nbsp;;</li> - <li>J&rsquo;ai utilisé un papier et un stylo&nbsp;;</li> - <li>J&rsquo;ai fait un peu de maths&nbsp;;</li> - <li>J&rsquo;ai résolu le problème en 10 minutes&nbsp;;</li> - <li>Conclusion: Pragmatisme n&rsquo;est pas&nbsp;: &laquo;n&rsquo;utilisez jamais la théorie&raquo;. + <li>J’ai essayé de programmer un simple filtre&nbsp;;</li> + <li>J’ai été bloqué pendant deux jours&nbsp;;</li> + <li>J’ai arrêté de penser comme un robot&nbsp;;</li> + <li>J’ai utilisé un papier et un stylo&nbsp;;</li> + <li>J’ai fait un peu de maths&nbsp;;</li> + <li>J’ai résolu le problème en 10 minutes&nbsp;;</li> + <li>Conclusion: Pragmatisme n’est pas&nbsp;: «n’utilisez jamais la théorie». </div> </ul> -<h2 id="rsum-plus-long-que-le--abbr-titletrop-long--liresctllscabbr">Résumé (plus long que le <abbr title="Trop Long À Lire"><span class="sc">tlàl</span></abbr>)</h2> +<h2 id="rsum-plus-long-que-le--abbr-titletrop-long--liresctllscabbr">Résumé (plus long que le <abbr title="Trop Long À Lire"><span class="sc">tlàl</span></abbr>)</h2> -<p>Je devais résoudre un...</p></div> +<p>Je devais résoudre un problème à mon travail. Au début cela +semblait assez facile. J’ai donc comme...</p></div> tag:yannesposito.com,2010-05-19:/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ - Comment réparer un XML coupé ? + Comment réparer un XML coupé ? 2010-05-19T20:20:34Z 2010-05-19T20:20:34Z @@ -876,17 +942,31 @@ Lorsque je m&...</p> yannesposito.com - <p>Sur ma page d&rsquo;accueil vous pouvez voir la liste des mes derniers articles avec le début de ceux-ci. Pour arriver à faire ça, j&rsquo;ai besoin de couper le code XHTML de mes pages en plein milieu. Il m&rsquo;a donc fallu trouver un moyen de les réparer.</p> + <p>Sur ma page d’accueil vous pouvez voir la liste des mes derniers articles avec le début de ceux-ci. Pour arriver à faire ça, j’ai besoin de couper le code XHTML de mes pages en plein milieu. Il m’a donc fallu trouver un moyen de les réparer.</p> <p>Prenons un exemple&nbsp;:</p> -<pre class="twilight"> -<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span> <span class="MetaTagAll">class</span>=<span class="String"><span class="String">&quot;</span>corps<span class="String">&quot;</span></span><span class="MetaTagAll">&gt;</span></span> - <span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span> <span class="MetaTagAll">class</span>=<span class="String"><span class="String">&quot;</span></span></span></pre> +<pre><code class="html">&lt;div class="corps"&gt; + &lt;div class="intro"&gt; + &lt;p&gt;Introduction&lt;/p&gt; + &lt;/div&gt; + &lt;p&gt;The first paragraph&lt;/p&gt; + &lt;img src="/img/img.png" alt="an image"/&gt; + &lt;p&gt;Another long paragraph&lt;/p&gt; +&lt;/div&gt; +</code></pre> + +<p>Après avoir coupé, j’obtiens&nbsp;:</p> + +<pre><code class="html">&lt;div class="corps"&gt; + &lt;div class="intro"&gt; + &lt;p&gt;Introduction&lt;/p&gt; + &lt;/div&gt; + &lt;p&gt;The first para...</code></pre> tag:yannesposito.com,2010-05-17:/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/ - Je reviens à la vie ! + Je reviens à la vie ! 2010-05-17T11:25:51Z 2010-05-17T11:25:51Z @@ -894,18 +974,20 @@ Lorsque je m&...</p> yannesposito.com - <p>Bonjour à tous&nbsp;!</p> + <p>Bonjour à tous&nbsp;!</p> <blockquote cite="http://www.madore.org/~david/weblog/2010-05.html#d.2010-05-12.1752"> - <p>&hellip;plus on retarde quelque chose, plus il devient difficile de s&rsquo;y mettre&hellip;</p> + <p>…plus on retarde quelque chose, plus il devient difficile de s’y mettre…</p> </blockquote> -<p>Je devais écrire d&rsquo;autres articles pour ce blog. J&rsquo;ai noté plein d&rsquo;idées dans mes <em>todolist</em>. Mais j&rsquo;avais pas mal d&rsquo;autres choses à faire. Et jusqu&rsquo;ici, j&rsquo;ai toujours dit &laquo;je le ferai plus tard&raquo;. Ce qui m&rsquo;a fait agir, c&rsquo;est la petite réflexion que j&rsquo;avais lu une fois. -&gt; Arrétez d&rsquo;écrire des <code>TODO</code> dans votre code est faites le maintenant&nbsp;!<br /> -&gt; Vous serez surpris de l&rsquo;efficacité de cette mesure.</p> +<p>Je devais écrire d’autres articles pour ce blog. J’ai noté plein d’idées dans mes <em>todolist</em>. Mais j’avais pas mal d’autres choses à faire. Et jusqu’ici, j’ai toujours dit «je le ferai plus tard». Ce qui m’a fait agir, c’est la petite réflexion que j’avais lu une fois. +&gt; Arrétez d’écrire des <code>TODO</code> dans votre code est faites le maintenant&nbsp;!<br /> +&gt; Vous serez surpris de l’efficacité de cette mesure.</p> -<p>En résumé&nbsp;: -&gt; </p></p> +<p>En résumé&nbsp;: +&gt; <strong>Just do it!</strong> ou <strong>Juste fait le</strong> comme auraient dit les nuls.</p> + +<p>Finallement j’écri...</p></p> tag:yannesposito.com,2010-03-23:/Scratch/fr/blog/2010-03-23-Encapsulate-git/ @@ -919,18 +1001,19 @@ Lorsque je m&...</p> <p><span class="intro"> Voici une solution pour conserver des branches divergentes avec <code>git</code>. -Parce qu&rsquo;il est facile de <em>merger</em> par erreur, je propose un script qui encapsule le comportement de <code>git</code> pour interdire certains <em>merges</em> dangereux. Mais qui permet aussi de faire des merges en cascades de la racines vers les autres branches. +Parce qu’il est facile de <em>merger</em> par erreur, je propose un script qui encapsule le comportement de <code>git</code> pour interdire certains <em>merges</em> dangereux. Mais qui permet aussi de faire des merges en cascades de la racines vers les autres branches. </span></p> -<h2 id="se-prmunir-contre-les-erreurs">Se prémunir contre les erreurs</h2> +<h2 id="se-prmunir-contre-les-erreurs">Se prémunir contre les erreurs</h2> -<p>Je travaille sur un projet dans lequel certaines de mes branches <code>git</code> doivent rester divergentes. Et les divergences devraient aller en s&rsquo;accentuant.</p> +<p>Je travaille sur un projet dans lequel certaines de mes branches <code>git</code> doivent rester divergentes. Et les divergences devraient aller en s’accentuant.</p> -<p>J&rsquo;utilise aussi certaines branches qui contiennent la partie commune de ces projets.</p> +<p>J’utilise aussi certaines branches qui contiennent la partie commune de ces projets.</p> -<p>Disons que j&rsquo;ai les branches&nbsp;:</p> +<p>Disons que j’ai les branches&nbsp;:</p> -... +<ul> + <li>master: commun...</li></ul> tag:yannesposito.com,2010-03-22:/Scratch/fr/blog/2010-03-22-Git-Tips/ @@ -942,35 +1025,40 @@ Parce qu&rsquo;il est facile de <em>merger</em> par erreur, je p yannesposito.com - <h2 class="first" id="cloner-de-github--travers-un-pare-feu">Cloner de github à travers un pare-feu</h2> + <h2 class="first" id="cloner-de-github--travers-un-pare-feu">Cloner de github à travers un pare-feu</h2> -<p>La façon standard:</p> +<p>La façon standard:</p> -<div><pre class="twilight"> -git clone git@github.com:yogsototh/project.git -</pre></div> +<div> + +<pre><code class="zsh">git clone git@github.com:yogsototh/project.git +</code></pre> + +</div> <p>En utilisant le port HTTPS&nbsp;:</p> -<div><pre class="twilight"> -git clone git+ssh://git@github.com:443/yogsototh/project.git -</pre></div> +<div> + +<pre><code class="zsh">git clone git+ssh://git@github.com:443/yogsototh/project.git +</code></pre> + +</div> <h2 id="cloner-toutes-les-branches">Cloner toutes les branches</h2> -<p><code>git clone</code> peut seulement récuper la branche <code>master</code>.</p> +<p><code>git clone</code> peut seulement récuper la branche <code>master</code>.</p> -<p>Si vous n&rsquo;avez pas beaucoup de branches, vous pouvez simplement les clone le project et ensuite pour chacune d&rsquo;entre elle lancer la commande suivante&nbsp;:</p> +<p>Si vous n’avez pas beaucoup de branches, vous pouvez simplement les clone le project et ensuite pour chacune d’entre elle lancer la commande suivante&nbsp;:</p> -<div><pre class="twilight"> -git branch --track local_branch remote_branch -</pre></div> +<div> -<p>par ...</p> +<pre><code class="zsh">git branch --track local_branch remote_branch +</code></pre></div> tag:yannesposito.com,2010-02-23:/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/ - Quand se passer des expressions régulières ? + Quand se passer des expressions régulières ? 2010-02-23T08:09:52Z 2010-02-23T08:09:52Z @@ -978,18 +1066,31 @@ git branch --track local_branch remote_branch yannesposito.com - <p>Les expressions régulières sont très utiles. Cependant, elles ne sont pas toujours la meilleure manière d&rsquo;aborder certain problème autour des chaines de caractères. + <p>Les expressions régulières sont très utiles. Cependant, elles ne sont pas toujours la meilleure manière d’aborder certain problème autour des chaines de caractères. Et surtout quand les transformations que vous voulez accomplir sont simples.</p> -<p>Je voulais savoir comment récupérer le plus vite possible l&rsquo;extension d&rsquo;un nom de fichier. Il y a trois manière naturelle d&rsquo;accomplir celà&nbsp;:</p> +<p>Je voulais savoir comment récupérer le plus vite possible l’extension d’un nom de fichier. Il y a trois manière naturelle d’accomplir celà&nbsp;:</p> -<div><pre class="twilight"> -<span class="Comment"><span class="Comment">#</span> regexp</span> -str.<span class="Entity">match</span>(<span class="StringRegexp"><span class="StringRegexp">/</span></span><span class="StringRegexp"><span class="StringRegexp"><span class="StringRegexp">[</span>^.<span class="StringRegexp">]</span></span>*$</span></pre></div> +<div> + +<pre><code class="ruby"># regexp +str.match(/[^.]*$/); +ext=$&amp; + +# split +ext=str.split('.')[-1] + +# File module +ext=File.extname(str) +</code></pre> + +</div> + +<p>A première vue, je pensais que l’expression régulière serait plus rapide que le <code>split</code> parce qu’il pouvait y avoir plusieurs de <code>.</code> dans un nom de fichier. Mais la majorité du temps il n’y a qu’un seul ...</p> tag:yannesposito.com,2010-02-18:/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/ - découper un fichier par mots clés + découper un fichier par mots clés 2010-02-18T13:29:14Z 2010-02-18T13:29:14Z @@ -997,22 +1098,39 @@ str.<span class="Entity">match</span>(<span class="StringRegexp"& yannesposito.com - <p>Assez bizarrement, je n&rsquo;ai trouvé aucun outil UNIX pour découper un fichier par mot clé. -Alors j&rsquo;en ai fait un en <code>awk</code>. Je le met ici principalement pour moi, mais ça peut toujours servir à quelqu&rsquo;un d&rsquo;autre. -Le code suivant découpe un fichier pour chacune de ses ligne contenant le mot <code>UTC</code>.</p> + <p>Assez bizarrement, je n’ai trouvé aucun outil UNIX pour découper un fichier par mot clé. +Alors j’en ai fait un en <code>awk</code>. Je le met ici principalement pour moi, mais ça peut toujours servir à quelqu’un d’autre. +Le code suivant découpe un fichier pour chacune de ses ligne contenant le mot <code>UTC</code>.</p> -<div><pre class="twilight"> -<span class="Comment"><span class="Comment">#</span>!/usr/bin/env awk</span> -<span class="Entity">BEGIN</span>{i=0;} -<span class="StringRegexp"><span class="StringRegexp">/</span>UTC<span class="StringRegexp">/</span></span> { +<div> + +<pre><code class="perl">#!/usr/bin/env awk +BEGIN{i=0;} +/UTC/ { i+=1; - FIC=<span class="SupportFunction">sprintf</span>(<span class="String"><span class="String">&quot;</span>fic.%03d<span class="String">&quot;</span></span>,i); + FIC=sprintf("fic.%03d",i); } -{</pre></div> +{print $0&gt;&gt;FIC} +</code></pre> + +</div> + +<p>En réalité, j’avais besoin de cet outils pour avoir un fichier par jour. Chaque ligne contenant UTC ayant le format suivant&nbsp;:</p> + +<pre class="twilight"> +Mon Dec 7 10:32:30 UTC 2009 +</pre> + +<p>J’en suis finallement arrivé au code suivant&nbsp;:</p> + +<div> + +<pre><code class="perl">#!/usr/bin/env awk +B...</code></pre></div> tag:yannesposito.com,2010-02-16:/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/ - Tout sauf quelquechose en expression régulière. + Tout sauf quelquechose en expression régulière. 2010-02-16T08:33:21Z 2010-02-16T08:33:21Z @@ -1020,8 +1138,8 @@ Le code suivant découpe un fichier pour chacune de ses ligne contenant le m yannesposito.com - <p>Dans mon <a href="previouspost">précédent article</a> j&rsquo;ai donné certaines astuces pour matcher &lsquo;tout sauf quelque chose&rsquo;. De la même manière, un truc pour matcher la chaine de caractère la plus petite possible. -Disons que vous voulez matcher la chaine de caractère entre &lsquo;a&rsquo; et &lsquo;b&rsquo;. Par exemple, vous voulez matcher&nbsp;:</p> + <p>Dans mon <a href="previouspost">précédent article</a> j’ai donné certaines astuces pour matcher ‘tout sauf quelque chose’. De la même manière, un truc pour matcher la chaine de caractère la plus petite possible. +Disons que vous voulez matcher la chaine de caractère entre ‘a’ et ‘b’. Par exemple, vous voulez matcher&nbsp;:</p> <pre class="twilight"> a.....<span class="Constant"><strong>a......b</strong></span>..b..a....<span class="Constant"><strong>a....b</strong></span>... @@ -1034,11 +1152,11 @@ a.....<span class="Constant"><strong>a......b</strong></spa <span class="Constant"><strong>a.....a......b..b..a....a....b</strong></span>... </pre> -<p>La première erreur vient de l&rsquo;utilisation du <em>terrible</em> </p> +<p>La première erreur vient de l’utilisation du <em>terrible</em> <code>.*</code>. Parce que vous allez matcher la chaîne de...</p> tag:yannesposito.com,2010-02-15:/Scratch/fr/blog/2010-02-15-All-but-something-regexp/ - Expression régulière pour tout sauf quelquechose + Expression régulière pour tout sauf quelquechose 2010-02-15T09:16:12Z 2010-02-15T09:16:12Z @@ -1046,16 +1164,25 @@ a.....<span class="Constant"><strong>a......b</strong></spa yannesposito.com - <p>Parfois vous ne pouvez simplement pas écrire&nbsp;:</p> + <p>Parfois vous ne pouvez simplement pas écrire&nbsp;:</p> -<div><pre class="twilight"> -<span class="Keyword">if</span> str.<span class="Entity">match</span>(regexp) <span class="Keyword">and</span> - <span class="Keyword">not</span> str.<span class="Entity">match</span>(other_regexp) +<div> + +<pre><code class="ruby">if str.match(regexp) and + not str.match(other_regexp) do_something -</pre></div> +</code></pre> -<p>et vous devez obtenir le même comportement avec seulement une expression régulière. Le problème c&rsquo;est que le complémentaire des régulier n&rsquo;est pas régulier. Celà peut s&rsquo;avérer impossible.</p> +</div> -<p>Cependant, pour certaines expressions ce peut être possible<sup><a href="#note1">&dagger;</a></sup>. Disons que vous souhaitez <em>matcher</em> toutes les lignes contenant le mot <code>bull</code>, mais que vous ne souhaitez pas matcher <code>bu...</code></p> +<p>et vous devez obtenir le même comportement avec seulement une expression régulière. Le problème c’est que le complémentaire des régulier n’est pas régulier. Celà peut s’avérer impossible.</p> + +<p>Cependant, pour certaines expressions ce peut être possible<sup><a href="#note1">†</a></sup>. Disons que vous souhaitez <em>matcher</em> toutes les lignes contenant le mot <code>bull</code>, mais que vous ne souhaitez pas matcher <code>bullshit</code>. Voici une façon sympa d’y arriver&nbsp;:</p> + +<div> + +<pre><code class="ruby"># matcher toute les chaines qui +# matchent 'bull' (bullshit compris) +/bull...</code></pre></div> diff --git a/output/Scratch/fr/blog/index.html b/output/Scratch/fr/blog/index.html index fdf517131..0b4280e5b 100644 --- a/output/Scratch/fr/blog/index.html +++ b/output/Scratch/fr/blog/index.html @@ -220,7 +220,7 @@ Les 18 derniers articles
    -
    Title image + @@ -246,7 +246,7 @@ Les 18 derniers articles $('.tag.selected').removeClass('selected'); $('#tag_'+id).addClass('selected'); } -

    analyser

      +

      analyser

      expressions régulières

        +

      expressions régulières

      mathématiques

        +

      mathématiques

      Réalité alternée

        +

      Réalité alternée

      securité

        +

      securité

      théorie

        +

      théorie

      - modifié le : 24/04/2012 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/blog/mvc/index.html b/output/Scratch/fr/blog/mvc/index.html index d302f5146..d8bcffe8b 100644 --- a/output/Scratch/fr/blog/mvc/index.html +++ b/output/Scratch/fr/blog/mvc/index.html @@ -10,6 +10,7 @@ + @@ -17,6 +18,8 @@ + + @@ -54,17 +57,17 @@

      Why This article and for whom?

      -

      Many website explaining how MVC works. But I can’t found one who explain why.

      +

      Many website explaining how MVC works. But I can’t found one who explain why.

      -

      I have difficulties to obey some principle don’t know why I should use it. And something better than the:

      +

      I have difficulties to obey some principle don’t know why I should use it. And something better than the:

      -

      “Smarter people than you decided you have to do so”

      +

      “Smarter people than you decided you have to do so”

      This article is for people who like me want to understand the real motivation of using an MVC architecture and which advantage it gives you.

      -

      Let’s start making a project from scratch pretending we don’t know anything about the MVC architecture. Then let see how we’ll naturally derive to an MVC architecture.

      +

      Let’s start making a project from scratch pretending we don’t know anything about the MVC architecture. Then let see how we’ll naturally derive to an MVC architecture.

      → Next

      diff --git a/output/Scratch/fr/blog/programming-language-experience/index.html b/output/Scratch/fr/blog/programming-language-experience/index.html index e85e27888..a7a1403bb 100644 --- a/output/Scratch/fr/blog/programming-language-experience/index.html +++ b/output/Scratch/fr/blog/programming-language-experience/index.html @@ -12,6 +12,7 @@ + @@ -19,6 +20,8 @@ + + @@ -59,63 +62,61 @@
      -tlàl : Mon avis court et hautement subjectif concernant les différents languages de programmations que j’ai utilisé. +tlàl : Mon avis court et hautement subjectif concernant les différents languages de programmations que j’ai utilisé.

      BASIC

      -

      Title image

      +

      Title image

      Ah ! Le language de mes premiers programmes ! Je devais avoir 10-11 ans. Sous MO5, Amstrad CPC 6128 et même Atari STe. Le langage des GOTOs. -Je suis empleint de nostalgie rien que d’y penser. -C’est à peu prêt le seul intérêt de ce langage.

      +Je suis empleint de nostalgie rien que d’y penser. +C’est à peu prêt le seul intérêt de ce langage.

      -

      Aujourd’hui ce langage est tombé en désuétude. -Ce n’est ni un bon langage pour apprendre, ni un bon langage pour faire de vrai programmes. +

      Aujourd’hui ce langage est tombé en désuétude. +Ce n’est ni un bon langage pour apprendre, ni un bon langage pour faire de vrai programmes. Même si quelques années plus tard, je me remettais à programmer dans un basic avec un compilateur qui pourrait lui redonner vie. -Je m’en était servi pour faire un livre dont vous êtes le héro :-).

      +Je m’en était servi pour faire un livre dont vous êtes le héro :-).

      -
      -READY
      -10 PRINT "HELLO WORLD!"
      +
      READY
      +10 PRINT "HELLO WORLD!"
       20 GOTO 10
       RUN
      -
      +
      -

      Je m’en souviens aussi pour avoir copier des codes de jeux vidéo à partir de magasines. +

      Je m’en souviens aussi pour avoir copier des codes de jeux vidéo à partir de magasines. La plupart des lignes ressemblaient à

      -
      -3110 DATA FA,01,FF,FF,FF,FF,00,23,22,43,DA,DE,EE,FF,FF,FF,00,03,4A,F2
      -
      +
      3110 DATA FA,01,FF,FF,FF,FF,00,23,22,43,DA,DE,EE,FF,FF,FF,00,03,4A,F2
      +
      -

      Quel plaisir c’était !

      +

      Quel plaisir c’était !

      -

      Dragon fractal -Toujours lors que j’avais 10 ans, on pouvait faire de petits programmes sympathiques.

      +

      Dragon fractal

      -

      Je me souviens que lors du chargement de l’application logo on avait droit à de la musique de Bach.

      +

      Toujours lors que j’avais 10 ans, on pouvait faire de petits programmes sympathiques.

      + +

      Je me souviens que lors du chargement de l’application logo on avait droit à de la musique de Bach.

      Oui, il fallait charger le programme en mémoire avec une cassette. -Et elle ne faisait pas les ‘Krrrkrr csssss krrrr’.

      +Et elle ne faisait pas les ‘Krrrkrr csssss krrrr’.

      -

      Je l’avais utilisé sans les boucles. -Des années plus tard, je le réutiliser pour faire de l’initiation à l’informatique à mes étudiants de DEUG MIAS première année. -Il s’est en fait révélé très utile. -Grace à lui, faire des fractales se révèle être un jeu d’enfant, au sens litéral. +

      Je l’avais utilisé sans les boucles. +Des années plus tard, je le réutiliser pour faire de l’initiation à l’informatique à mes étudiants de DEUG MIAS première année. +Il s’est en fait révélé très utile. +Grace à lui, faire des fractales se révèle être un jeu d’enfant, au sens litéral. Je ne peux que conseiller ce langage pour apprendre à programmer et aussi pour le fun.

      -

      Voici un exemple de code et le résultat est la jolie fractale ‘dragon’.

      +

      Voici un exemple de code et le résultat est la jolie fractale ‘dragon’.

      -
      -HIDETURTLE
      +
      HIDETURTLE
       
       PENUP
       SETXY -200 0
      @@ -124,10 +125,10 @@ PENDOWN
       
       to dragon :degree :size
           setpensize 1
      -    if :size>5  [setpensize 2]
      -    if :size>10 [setpensize 3]
      -    if :size>20 [setpensize 4]
      -    if :size>40 [setpensize 5]
      +    if :size>5  [setpensize 2]
      +    if :size>10 [setpensize 3]
      +    if :size>20 [setpensize 4]
      +    if :size>40 [setpensize 5]
           ifelse :degree=0 [ 
               fd :size 
           ][
      @@ -139,20 +140,20 @@ to dragon :degree :size
       end
       
       dragon 6 3000
      -
      +

      Pascal

      -

      L’éternel numéro 2.

      +

      L’éternel numéro 2.

      -

      J’ai dû apprendre à programmer en Pascal aux alentour de 15 ans et je l’ai aussi réutiliser un peit peu en faculté. +

      J’ai dû apprendre à programmer en Pascal aux alentour de 15 ans et je l’ai aussi réutiliser un peit peu en faculté. Je dois avouer, que je le trouve inférieur au C en tous points. -J’ai fait pas mal de chose avec ça, comme des algorithmes de graphes, des algorithmes de tri, et même un peu d’intelligence artificielle comme des algorithmes génétiques. +J’ai fait pas mal de chose avec ça, comme des algorithmes de graphes, des algorithmes de tri, et même un peu d’intelligence artificielle comme des algorithmes génétiques. Mais je préfère largement le C.

      C

      -

      Pointer representation from Dancing links

      +

      Pointer representation from Dancing links

      Le langage des pointeurs

      @@ -166,215 +167,214 @@ Si vous voulez avoir du code de bonne qualité, alors apprendre le C est quasi-o En particulier, (la majorité du temps). Il y a une relation linéaire entre la taille du code en C et de son résultat compilé en assembleur.

      -

      Ça signifie qu’à chaque fois que vous écrivez une ligne de C, il ne va pas se passer de choses toutes bizarres comme lancer un algorithme qui va prendre deux plombes.

      +

      Ça signifie qu’à chaque fois que vous écrivez une ligne de C, il ne va pas se passer de choses toutes bizarres comme lancer un algorithme qui va prendre deux plombes.

      -

      Il est très proche de la machine tout en ayant une abstraction suffisante pour ne pas être “trop” désagréable.

      +

      Il est très proche de la machine tout en ayant une abstraction suffisante pour ne pas être “trop” désagréable.

      -

      J’ai fait beaucoup de choses avec. -Tous les algorithmes de tri, des algorithmes d’intelligence artificielle (résolution de SAT3), du système, du réseau etc… -Bref il est versatile, et on ne peut pas dire que l’on sait programmer si on ne s’est jamais mis à programmer sérieusement en C.

      +

      J’ai fait beaucoup de choses avec. +Tous les algorithmes de tri, des algorithmes d’intelligence artificielle (résolution de SAT3), du système, du réseau etc… +Bref il est versatile, et on ne peut pas dire que l’on sait programmer si on ne s’est jamais mis à programmer sérieusement en C.

      ADA

      Le langage super-propre.

      -

      J’avais bien aimé ADA, mais j’avoue que ça n’a duré que le temps d’un semestre de cours. -Peut-être qu’un jour je m’y remettrai. -Disons qu’il est assez vieux et qu’il a inspiré la plupart des concepts objets.

      +

      J’avais bien aimé ADA, mais j’avoue que ça n’a duré que le temps d’un semestre de cours. +Peut-être qu’un jour je m’y remettrai. +Disons qu’il est assez vieux et qu’il a inspiré la plupart des concepts objets.

      Les langages orientés objets

      -

      Bon, oui, le Pascal, le C, le Basic (fortran, Cobol et autres) étaient tous des langages impératifs, sans notion d’objets.

      +

      Bon, oui, le Pascal, le C, le Basic (fortran, Cobol et autres) étaient tous des langages impératifs, sans notion d’objets.

      -

      En gros, il n’y avait pas d’aide pour structurer votre code.

      +

      En gros, il n’y avait pas d’aide pour structurer votre code.

      -

      Alors, pour aider à limiter le nombre de bug, en particulier pour la création de très gros programmes, on s’est mis à réfléchir à la meilleure façon d’organiser du code d’ordinateur. +

      Alors, pour aider à limiter le nombre de bug, en particulier pour la création de très gros programmes, on s’est mis à réfléchir à la meilleure façon d’organiser du code d’ordinateur. À la fin, ça à donné la programmation orienté objet. Et donc les langages comme le C manquaient de système pour aider au développement orienté objet. -Attention, la programmaiton orienté objet n’est pas la panacée. Combien de programme utilisez-vous qui n’ont pas de bug ? +Attention, la programmaiton orienté objet n’est pas la panacée. Combien de programme utilisez-vous qui n’ont pas de bug ? Et ça ne convient pas à tous les type de problème. Mais pour faire une application banquaire, un système de gestion des stocks, des clients ou des archives. -C’est-à-dire un système d’information, c’est pas trop mal.

      +C’est-à-dire un système d’information, c’est pas trop mal.

      Donc les langages orientés objets se sont mis à fleurir.

      C++

      -

      Messy router

      +

      Messy router

      Le malpropre

      -

      Et oui l’industrie voulait un langage objet, mais elle n’était pas prête à mettre à la poubelle tout ses codes en C. +

      Et oui l’industrie voulait un langage objet, mais elle n’était pas prête à mettre à la poubelle tout ses codes en C. La solution, prendre C et lui rajouter une couche objet. -Le problème avec C++ c’est qu’il fait trop de choses. -L’héritage multiple, des templates, etc… -Bon, je l’ai quand même choisi pour faire le plus gros programme que j’ai jamais fais lors de ma thèse. -Et je dois avouer que l’expérience m’a plu. -Le seul reproche que j’ai à faire, c’est que la STL n’était pas aussi complète que l’on aurait pu l’espérer pour un détail. +Le problème avec C++ c’est qu’il fait trop de choses. +L’héritage multiple, des templates, etc… +Bon, je l’ai quand même choisi pour faire le plus gros programme que j’ai jamais fais lors de ma thèse. +Et je dois avouer que l’expérience m’a plu. +Le seul reproche que j’ai à faire, c’est que la STL n’était pas aussi complète que l’on aurait pu l’espérer pour un détail. On ne peut pas faire de String<T> pour autre chose que des char16. Du coup, mon alphabet était limité à 216 lettres. -Hors, pour certaines application, l’alphabet doit être gigantesque.

      +Hors, pour certaines application, l’alphabet doit être gigantesque.

      -

      En conclusion je dirai que C++ est un très bon langage si vous vous fixez à l’avance un sous ensemble de ses fonctionnalités.

      +

      En conclusion je dirai que C++ est un très bon langage si vous vous fixez à l’avance un sous ensemble de ses fonctionnalités.

      Eiffel

      -

      Eiffel tower construction

      +

      Eiffel tower construction

      -

      Bon, ok c’est un très beau langage objet. +

      Bon, ok c’est un très beau langage objet. Bien plus propre que C++. -Mais, à moins que les choses aient changées, il n’est pas très populaire. -Derrière lui il n’a pas la communauté de C++. -Pour être franc, j’ai préféré travailler en C++. -J’ai menti à mes profs de l’époque pour leur faire plaisir. -Lorsqu’on viens du C, il est désagréable de changer ses habitudes.

      +Mais, à moins que les choses aient changées, il n’est pas très populaire. +Derrière lui il n’a pas la communauté de C++. +Pour être franc, j’ai préféré travailler en C++. +J’ai menti à mes profs de l’époque pour leur faire plaisir. +Lorsqu’on viens du C, il est désagréable de changer ses habitudes.

      Java

      -

      Holy Grail from the Monty Python

      +

      Holy Grail from the Monty Python

      -

      On continue vers les langages objets. Alors, à une époque où j’en ai entendu parler, c’était le Graal !

      +

      On continue vers les langages objets. Alors, à une époque où j’en ai entendu parler, c’était le Graal !

      -

      La portabilité, votre programme marchera partout. Il était orienté objet. Incrusté à l’intérieur il y avait des concepts d’architecture qui empêchent de faire n’importe quoi… Sauf que.

      +

      La portabilité, votre programme marchera partout. Il était orienté objet. Incrusté à l’intérieur il y avait des concepts d’architecture qui empêchent de faire n’importe quoi… Sauf que.

      -

      Sauf qu’il est incroyablement verbeux. -Et que les limitations sont très désagréables si on sait ce que l’on fait.

      +

      Sauf qu’il est incroyablement verbeux. +Et que les limitations sont très désagréables si on sait ce que l’on fait.

      -

      Par exemple, il n’y a pas d’héritage multiple en Java. -Ce qui est en général un choix que je trouve cohérent s’il est bien appuyé par des systèmes qui compensent ce manque. +

      Par exemple, il n’y a pas d’héritage multiple en Java. +Ce qui est en général un choix que je trouve cohérent s’il est bien appuyé par des systèmes qui compensent ce manque. En java, il existe les interfaces. -Les interfaces permettent d’ajouter des méthodes à une classe. -En aucun cas on ne peut rajouter un attribut autrement qu’en héritant. -Cet état de fait m’a vraiment géné.

      +Les interfaces permettent d’ajouter des méthodes à une classe. +En aucun cas on ne peut rajouter un attribut autrement qu’en héritant. +Cet état de fait m’a vraiment géné.

      Typiquement je faisais une GUI en Java Swing. -J’avais créé mon propre système de notification entre objets. -Au début je considérais qu’un objet ne devait envoyer des notifications qu’à un seul objet. -Ô quelle erreur lorsque je réalisais qu’il fallait non plus gérer un seul objet mais parfois plusieurs. -Je changeais mon implémentation d’interface partout, conséquence, des copier/coller dans tous les sens pour mes classes. +J’avais créé mon propre système de notification entre objets. +Au début je considérais qu’un objet ne devait envoyer des notifications qu’à un seul objet. +Ô quelle erreur lorsque je réalisais qu’il fallait non plus gérer un seul objet mais parfois plusieurs. +Je changeais mon implémentation d’interface partout, conséquence, des copier/coller dans tous les sens pour mes classes. Les copier/coller qui sont justement un problème censé être évité par les langages orientés objets.

      De plus toujours pour ma GUI, je devais évidemment gérer des threads. -Hors, il m’a fallu faire mon propre système de gestion de threads pour éviter les locks, pour les notifications (ce thread à fini, etc…). -À l’époque j’utilisais Java 1.5. +Hors, il m’a fallu faire mon propre système de gestion de threads pour éviter les locks, pour les notifications (ce thread à fini, etc…). +À l’époque j’utilisais Java 1.5. Normallement ce problème devait être réglé sur Java 1.6. -J’espère que c’est le cas, mais avoir ce type de “feature” essentielle oubliée par le langage était assez grave.

      +J’espère que c’est le cas, mais avoir ce type de “feature” essentielle oubliée par le langage était assez grave.

      -

      De même, il a fallu attendre très longtemps avant d’avoir des boucles foreach qui rendent le code bien plus lisible.

      +

      De même, il a fallu attendre très longtemps avant d’avoir des boucles foreach qui rendent le code bien plus lisible.

      Bon, après cette expérience je déconseillerai Java. -La portabilité, n’est pas si intéressante que ce qu’on pourrait croire.

      +La portabilité, n’est pas si intéressante que ce qu’on pourrait croire.

      En ce qui concerne les GUI, portable signifie interface fonctionnelle mais médiocre sur toutes les plateformes. -Quelquesoit le système d’ailleurs (wxWidget, QT, etc…). -Donc, pour des applications à distribuer à des tiers, c’est à éviter.

      +Quelquesoit le système d’ailleurs (wxWidget, QT, etc…). +Donc, pour des applications à distribuer à des tiers, c’est à éviter.

      Le système de Java est très clos. Par contre il résoud un très bon problème. Il permet à des développeurs médiocres de travailler en groupe sans faire trop de mal. -Et un bon programmeur sera tout de même capable d’y faire des choses très intéressantes. -Veuillez noter que je n’ai pas dit que les programmeurs Java sont de mauvais programmeurs, ce n’est pas ce que je pense.

      +Et un bon programmeur sera tout de même capable d’y faire des choses très intéressantes. +Veuillez noter que je n’ai pas dit que les programmeurs Java sont de mauvais programmeurs, ce n’est pas ce que je pense.

      Objective-C

      -

      Xcode Logo

      +

      Xcode Logo

      -

      Le langage que je n’ai appris et utilisé que pour faire des applications sur les plateformes d’Apple©. -J’ai appris Objective-C après Python. -Et je dois avouer que j’ai eu du mal à m’y mettre. -Je n’ai pas du tout aimé la syntaxe et pas mal d’autres détails. +

      Le langage que je n’ai appris et utilisé que pour faire des applications sur les plateformes d’Apple©. +J’ai appris Objective-C après Python. +Et je dois avouer que j’ai eu du mal à m’y mettre. +Je n’ai pas du tout aimé la syntaxe et pas mal d’autres détails. Mais ça fait parti de ces langages que plus on utilise, plus on aime. En réalité, il y a quelque chose dans ce langage qui fait que tout est bien pensé. -Mais surtout, ici, ce n’est pas le langage qui est la meilleure partie, c’est plutôt le framework Cocoa qui lui est le plus souvent associé qui est une merveille. +Mais surtout, ici, ce n’est pas le langage qui est la meilleure partie, c’est plutôt le framework Cocoa qui lui est le plus souvent associé qui est une merveille. Par rapport à tous les autres framework permettant de fabriquer des GUI, Cocoa est de très loin supérieur. Même si ça semble être des détails sur le papier, en pratique cela fait une grande différence.

      -

      Vraiment jusqu’ici, même si Objective-C reste assez bas niveau, le fait que le typage de ce langage soit dynamique est un vrai plus pour l’interface graphique. -Je ne peux que vous encourager à vous accrocher à ce langage et de faire un vrai programme avec. Vous en serez certainement plus ravi qu’il n’y parrait eu début.

      +

      Vraiment jusqu’ici, même si Objective-C reste assez bas niveau, le fait que le typage de ce langage soit dynamique est un vrai plus pour l’interface graphique. +Je ne peux que vous encourager à vous accrocher à ce langage et de faire un vrai programme avec. Vous en serez certainement plus ravi qu’il n’y parrait eu début.

      Les langages interprétés modernes

      PHP

      -

      A Jacky Touch Car

      +

      A Jacky Touch Car

      -

      Le petit langage de script que nous utilisions tous pour faire des sites web à l’époque des gifs animées !

      +

      Le petit langage de script que nous utilisions tous pour faire des sites web à l’époque des gifs animées !

      -

      Sympatique, mais sans plus. Apparemment il y a eu pas mal de progrès depuis PHP5, un jour peut-être que j’y reviendrai. Mais, il a derrière lui une réputation de langage pour les “scripts kiddies”. +

      Sympatique, mais sans plus. Apparemment il y a eu pas mal de progrès depuis PHP5, un jour peut-être que j’y reviendrai. Mais, il a derrière lui une réputation de langage pour les “scripts kiddies”. En gros ceux qui ne savent pas coder. -Des trous de sécurité de tous les cotés, etc…

      +Des trous de sécurité de tous les cotés, etc…

      -

      En réalité, PHP est au niveau d’abstration à peine supérieur au C. +

      En réalité, PHP est au niveau d’abstration à peine supérieur au C. Et donc, il est beaucoup moins bien organisé que des langages objets, favorisant ainsi la création de bug. -Pour les applications web, c’est un vrai problème.

      +Pour les applications web, c’est un vrai problème.

      -

      PHP, reste pour moi le langage de l’injection SQL. J’en fait encore un peu de temps en temps. Et j’ai moi-même dû protéger les accès au SQL pour éviter les injections. Oui, je n’ai pas trouvé de librairie toute prête pour protéger les entrées SQL. Je n’ai pas beaucoup cherché non plus.

      +

      PHP, reste pour moi le langage de l’injection SQL. J’en fait encore un peu de temps en temps. Et j’ai moi-même dû protéger les accès au SQL pour éviter les injections. Oui, je n’ai pas trouvé de librairie toute prête pour protéger les entrées SQL. Je n’ai pas beaucoup cherché non plus.

      Python

      -

      Python. Do you speak it?

      +

      Python. Do you speak it?

      Alors là, attention ! Révélation !

      -

      Lorsqu’on avait l’habitude de travailler avec des langages compilé, type C++, Java et qu’on passe à Python, on se prend une claque magistrale. +

      Lorsqu’on avait l’habitude de travailler avec des langages compilé, type C++, Java et qu’on passe à Python, on se prend une claque magistrale. La programmation comme elle doit être faite. -Tout est si naturel, c’est magique. -Oui, c’est si bien que ça. -Mais quelque chose d’aussi incroyablement bien doit avoir des inconvénients me dirais-vous.

      +Tout est si naturel, c’est magique. +Oui, c’est si bien que ça. +Mais quelque chose d’aussi incroyablement bien doit avoir des inconvénients me dirais-vous.

      Et bien, oui, comme tous les langages de scripts de haut niveau, Python est lent. Attention pas juste un peu lent, comme 2 fois plus lent que du C. -Non, de l’ordre de 10 à 20 fois plus lent que le C. -Argh… Bon ça reste utilisable pour beaucoup de choses. +Non, de l’ordre de 10 à 20 fois plus lent que le C. +Argh… Bon ça reste utilisable pour beaucoup de choses. Mais certaines application lui sont donc interdites.

      Awk

      Des filtres de fichiers à faire. -Si ce n’est pas trop compliqué, c’est le langage idéal. +Si ce n’est pas trop compliqué, c’est le langage idéal. Vous avez un fichier et vous voulez savoir quels sont les mots les plus utilisés. Savoir combien de fois un mot est utilisé. -Filtrer sous des condition un peu plus compliquées qu’un grep. -Super outils. Je l’ai utilisé pour modifier en masse des centaines de fichier XML plus facilement qu’avec du XSLT.

      +Filtrer sous des condition un peu plus compliquées qu’un grep. +Super outils. Je l’ai utilisé pour modifier en masse des centaines de fichier XML plus facilement qu’avec du XSLT.

      Perl

      -

      Perl c’est assez magique, mais la syntaxe est tellement désagréable à lire que personne ne peut vraiment aimer programmer dans un environnement de plusieurs personnes en Perl. +

      Perl c’est assez magique, mais la syntaxe est tellement désagréable à lire que personne ne peut vraiment aimer programmer dans un environnement de plusieurs personnes en Perl. A moins que tous les autres soient des cadors du Perl. Mais la feature qui tue, les expressions régulières :

      -
      -$var =~ s/toto/titi/g
      -
      +
      $var =~ s/toto/titi/g
      +

      Va remplacer toto par titi dans la valeur de la variable $var. Et oui, les expressions régulière y sont intégrées directement comme avec sed et awk. Et ça rend le code beacoup plus compact (et parfois illisible). -Mais c’est vraiment pas mal. -C’est une sorte de awk sous stéroides.

      +Mais c’est vraiment pas mal. +C’est une sorte de awk sous stéroides.

      Ruby

      -

      C’est une sorte de Perl en plus propre. +

      C’est une sorte de Perl en plus propre. Un mélange de Perl et de Python. -Les notion objets y sont plus fortes qu’en Python. -Je l’ai beaucoup utilisé, je reste quand même un Pythoniste de préférence. +Les notion objets y sont plus fortes qu’en Python. +Je l’ai beaucoup utilisé, je reste quand même un Pythoniste de préférence. Mais Ruby est vraiment très bien. -Par contre en terme d’efficacité, c’est le pire langage utilisé par beaucoup de monde de ce point de vue. -C’est le langage qui perd quasiment tous les benchmarks. -Par contre c’est un outil parfait pour faire des prototypes. +Par contre en terme d’efficacité, c’est le pire langage utilisé par beaucoup de monde de ce point de vue. +C’est le langage qui perd quasiment tous les benchmarks. +Par contre c’est un outil parfait pour faire des prototypes. Et si vous voulez faire un prototype de site web, RoR est ce qui se fait de mieux. -De l’idée au site, il ne se passera que peu de temps.

      +De l’idée au site, il ne se passera que peu de temps.

      Javascript

      -

      C’est la bonne surprise. +

      C’est la bonne surprise. Pendant des années, javascript était considéré comme un langage tout bon à vous embéter dans votre navigation web. En réalité, javascript possède beaucoup de qualité des langages de haut niveau. En particulier, il est facille de passer une fonction en paramèter ou de créer des fonctions anonymes (closures). Récemment, il est devenu très rapide et beaucoup de frameworks et de librairies naissent un peu partout.

        -
      • Il y a Cappuccino, Objective-J (comme de l’objective-C mais avec du javascript)
      • +
      • Il y a Cappuccino, Objective-J (comme de l’objective-C mais avec du javascript)
      • Sproutcore
      • Spine.js
      • Backbone.js
      • @@ -383,62 +383,60 @@ Récemment, il est devenu très rapide et beaucoup de frameworks et de librairie

      En particulier avec jQuery, on peut faire des appels chainés, très agréables à utiliser. -Comme je le disais, c’est une bonne surprise, javascript a été choisi un peu au hasard lors de la création des navigateurs web comme langage de script. -Et il s’avère qu’à part sa syntaxe, tout le reste est bien. +Comme je le disais, c’est une bonne surprise, javascript a été choisi un peu au hasard lors de la création des navigateurs web comme langage de script. +Et il s’avère qu’à part sa syntaxe, tout le reste est bien. Heureusement, en ce qui concerne la syntaxe, on peu pallier à ce problème en utilisant CoffeeScript.

      Les langages fonctionnels

      CamL

      -

      J’ai appris CamL à la fac, j’avais trouvé cette expérience très interressante. -J’étais plutôt bon, et j’avais les bonnes intuitions mathématiques qui vont avec la programmation fonctionnelle. -Mais je dois avouer que je ne l’ai plus jamais utilisé. -Simplement, ce type de langage semble si loin de ce qui se fait pour fabriquer des produits que ça me donnais vraiment l’impression d’être un langage pour chercheurs.

      +

      J’ai appris CamL à la fac, j’avais trouvé cette expérience très interressante. +J’étais plutôt bon, et j’avais les bonnes intuitions mathématiques qui vont avec la programmation fonctionnelle. +Mais je dois avouer que je ne l’ai plus jamais utilisé. +Simplement, ce type de langage semble si loin de ce qui se fait pour fabriquer des produits que ça me donnais vraiment l’impression d’être un langage pour chercheurs.

      Haskell

      -

      Je suis en train d’apprendre ce langage. -Et je dois dire que c’est un vrai plaisir. +

      Je suis en train d’apprendre ce langage. +Et je dois dire que c’est un vrai plaisir. En général les concepts derrière tous les langages de programmation sont assez limités. -Chaque langage y va de son petit lot de nouveau concepts, et en général en une après-midi, c’est appris. -Pour haskell, c’est très différent. -Je sens bien qu’il va me falloir plusieurs semaines pour maîtriser la bête. -Ça doit faire quatre semaines que j’apprend haskell un peut tous les jours et je sais qu’il y a des notions que j’ai juste survollées et qui sont assez incroyables. -Les Monades par exemple, est un concept que je n’avais jamais rencontré ailleurs. -C’est un super concept. +Chaque langage y va de son petit lot de nouveau concepts, et en général en une après-midi, c’est appris. +Pour haskell, c’est très différent. +Je sens bien qu’il va me falloir plusieurs semaines pour maîtriser la bête. +Ça doit faire quatre semaines que j’apprend haskell un peut tous les jours et je sais qu’il y a des notions que j’ai juste survollées et qui sont assez incroyables. +Les Monades par exemple, est un concept que je n’avais jamais rencontré ailleurs. +C’est un super concept. De plus le design du langage en fait un parfait système pour paralléliser les calculs naturellement. -haskell sépare la partie “pure” de la partie “impure” de la programmation. -À ma connaissance, c’est le seul langage de programmation qui fait ça. +haskell sépare la partie “pure” de la partie “impure” de la programmation. +À ma connaissance, c’est le seul langage de programmation qui fait ça. Enfin, je prend beaucoup de plaisir à apprendre ce langage. La communauté est aussi très acceuillante. -Pas de “L0L! URAN00B!”. +Pas de “L0L! URAN00B!”. Et aussi pas de concession du langage pour devenir populaire. Le langage est bon, voilà tout. -Alors qu’en Java et C++, typiquement certain choix ont été fait en dépis du bon sens pour “faire plaisir”.

      +Alors qu’en Java et C++, typiquement certain choix ont été fait en dépis du bon sens pour “faire plaisir”.

      Langages originaux

      Metapost

      Metapost est un langage qui permet de programmer des dessins. -Le gros plus de metapost, c’est sa capacité de résoudre automatiquement les systèmes d’équations linéaires. +Le gros plus de metapost, c’est sa capacité de résoudre automatiquement les systèmes d’équations linéaires. Par exemple, si vous écrivez :

      -
      -AA=1/3[A,B]
      -
      +
      AA=1/3[A,B]
      +

      Il va position le point AA entre A et B. Plus précisément, au barycentre (2A + B)/3.

      -
      -X=whatever[A,B]
      -X=whatever[C,D]
      -
      +
      X=whatever[A,B]
      +X=whatever[C,D]
      +
      -

      Ce deuxième exemple positionne X à l’intersection des deux segments AB et CD. -Vous pouvez aussi voir pas mal d’exemples ici. +

      Ce deuxième exemple positionne X à l’intersection des deux segments AB et CD. +Vous pouvez aussi voir pas mal d’exemples ici. You could see more example there.

      Cette fonction est très utile. @@ -448,30 +446,30 @@ De mon point de vue, les autres langages de programmation devraient penser à ra

      zsh

      Oui, zsh est un shell. -Mais c’est aussi un langage de script très bien adapté aux traitement de fichiers. +Mais c’est aussi un langage de script très bien adapté aux traitement de fichiers. Je le recommande chaudement. -C’est pour l’instant le meilleur shell que j’ai utilisé. Je le préfère au bash.

      +C’est pour l’instant le meilleur shell que j’ai utilisé. Je le préfère au bash.

      Prolog

      -

      Je n’ai jamais rien fait de conséquent avec Prolog, mais j’ai adoré l’apprendre et l’utiliser. -J’ai eu la chance d’apprendre Prolog par Alain Colmerauer lui-même. -C’est un langage qui essaye de résoudre les contraintes autant qu’il le peut pour vous. +

      Je n’ai jamais rien fait de conséquent avec Prolog, mais j’ai adoré l’apprendre et l’utiliser. +J’ai eu la chance d’apprendre Prolog par Alain Colmerauer lui-même. +C’est un langage qui essaye de résoudre les contraintes autant qu’il le peut pour vous. Il en ressort un impression de magie. -On ne fait que décrire ce qu’il faut et on ne donne pas d’ordre. +On ne fait que décrire ce qu’il faut et on ne donne pas d’ordre. Un peu comme la programmation fonctionnelle mais en beaucoup plus puissant.

      Les langages à découvrir

      Il reste encore pas mal de langages et de framework à essayer. Actuellement je pense que je vais passer un moment avec haskell. -Peut-être demain que j’irai apprendre LISP, Scala ou Erlang. -Comme je suis plus dans la création de site web, j’irai certainement jeter un coup d’œil à clojure aussi. -Et certainement beaucoup d’autres choses.

      +Peut-être demain que j’irai apprendre LISP, Scala ou Erlang. +Comme je suis plus dans la création de site web, j’irai certainement jeter un coup d’œil à clojure aussi. +Et certainement beaucoup d’autres choses.

      Dites moi si vous avez une autre expérience avec ces langages de programmation. Évidement mes impression sont hautement subjectives. -Cependant, j’ai utilisé tous les langages dont j’ai parlé.

      +Cependant, j’ai utilisé tous les langages dont j’ai parlé.

      @@ -589,7 +587,7 @@ Cependant, j’ai utilisé tous les langages dont j’ai parlé.

      Écrit le : 28/09/2011 - modifié le : 26/10/2011 + modifié le : 26/04/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/index.html b/output/Scratch/fr/index.html index cbccfaec3..4442a0792 100644 --- a/output/Scratch/fr/index.html +++ b/output/Scratch/fr/index.html @@ -148,7 +148,7 @@ Droits de reproduction ©, Yann Esposito
      - modifié le : 24/04/2012 + modifié le : 02/05/2012
      Site entièrement réalisé avec diff --git a/output/Scratch/fr/rss/index.html b/output/Scratch/fr/rss/index.html index a3cc56300..f5dc7b4ba 100644 --- a/output/Scratch/fr/rss/index.html +++ b/output/Scratch/fr/rss/index.html @@ -64,9 +64,9 @@

      rss

      -

      On vous propose de vous abonner au flux RSS. Mais de quoi s’agit il ?

      +

      On vous propose de vous abonner au flux RSS. Mais de quoi s’agit il ?

      -

      Si vous n’êtes pas anglophobe je vous recommande la lecture de what is rss ou encore mieux, de regarder cette vidéo RSS explained.

      +

      Si vous n’êtes pas anglophobe je vous recommande la lecture de what is rss ou encore mieux, de regarder cette vidéo RSS explained.

      @@ -76,23 +76,23 @@

      Mon explication

      -

      Il s’agit d’un moyen facile d’agréger dans un seul endroit toutes les mises à jours de tous les sites qui vous intéressent.

      +

      Il s’agit d’un moyen facile d’agréger dans un seul endroit toutes les mises à jours de tous les sites qui vous intéressent.

      choisir un client

      -

      Tout d’abord, il faut choisir un client de flux RSS. Aujourd’hui il existe de nombreux client en ligne. C’est-à-dire des sites web qui vont s’occuper du regroupement. Ces client s’appellent des “aggregator”.

      +

      Tout d’abord, il faut choisir un client de flux RSS. Aujourd’hui il existe de nombreux client en ligne. C’est-à-dire des sites web qui vont s’occuper du regroupement. Ces client s’appellent des “aggregator”.

      -

      Personnellement j’utilise Netvibes. J’en ai essayé vraiment beaucoup, et il reste de loin mon préféré.

      +

      Personnellement j’utilise Netvibes. J’en ai essayé vraiment beaucoup, et il reste de loin mon préféré.

      -

      Évidemment Google propose son client aussi : Google Reader. S’il reste adapté pour les contenus pour lesquels on ne veut rien perdre. Il est moins agréable d’utilisation lorsque l’on s’abonne à des flux qui proposent une vingtaine de nouveaux liens par jour.

      +

      Évidemment Google propose son client aussi : Google Reader. S’il reste adapté pour les contenus pour lesquels on ne veut rien perdre. Il est moins agréable d’utilisation lorsque l’on s’abonne à des flux qui proposent une vingtaine de nouveaux liens par jour.

      -

      S’abonner aux flux d’un site

      +

      S’abonner aux flux d’un site

      -

      Donc une fois que l’on a choisi son client, il suffit pour s’abonner de cliquer sur l’icône d’abonnement. Soit il est bien visible sur la page, soit tout en haut dans la barre des tâches.

      +

      Donc une fois que l’on a choisi son client, il suffit pour s’abonner de cliquer sur l’icône d’abonnement. Soit il est bien visible sur la page, soit tout en haut dans la barre des tâches.

      -

      Récupérer les “news”

      +

      Récupérer les “news”

      -

      Ensuite lorsque vous utilisez votre client RSS les nouvelles provenant du blog se mettront à jour. Ainsi, il n’y a plus besoin d’aller sur les sites intéressants pour voir s’il n’y a rien de neuf. Ce sont eux qui vous donne leur dernières nouvelles.

      +

      Ensuite lorsque vous utilisez votre client RSS les nouvelles provenant du blog se mettront à jour. Ainsi, il n’y a plus besoin d’aller sur les sites intéressants pour voir s’il n’y a rien de neuf. Ce sont eux qui vous donne leur dernières nouvelles.

      diff --git a/output/Scratch/fr/softwares/yaquabubbles/index.html b/output/Scratch/fr/softwares/yaquabubbles/index.html index bd074572a..6a72c5823 100644 --- a/output/Scratch/fr/softwares/yaquabubbles/index.html +++ b/output/Scratch/fr/softwares/yaquabubbles/index.html @@ -62,10 +62,10 @@

      Screenshot

      -

      YAquaBubbles est un économiseur d’écran réalisé avec QuartzComposer. -Il s’agissait d’un simple essai mais le résultat était plaisant.

      +

      YAquaBubbles est un économiseur d’écran réalisé avec QuartzComposer. +Il s’agissait d’un simple essai mais le résultat était plaisant.

      -

      YAquaBubbles.dmg

      +

      YAquaBubbles.dmg

      diff --git a/output/Scratch/fr/softwares/yclock/index.html b/output/Scratch/fr/softwares/yclock/index.html index 414b24719..02c635a05 100644 --- a/output/Scratch/fr/softwares/yclock/index.html +++ b/output/Scratch/fr/softwares/yclock/index.html @@ -58,11 +58,11 @@

      Screenshot

      -

      YClock est un économiseur d’écran qui vous donne l’heure.i +

      YClock est un économiseur d’écran qui vous donne l’heure.i Il a trois thèmes clair, rouge et noir. -Il utilise une base de QuartzComposition + du code objective-C pour la gestion du nombre d’images par seconde.

      +Il utilise une base de QuartzComposition + du code objective-C pour la gestion du nombre d’images par seconde.

      -

      YClock.dmg

      +

      YClock.dmg

      diff --git a/output/Scratch/fr/softwares/ypassword/index.html b/output/Scratch/fr/softwares/ypassword/index.html index f76ab1b2a..8f20fddb6 100644 --- a/output/Scratch/fr/softwares/ypassword/index.html +++ b/output/Scratch/fr/softwares/ypassword/index.html @@ -58,7 +58,7 @@

      Une gestion simple, sécurisée et portable de ses mots de passes web.

      -

      Souvenez vous d’un seul mot de passe de bonne qualité, le reste suis.

      +

      Souvenez vous d’un seul mot de passe de bonne qualité, le reste suis.

      Ici vous trouverez :

      diff --git a/output/Scratch/fr/validation/index.html b/output/Scratch/fr/validation/index.html index 445776f53..ac113394f 100644 --- a/output/Scratch/fr/validation/index.html +++ b/output/Scratch/fr/validation/index.html @@ -61,19 +61,19 @@ de mes pages.

      Je voulais utiliser box-shadows et border-radius

      -

      J’ai donc préféré avoir un approche pragamatique que dogmatique.

      +

      J’ai donc préféré avoir un approche pragamatique que dogmatique.

      Utiliser ces propriétés me fait perdre la validation CSS -mais fonctionne très bien avec les navigateurs récents (Safari 4 -et Firefox 3.5 au moment de l’écriture de ces lignes)

      +mais fonctionne très bien avec les navigateurs récents (Safari 4 +et Firefox 3.5 au moment de l’écriture de ces lignes)

      -

      Si vous n’utilisez pas ces navigateur les pages s’affichent -correctement mais sans ces effets qui n’ont pour but que d’améliorer -l’aspect général de la page.

      +

      Si vous n’utilisez pas ces navigateur les pages s’affichent +correctement mais sans ces effets qui n’ont pour but que d’améliorer +l’aspect général de la page.

      -

      Par contre je suis plutôt un partisant de la validation et c’est -pourquoi il y a toujours les liens. Tout valide à l’exception +

      Par contre je suis plutôt un partisant de la validation et c’est +pourquoi il y a toujours les liens. Tout valide à l’exception des propriétés commençant par -moz et -webkit.

      diff --git a/output/Scratch/sitemap.xml b/output/Scratch/sitemap.xml index 2989df579..55308c180 100644 --- a/output/Scratch/sitemap.xml +++ b/output/Scratch/sitemap.xml @@ -1,759 +1,759 @@ - - http://yannesposito.com/Scratch/en/about/contact/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Yesod-excellent-ideas/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/ypassword/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/latest/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Password-Management/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Higher-order-function-in-zsh/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/about/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/ypassword/iphoneweb/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-09-replace-all-except-some-part/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_1/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/mvc/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_2/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/yaquabubbles/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-Focus-vs-Minimalism/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_3/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Typography-and-the-Web/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-03-22-Git-Tips/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-07-09-Indecidabilities/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_4/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/Typography-and-the-Web/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-12-06-iphone-call-filter/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/01_nanoc/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/11_Load_Disqus_Asynchronously/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-02-15-All-but-something-regexp/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_1/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/ypassword/web/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/05_git_create_remote_branch/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-untaught-git-usage/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/11_Load_Disqus_Asynchronously/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_2/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/about/cv/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-06-15-Get-my-blog-engine/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Haskell-Mandelbrot/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/about/technical_details/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/mvc/ - 2012-04-17 - - - http://yannesposito.com/Scratch/assets/css/dynamic.css - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/programming-language-experience/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/validation/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_3/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Yesod-tutorial-for-newbies/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Higher-order-function-in-zsh/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-untaught-git-usage/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/yclock/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2011-01-03-Happy-New-Year/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_4/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/02_ackgrep/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/03_losthighway/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/about/old/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/programming-language-experience/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-12-06-iphone-call-filter/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/about/contact/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-07-31-New-style-after-holidays/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-10-14-Fun-with-wav/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Yesod-excellent-ideas/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/02_ackgrep/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Haskell-the-Hard-Way/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/2010-02-15-All-but-something-regexp/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/06_How_I_use_git/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-09-replace-all-except-some-part/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/about/old/ - 2012-04-24 - - - http://yannesposito.com/Scratch/en/about/cv/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2011-04-20-Now-hosted-on-github/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/ypassword/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Password-Management/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/A-more-convenient-diff/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/feed/feed.xml - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/validation/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-06-14-multi-language-choices/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/A-more-convenient-diff/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2011-01-03-Happy-New-Year/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/yaquabubbles/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-07-31-New-style-after-holidays/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/06_How_I_use_git/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/03_losthighway/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/rss/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-launch-daemon-from-command-line/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-03-22-Git-Tips/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-03-23-Encapsulate-git/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/Haskell-Mandelbrot/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/ - 2012-04-24 - - - http://yannesposito.com/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/ypassword/web/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/Learn-Vim-Progressively/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-03-23-Encapsulate-git/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/ - 2012-04-17 - - - http://yannesposito.com/Scratch/assets/css/main.css - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/rss/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/about/technical_details/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/04_drm/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/SVG-and-m4-fractals/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-07-09-Indecidabilities/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/01_nanoc/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/about/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/ypassword/iphoneweb/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/ - 2012-04-24 - - - http://yannesposito.com/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/softwares/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2010-06-14-multi-language-choices/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/05_git_create_remote_branch/ - 2012-04-17 - - - http://yannesposito.com/Scratch/en/blog/feed/feed.xml - 2012-04-17 - http://yannesposito.com/Scratch/sitemap.xml - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/softwares/yclock/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/latest/ - 2012-04-17 - - - http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/ - 2012-04-17 + 2012-03-19 http://yannesposito.com/Scratch/en/blog/04_drm/ - 2012-04-17 + 2012-05-02 - http://yannesposito.com/Scratch/en/blog/2009-12-14-Git-vs--Bzr/ - 2012-04-17 + http://yannesposito.com/Scratch/en/blog/2011-01-03-Happy-New-Year/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/A-more-convenient-diff/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-08-23-Now-heberged-on-heroku/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/10_Synchronize_Custom_WebSite_with_mobileMe/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-09-jQuery-Tag-Cloud/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-06-17-track-events-with-google-analytics/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-12-06-iphone-call-filter/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/03_losthighway/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/11_Load_Disqus_Asynchronously/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-07-31-New-style-after-holidays/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-launch-daemon-from-command-line/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-06-14-multi-language-choices/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2011-04-20-Now-hosted-on-github/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/Haskell-Mandelbrot/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/02_ackgrep/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/01_nanoc/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/05_git_create_remote_branch/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-07-07-CSS-rendering-problems-by-navigator/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/mvc/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/07_Screensaver_compilation_option_for_Snow_Leopard/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2009-09-replace-all-except-some-part/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-How-to-preload-your-site-with-style/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/09_Why_I_didn-t_keep_whosamung-us/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-02-15-All-but-something-regexp/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-09-02-base64-and-sha1-on-iPhone/ + 2012-04-30 http://yannesposito.com/Scratch/en/blog/SVG-and-m4-fractals/ - 2012-04-17 + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/Higher-order-function-in-zsh/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-06-19-jQuery-popup-the-easy-way/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-06-15-Get-my-blog-engine/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/06_How_I_use_git/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-12-14-Git-vs--Bzr/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/Password-Management/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-07-05-Cappuccino-and-Web-applications/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/Typography-and-the-Web/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/Yesod-excellent-ideas/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-Focus-vs-Minimalism/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/programming-language-experience/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/comprendre/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/conf-et-install/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/commandes-avancees/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n00b/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-03-23-Encapsulate-git/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-02-16-All-but-something-regexp--2-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-03-22-Git-Tips/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-02-18-split-a-file-by-keyword/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_3/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_2/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_1/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/03_losthighway/03_losthighway_4/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-05-17-at-least-this-blog-revive/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-06-17-hide-yourself-to-analytics/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/2010-07-09-Indecidabilities/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2009-10-untaught-git-usage/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/2010-02-23-When-regexp-is-not-the-best-solution/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/blog/ + 2012-04-26 + + + http://yannesposito.com/Scratch/en/about/ + 2012-04-11 + + + http://yannesposito.com/Scratch/en/latest/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/rss/ + 2012-03-19 + + + http://yannesposito.com/Scratch/en/softwares/ypassword/iphoneweb/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/softwares/ypassword/web/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/softwares/ypassword/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/softwares/yclock/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/softwares/yaquabubbles/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/softwares/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/validation/ + 2012-03-19 + + + http://yannesposito.com/Scratch/en/about/old/ + 2012-03-20 + + + http://yannesposito.com/Scratch/en/about/technical_details/ + 2012-03-19 + + + http://yannesposito.com/Scratch/en/about/contact/ + 2012-05-02 + + + http://yannesposito.com/Scratch/en/about/cv/ + 2012-03-19 + + + http://yannesposito.com/Scratch/en/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/04_drm/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2011-01-03-Happy-New-Year/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2009-09-Disqus-versus-Intense-Debate--Why-I-switched-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/A-more-convenient-diff/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-08-23-Now-heberged-on-heroku/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/10_Synchronize_Custom_WebSite_with_mobileMe/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-09-jQuery-Tag-Cloud/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-06-17-track-events-with-google-analytics/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-01-12-antialias-font-in-Firefox-under-Ubuntu/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-Wait-to-hide-a-menu-in-jQuery/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-12-06-iphone-call-filter/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/03_losthighway/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/11_Load_Disqus_Asynchronously/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-07-31-New-style-after-holidays/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/Yesod-tutorial-for-newbies/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-launch-daemon-from-command-line/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-06-14-multi-language-choices/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2011-04-20-Now-hosted-on-github/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-08-31-send-mail-from-command-line-with-attached-file/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/Haskell-Mandelbrot/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/02_ackgrep/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/01_nanoc/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/05_git_create_remote_branch/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-07-07-CSS-rendering-problems-by-navigator/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/08_Configure_ssh_to_listen_the_port_443_on_Snow_Leopard/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/mvc/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/07_Screensaver_compilation_option_for_Snow_Leopard/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-05-19-How-to-cut-HTML-and-repair-it/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2009-09-replace-all-except-some-part/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-How-to-preload-your-site-with-style/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-10-14-Fun-with-wav/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/09_Why_I_didn-t_keep_whosamung-us/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-28-custom-website-synchronisation-with-mobileme--2-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-02-15-All-but-something-regexp/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-09-02-base64-and-sha1-on-iPhone/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/SVG-and-m4-fractals/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/Higher-order-function-in-zsh/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-06-19-jQuery-popup-the-easy-way/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-01-04-Change-default-shell-on-Mac-OS-X/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-06-15-Get-my-blog-engine/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/06_How_I_use_git/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-12-14-Git-vs--Bzr/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/Password-Management/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-07-05-Cappuccino-and-Web-applications/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/Learn-Vim-Progressively/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/Typography-and-the-Web/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/Yesod-excellent-ideas/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-Focus-vs-Minimalism/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-05-24-Trees--Pragmatism-and-Formalism/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/programming-language-experience/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/c-est-parti-pour-l-aventure/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/comprendre/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/Git-pour-quoi-faire/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/conf-et-install/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/commandes-avancees/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-30-How-to-handle-evil-IE/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-11-12-Git-for-n00b/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-03-23-Encapsulate-git/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-02-16-All-but-something-regexp--2-/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-03-22-Git-Tips/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/Haskell-the-Hard-Way/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-02-18-split-a-file-by-keyword/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_3/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_2/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_1/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/03_losthighway/03_losthighway_4/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-05-17-at-least-this-blog-revive/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-06-17-hide-yourself-to-analytics/ + 2012-04-30 + + + http://yannesposito.com/Scratch/fr/blog/2010-07-09-Indecidabilities/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2009-10-untaught-git-usage/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/2010-02-23-When-regexp-is-not-the-best-solution/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/blog/ + 2012-04-26 + + + http://yannesposito.com/Scratch/fr/about/ + 2012-04-11 + + + http://yannesposito.com/Scratch/fr/latest/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/rss/ + 2012-03-19 + + + http://yannesposito.com/Scratch/fr/softwares/ypassword/iphoneweb/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/softwares/ypassword/web/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/softwares/ypassword/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/softwares/yclock/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/softwares/yaquabubbles/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/softwares/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/validation/ + 2012-03-19 + + + http://yannesposito.com/Scratch/fr/about/old/ + 2012-03-20 + + + http://yannesposito.com/Scratch/fr/about/technical_details/ + 2012-03-19 + + + http://yannesposito.com/Scratch/fr/about/contact/ + 2012-05-02 + + + http://yannesposito.com/Scratch/fr/about/cv/ + 2012-03-19 + + + http://yannesposito.com/Scratch/fr/ + 2012-04-30 + + + http://yannesposito.com/Scratch/en/blog/feed/feed.xml + 2012-03-19 + + + http://yannesposito.com/Scratch/fr/blog/feed/feed.xml + 2012-03-19 + + + http://yannesposito.com/Scratch/assets/css/main.css + 2012-05-02 + + + http://yannesposito.com/Scratch/assets/css/dynamic.css + 2012-04-11