<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3860519759344647288</id><updated>2024-08-28T11:31:33.216+02:00</updated><category term="python"/><category term="hitsmash"/><category term="music"/><category term="algorithms"/><category term="freebsd"/><category term="analyzer"/><category term="facebook"/><category term="gtk"/><category term="hacks"/><category term="kernel"/><category term="subversion"/><category term="svn"/><category term="annotations"/><category term="audio"/><category term="bar"/><category term="cakephp"/><category term="database"/><category term="dynamic"/><category term="email"/><category term="fft"/><category term="for"/><category term="genetic"/><category term="mercurial"/><category term="mysql"/><category term="normalization"/><category term="notification"/><category term="programming"/><category term="progress"/><category term="repository"/><category term="search"/><category term="spectrum"/><category term="system"/><category term="xpdf"/><category term="3dscene"/><category term="XTerm"/><category term="acbi"/><category term="adjustment"/><category term="alpha"/><category term="alsa"/><category term="apple"/><category term="arguments"/><category term="automate"/><category term="autoscatto"/><category term="backup"/><category term="barduino"/><category term="bash"/><category term="blogger"/><category term="bluetooth"/><category term="bookmarks"/><category term="books"/><category term="bsod"/><category term="cairo"/><category term="camp"/><category term="canvs"/><category term="class"/><category term="cli"/><category term="clipboard"/><category term="codebase"/><category term="control"/><category term="conversation"/><category term="copy"/><category term="ctlr"/><category term="cube"/><category term="curriculum"/><category term="cv"/><category term="cygwin"/><category term="default"/><category term="deployment"/><category term="dft"/><category term="digits"/><category term="drawing"/><category term="eclipse"/><category term="else"/><category term="engine"/><category term="equalizer"/><category term="equanalizer"/><category term="escape"/><category term="events"/><category term="facesmash"/><category term="file"/><category term="filler"/><category term="fractals"/><category term="freehands"/><category term="gcalendar"/><category term="gdata"/><category term="generator"/><category term="glade"/><category term="gnome"/><category term="google"/><category term="gstream"/><category term="gstreamer"/><category term="hackitaly"/><category term="hotkeys"/><category term="html"/><category term="icons"/><category term="info"/><category term="insert"/><category term="interface"/><category term="internet"/><category term="inverted"/><category term="ipsum"/><category term="java"/><category term="javascript"/><category term="job"/><category term="jquery"/><category term="keyboard"/><category term="keys"/><category term="led"/><category term="linux"/><category term="list"/><category term="loading"/><category term="loop"/><category term="lorem"/><category term="lsystem"/><category term="lyndenmayer"/><category term="master"/><category term="matrix"/><category term="maze"/><category term="menu"/><category term="multimedia"/><category term="multiple"/><category term="mv"/><category term="mystipy"/><category term="nc10"/><category term="network"/><category term="next"/><category term="notation"/><category term="notify"/><category term="oauth"/><category term="osd"/><category term="packaging"/><category term="paste"/><category term="pendulum"/><category term="permutation"/><category term="perspective"/><category term="phone"/><category term="php"/><category term="physic"/><category term="picobsd"/><category term="pidgin"/><category term="player"/><category term="plugin"/><category term="polish"/><category term="primary"/><category term="problem"/><category term="productivity"/><category term="projects"/><category term="prompt"/><category term="puzzle"/><category term="pymaze"/><category term="radio"/><category term="registry"/><category term="remap"/><category term="rename"/><category term="replace"/><category term="restore"/><category term="reverse"/><category term="rmi"/><category term="rotating"/><category term="rpn"/><category term="samsung"/><category term="screensaver"/><category term="server"/><category term="shift"/><category term="simulation"/><category term="smallest"/><category term="snippet"/><category term="snow"/><category term="sorting"/><category term="sound"/><category term="source"/><category term="sqlite"/><category term="summary"/><category term="table_parser"/><category term="ternary"/><category term="testing"/><category term="text"/><category term="texturizer"/><category term="thesis"/><category term="thread"/><category term="tkinter"/><category term="tone"/><category term="touchpad"/><category term="transparent"/><category term="ui"/><category term="update"/><category term="user"/><category term="userspace"/><category term="vagrant"/><category term="viewer"/><category term="vim"/><category term="visual"/><category term="vitae"/><category term="webcam"/><category term="webpy"/><category term="while"/><category term="windows"/><category term="word-challenge"/><category term="wordpress"/><category term="youtube"/><title type='text'>Matteo Landi</title><subtitle type='html'>From space, I saw the fragile blue earth without borders.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/09744311058730946457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>93</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-483294450336123916</id><published>2014-02-11T18:00:00.000+01:00</published><updated>2014-02-11T18:00:07.034+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ctlr"/><category scheme="http://www.blogger.com/atom/ns#" term="escape"/><category scheme="http://www.blogger.com/atom/ns#" term="remap"/><category scheme="http://www.blogger.com/atom/ns#" term="windows"/><title type='text'>Windows tips: remap Ctrl to Escape</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5MaT0H0VPqOetu-klWx8abOkfixiHx9U2MhttWL4yeiFnTpjuwsANDEiO5fu6b_4rIcmaOtoJnM2WaN8TBbMufIIkpcpXnLruzoHQBFtuBqXXhLD20XFrIDIVs0_Fk1DcpFcxeEoLmRsc/s1600/ctrl-key.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5MaT0H0VPqOetu-klWx8abOkfixiHx9U2MhttWL4yeiFnTpjuwsANDEiO5fu6b_4rIcmaOtoJnM2WaN8TBbMufIIkpcpXnLruzoHQBFtuBqXXhLD20XFrIDIVs0_Fk1DcpFcxeEoLmRsc/s320/ctrl-key.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;The following is by far one of the most useful
&lt;a href=&quot;http://www.autohotkey.com/&quot;&gt;AutoHotkey&lt;/a&gt; snippet around:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Ctrl UP::Send {Escape}
Ctrl &amp;amp; F13::
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Smells like &lt;em&gt;black magic&lt;/em&gt;, doesn&#39;t it?  In practice this guy is making the
&lt;code&gt;Ctrl&lt;/code&gt; key act like it was the &lt;code&gt;Escape&lt;/code&gt; one, but only when pressed and released
on its own.&lt;/p&gt;

&lt;p&gt;Isn&#39;t that beautiful? -- I am talking to you, &lt;a href=&quot;www.vim.org/&quot;&gt;Vim&lt;/a&gt; users out
there.&lt;/p&gt;

&lt;p&gt;Oh, and it works even with your re-mapped &lt;code&gt;Capslock&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;What a joy, that made my day!&lt;/p&gt;

&lt;p&gt;PS. It&#39;s a bit like &lt;a href=&quot;https://github.com/alols/xcape&quot;&gt;xcape&lt;/a&gt;, but for Windows
users.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/483294450336123916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2014/02/windows-tips-remap-ctrl-to-escape.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/483294450336123916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/483294450336123916'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2014/02/windows-tips-remap-ctrl-to-escape.html' title='Windows tips: remap Ctrl to Escape'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/09744311058730946457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5MaT0H0VPqOetu-klWx8abOkfixiHx9U2MhttWL4yeiFnTpjuwsANDEiO5fu6b_4rIcmaOtoJnM2WaN8TBbMufIIkpcpXnLruzoHQBFtuBqXXhLD20XFrIDIVs0_Fk1DcpFcxeEoLmRsc/s72-c/ctrl-key.jpg" height="72" width="72"/><thr:total>1</thr:total><georss:featurename>Viareggio Lucca, Italy</georss:featurename><georss:point>43.8657267 10.2513103</georss:point><georss:box>43.8657267 10.2513103 43.8657267 10.2513103</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-2491819786022481852</id><published>2014-02-05T14:19:00.002+01:00</published><updated>2014-02-05T14:19:44.262+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bookmarks"/><category scheme="http://www.blogger.com/atom/ns#" term="engine"/><category scheme="http://www.blogger.com/atom/ns#" term="productivity"/><category scheme="http://www.blogger.com/atom/ns#" term="search"/><title type='text'>Bunny1: the untold productivity tool</title><content type='html'>&lt;p&gt;How could I have missed &lt;a href=&quot;http://www.bunny1.org&quot;&gt;Bunny1&lt;/a&gt;?&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRq0yli68c7ulLPu3qsy2hCvdB_jz6lNnZJCNwwyuAwad4ObbF-IsOKn-OTdRGb6AZd1SvRdXDRyFbHOI22bD8cCwGZVCQ8G4jS-bYcRrJyHuNXMsLypVHrJhUYlOcF-_YJ6pYzvDv9pfZ/s1600/2133_50092888777_167_n.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRq0yli68c7ulLPu3qsy2hCvdB_jz6lNnZJCNwwyuAwad4ObbF-IsOKn-OTdRGb6AZd1SvRdXDRyFbHOI22bD8cCwGZVCQ8G4jS-bYcRrJyHuNXMsLypVHrJhUYlOcF-_YJ6pYzvDv9pfZ/s200/2133_50092888777_167_n.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;What the hell is that?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In essence Bunny1 is a one-page webserver in charge of redirecting all the
input requests to external services supposed to be the best ones capable of
handling them -- the requests.&lt;/p&gt;

&lt;p&gt;For example, you send Bunny1 a request containing
&lt;code&gt;g lorem ipsum&lt;/code&gt;, it will parse the leading &lt;code&gt;g&lt;/code&gt;, notice that &lt;code&gt;g&lt;/code&gt; is an alias
for &lt;code&gt;google&lt;/code&gt; and answer with a redirect to
&lt;a href=&quot;http://www.google.com/search?q=lorem%20ipsum&quot;&gt;Google&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Likewise, if you send it a request containing &lt;code&gt;yt where the hell is matt&lt;/code&gt;,
you will be redirected to
&lt;a href=&quot;http://www.youtube.com/results?search_query=where+the+hell+is+matt&amp;amp;search_type=&amp;amp;aq=-1&amp;amp;oq=&quot;&gt;Youtube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that all the queries that cannot be managed by Bunny1 &lt;em&gt;internally&lt;/em&gt; will
be automatically redirected to an &lt;em&gt;external&lt;/em&gt; service,
&lt;a href=&quot;http://www.yubnub.org/&quot;&gt;YouNub&lt;/a&gt;, which is supposed to work the way Bunny1
does but better -- it should manage more external services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Are you concerned about proxying all of your requests thru a Facebook server?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Host your Bunny1 server locally, its code is available on
&lt;a href=&quot;https://github.com/ccheever/bunny1&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Do you find any of the proposed redirects wrong or not up to date?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Extend Bunny1!  For example I did it to add support for Wikipedia:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def wikipedia(self, arg):
    &quot;&quot;&quot;Searches Wikipedia or goes there&quot;&quot;&quot;
    if arg:
        return &quot;http://en.wikipedia.org/wiki/Special:Search?search=%s&amp;amp;go=Go&quot; % qp(arg)
    else:
        return &quot;http://en.wikipedia.org&quot;
# make wi an alias for wikipedia
wi = wikipedia
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;StackOverflow:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def stackoverflow(self, arg):
    &quot;&quot;&quot;Searches StackOverflow or goes there&quot;&quot;&quot;
    if arg:
        return &quot;http://stackoverflow.com/search?q=%s&quot; % qp(arg)
    else:
        return &quot;http://stackoverflow.com&quot;
# make so an alias for stackoverflow
so = stackoverflow
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And even for Javascript -- default direct seems to be broken:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def javascript(self, arg):
    &quot;&quot;&quot;Search StackOverflow[Javascript] or goes there&quot;&quot;&quot;
    return self.stackoverflow(&#39;[javascript] &#39; + arg)
# make js an alias for stackoverflow[javascript]
js = javascript
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;I Am a &lt;a href=&quot;http://5digits.org/pentadactyl/&quot;&gt;Pentadactyl&lt;/a&gt; maniac, how could I use
Bunny1 with it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add the following lines to your &lt;code&gt;.pentadactylrc&lt;/code&gt; -- thanks &lt;a href=&quot;https://groups.google.com/d/msg/vimperator-labs/1q1sUUls5XM/ssO4hoQFovsJ&quot;&gt;Google
Groups&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:bmark -title=&quot;bunny1 query&quot; -keyword=&quot;bu&quot; www.bunny1.org/?___=%s
:set defsearch=bu
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/2491819786022481852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2014/02/bunny1-untold-productivity-tool.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/2491819786022481852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/2491819786022481852'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2014/02/bunny1-untold-productivity-tool.html' title='Bunny1: the untold productivity tool'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/09744311058730946457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRq0yli68c7ulLPu3qsy2hCvdB_jz6lNnZJCNwwyuAwad4ObbF-IsOKn-OTdRGb6AZd1SvRdXDRyFbHOI22bD8cCwGZVCQ8G4jS-bYcRrJyHuNXMsLypVHrJhUYlOcF-_YJ6pYzvDv9pfZ/s72-c/2133_50092888777_167_n.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Pisa, Italy</georss:featurename><georss:point>43.7228386 10.401688799999988</georss:point><georss:box>43.6310106 10.240327299999988 43.8146666 10.563050299999988</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-4517763785603625934</id><published>2014-01-31T08:56:00.002+01:00</published><updated>2014-01-31T08:56:30.652+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="jquery"/><category scheme="http://www.blogger.com/atom/ns#" term="perspective"/><title type='text'>jQuery Perspective</title><content type='html'>&lt;p&gt;Tonight I am gonna show you a very small project which kept me busy for the
last week-end.  Everything came out from this video (don&#39;t have to watch it
all, 12 seconds should be enough):&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;//www.youtube.com/embed/1IH6FZlzTRY&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;Have you spot it?  Did you notice what happens to pictures when the page is
scrolled?  Have you seen them moving up and down creating that
&lt;em&gt;perspective&lt;/em&gt;-like effect?&lt;/p&gt;

&lt;p&gt;Well, I had to try and reproduce it, and that&#39;s how
&lt;a href=&quot;https://github.com/iamFIREcracker/perspective&quot;&gt;perspective&lt;/a&gt; was born.&lt;/p&gt;

&lt;h4&gt;Theory&lt;/h4&gt;

&lt;p&gt;Let me try and explain how that effect works with the aid of some pictures.&lt;/p&gt;

&lt;p&gt;Let&#39;s start with the beautiful landscape we would like to take a picture of:&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN61rQk9DgKqcyTw-WtTz3NRqHMgao76XjJ3fALrRNMTR6WbzwhVxD5-GIm2v-K-v3RSbAQ_omY5AuvWKkS3HiTB59S28kLcmiiy3yauQX6RjAe1vF5-gt7sH5p7CavMIt7qkFPPW5KUyj/s1600/drawing1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN61rQk9DgKqcyTw-WtTz3NRqHMgao76XjJ3fALrRNMTR6WbzwhVxD5-GIm2v-K-v3RSbAQ_omY5AuvWKkS3HiTB59S28kLcmiiy3yauQX6RjAe1vF5-gt7sH5p7CavMIt7qkFPPW5KUyj/s1600/drawing1.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Now image to enjoy that landscape thru a window: it seems like our view is less
detailed than before, isn&#39;t it?&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEVUXuv_7K6C5OYMJoZTsroXR5gNJ-BRZES4VAgYU9OzEw7HRuEISzk_3UyEbdG3T5_a05Dt7R3L4hyphenhyphen5wVn_7fOSGy4-tXUizFxjucpdBciaTfyf9aSQnnM6_MJA_CihyphenhyphenF3OwgFzX0G86R/s1600/drawing2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEVUXuv_7K6C5OYMJoZTsroXR5gNJ-BRZES4VAgYU9OzEw7HRuEISzk_3UyEbdG3T5_a05Dt7R3L4hyphenhyphen5wVn_7fOSGy4-tXUizFxjucpdBciaTfyf9aSQnnM6_MJA_CihyphenhyphenF3OwgFzX0G86R/s1600/drawing2.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;It is not true: what happens indeed if you bend down a little bit?  New details
appear..&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgicCBB87-5u7zyn92HMSy-dHiD4Lv_bQCrkZHn3aBnpTd2alJqV2qGW5GKg0uSQM7JsVMQEeHclV4iGZTxkbc36K9w5B-490xTsLaXanW4nQuQ6wctVt9vV9Sjbq5bh0Lwe_7eCt-CwxPw/s1600/drawing3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgicCBB87-5u7zyn92HMSy-dHiD4Lv_bQCrkZHn3aBnpTd2alJqV2qGW5GKg0uSQM7JsVMQEeHclV4iGZTxkbc36K9w5B-490xTsLaXanW4nQuQ6wctVt9vV9Sjbq5bh0Lwe_7eCt-CwxPw/s1600/drawing3.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;And if you jump on a chair?  The same, some details appear, some details go
away.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR1GkcWwt_XwsXHndMSfvuPI3meXIpyCDRHdMJUNtCnSrgG9xCowuCk6ervAjCKGRqdBfODbWz_N98x7O0zPKHGV2GeFQrfoojN-H0UxmbqQW0sXh_6waZLUU6cUJzozJwf4ntOia8uJUI/s1600/drawing4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR1GkcWwt_XwsXHndMSfvuPI3meXIpyCDRHdMJUNtCnSrgG9xCowuCk6ervAjCKGRqdBfODbWz_N98x7O0zPKHGV2GeFQrfoojN-H0UxmbqQW0sXh_6waZLUU6cUJzozJwf4ntOia8uJUI/s1600/drawing4.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Very &lt;strong&gt;remarkable&lt;/strong&gt; isn&#39;t it?  Joking apart, we should have now all the pieces
we need synthesize that &lt;em&gt;perspective&lt;/em&gt; effect.&lt;/p&gt;

&lt;h4&gt;Practice&lt;/h4&gt;

&lt;p&gt;With the outcome of these observations at hand we are going to mimic that
effect by means of a jQuery plugin.  In particular, such plugin should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;take the pictures -- the landscape -- on which to apply the effect&lt;/li&gt;
&lt;li&gt;embed them into &lt;em&gt;cropping&lt;/em&gt; containers -- the window -- enabling us to hide
or reveal details on demand&lt;/li&gt;
&lt;li&gt;move the pictures up and down depending on the center of the point of view
-- this is the twist: in the real world we are the ones moving in respect of
the window while here we stand still and move the windows up or down&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;Collect the pictures&lt;/h5&gt;

&lt;p&gt;That is the easiest of the steps: create a new &#39;.js&#39; file containing the
standard boilerplate code needed to extend jQuery:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function ($) {
    $.fn.perspective = function() {
        return this.each(function() {
            // Logic to execute on each selected element
        });
    };
}(window.jQuery));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should enable us to do something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$(&#39;img&#39;).perspective();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or even better:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$(&#39;.perspective&#39;).perspective();
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Crop selected items&lt;/h5&gt;

&lt;p&gt;Even this one shouldn&#39;t be an hard task -- at least for all the HTML5/CSS gurus
out there;  unfortunately I am not one of those, but with the help of Google
I spot &lt;a href=&quot;http://www.squareonemd.co.uk/how-to-crop-an-image-with-a-css-class/&quot;&gt;this interesting blog
article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you know the trick, the rest is easy:  let&#39;s create a new &lt;code&gt;Perspective&lt;/code&gt;
object (?!) in charge of creating the &lt;em&gt;cropping&lt;/em&gt; element around each selected
item.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function ($) {
    var $window = $(window);

    var perspectives = [];

    var Perspective = function(element, options) {
        this.$element = $(element);
        this.options = $.extend({}, Perspective.defaults, options);
        this.$cropper = this.$element.wrap(
            $(&#39;&amp;lt;div&amp;gt;&#39;).addClass(&#39;perspective-cropper&#39;).css({
                position: &#39;relative&#39;,
                overflow: &#39;hidden&#39;,
                width: this.$element.width() + &#39;px&#39;,
                height: (this.$element.height() - 2 * this.options.maxOffset) + &#39;px&#39;

            })).parent();
    };

    Perspective.defaults = {
        maxOffset: 30
    };

    $.fn.perspective = function(options) {
        return this.each(function() {
            var data = new Perspective(this, options);

            perspectives.push(data);
        });
    };
}(window.jQuery));
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Offset images&lt;/h5&gt;

&lt;p&gt;We finally arrived to the last piece of the puzzle:  set the offset of the
&lt;em&gt;cropped&lt;/em&gt; image given the position of the &lt;em&gt;cropper&lt;/em&gt; container in respect of the
visible portion of the page -- I wage you a dollar if you understood a bit of
what I said.&lt;/p&gt;

&lt;p&gt;Let me try again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the &lt;em&gt;cropper&lt;/em&gt; container in the &lt;strong&gt;bottom&lt;/strong&gt; part of the visible area?  Then
shift the &lt;em&gt;cropped&lt;/em&gt; image &lt;strong&gt;up&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Is the &lt;em&gt;cropper&lt;/em&gt; container in the &lt;strong&gt;top&lt;/strong&gt; part of the visible area?  Then shift
the &lt;em&gt;cropped&lt;/em&gt; image &lt;strong&gt;down&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Is the &lt;em&gt;cropper&lt;/em&gt; container in the &lt;strong&gt;middle&lt;/strong&gt; of the visible area?  Then do
&lt;strong&gt;nothing&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More or less this translates into the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ...

    Perspective.prototype.scroll = function(e) {
        var docViewHalfHeight = $window.height()/2;
        var docViewMiddle = $window.scrollTop() + docViewHalfHeight;

        var cropperHeight = this.$cropper.height();
        var cropperTop = this.$cropper.offset().top;
        var cropperMiddle = cropperTop + cropperHeight/2;

        var offset = cropperMiddle - docViewMiddle;

        var elementTop = cropperTop;
        // Make sure image is centered while in the middle of the view
        elementTop -= this.options.maxOffset;
        // Add an additional offset to create the perspective effect
        elementTop += this.options.maxOffset * offset / docViewHalfHeight;

        this.$element.offset({top: elementTop});
    };

    $.fn.perspective = function(options) {
        return this.each(function() {
            var data = new Perspective(this, options);

            perspectives.push(data);

            data.scroll();
        });
    };

    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Attach an handler to the &#39;scroll&#39; event of the window:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ...

    $(window).on(&#39;scroll&#39;, function(e) {
        $.each(perspectives, function() {
            this.scroll(e);
        });
    });
}(window.jQuery));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Done!&lt;/p&gt;

&lt;h4&gt;Showtime&lt;/h4&gt;

&lt;p&gt;I prepared a GitHub page to show you &lt;strong&gt;perspective&lt;/strong&gt; in action: unfortunately
it doesn&#39;t match the expectations -- the movement is not that smooth and to
enjoy it best I suggest you scroll the page by using the browser scrollbar.&lt;/p&gt;

&lt;p&gt;Anyway, it&#39;s a thing: &lt;a href=&quot;http://iamfirecracker.github.io/perspective/&quot;&gt;enjoy it&lt;/a&gt;!&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/4517763785603625934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2014/01/jquery-perspective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/4517763785603625934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/4517763785603625934'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2014/01/jquery-perspective.html' title='jQuery Perspective'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/09744311058730946457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN61rQk9DgKqcyTw-WtTz3NRqHMgao76XjJ3fALrRNMTR6WbzwhVxD5-GIm2v-K-v3RSbAQ_omY5AuvWKkS3HiTB59S28kLcmiiy3yauQX6RjAe1vF5-gt7sH5p7CavMIt7qkFPPW5KUyj/s72-c/drawing1.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Viareggio Lucca, Italy</georss:featurename><georss:point>43.8657267 10.2513103</georss:point><georss:box>43.8199382 10.1706293 43.911515200000004 10.3319913</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-6496945307461183153</id><published>2013-08-03T11:18:00.000+02:00</published><updated>2013-08-03T11:18:41.672+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="barduino"/><category scheme="http://www.blogger.com/atom/ns#" term="camp"/><category scheme="http://www.blogger.com/atom/ns#" term="hackitaly"/><title type='text'>HackItaly Camp 2013:  Barduino</title><content type='html'>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;480 Hackers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2187 Red Bull&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100 KG of Pasta&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;120 KG of Fish&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;138240 Minutes of Hacking&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;50 Projects&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/EyVDYtR9wkM&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;strong&gt;Our project?&lt;/strong&gt;  &lt;em&gt;Barduino&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is it?&lt;/strong&gt;  Have you ever heard about
&lt;a href=&quot;http://www.kickstarter.com/projects/partyrobotics/bartendro-a-cocktail-dispensing-robot&quot;&gt;Bartrendo&lt;/a&gt;,
the cocktail dispenser robot?  Well, we decided to recreate our own version of
it, because quoting Richard Feynman:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What I cannot create, I do not understand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The hardware we had handy was not alike, but what made our product different
was the way people could reserve their drink, that is &lt;em&gt;leaving a message on
a Facebook event wall&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it work?&lt;/strong&gt;  It is very simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a private Facebook event for the party and invite all the guests&lt;/li&gt;
&lt;li&gt;Bring your Barduino to the venue and link it to the Facebook event using the
admin interface (we used a small webapp running on the laptop communicating
with the hardware)&lt;/li&gt;
&lt;li&gt;Tell the guests the list of available drinks (i.e. the strings recognized as
valid drinks)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That&#39;s all.  Want a Negroni?  Post a new message on the event wall containing
the word &lt;em&gt;negroni&lt;/em&gt; and Barduino will respond you with the number of queued
drinks.  When it is your turn it will tell you to come to the cocktail station,
and grab your drink.&lt;/p&gt;

&lt;p&gt;Here is the video we used for the pitch:&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/P1fJNr8keSs&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;We know, we are not good at filming - and pitching too - too much focus on the
admin interface and the Facebook interaction workflow rather than hardware
closeups, but you know, when time is running out and you are feared not to be
able to pitch anything at all, you start doing things chaotically.&lt;/p&gt;

&lt;p&gt;Life is learning, enjoy your drinks!&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/6496945307461183153/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2013/08/hackitaly-camp-2013-barduino.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6496945307461183153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6496945307461183153'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2013/08/hackitaly-camp-2013-barduino.html' title='HackItaly Camp 2013:  Barduino'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/EyVDYtR9wkM/default.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Viareggio, Lucca, Italy</georss:featurename><georss:point>43.8516258 10.271229700000049</georss:point><georss:box>43.7600273 10.109868200000049 43.943224300000004 10.432591200000049</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-6954018428144140535</id><published>2013-01-12T18:28:00.000+01:00</published><updated>2013-01-12T18:30:51.363+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="notation"/><category scheme="http://www.blogger.com/atom/ns#" term="polish"/><category scheme="http://www.blogger.com/atom/ns#" term="reverse"/><category scheme="http://www.blogger.com/atom/ns#" term="rpn"/><title type='text'>RPN</title><content type='html'>&lt;p&gt;The very first time I heard about &lt;a href=&quot;http://en.wikipedia.org/wiki/Reverse_Polish_notation&quot;&gt;Reverse Polish
Notation&lt;/a&gt; was at
University, during the class of Digital Electronics.&lt;/p&gt;

&lt;p&gt;There was that &lt;em&gt;fool&lt;/em&gt; professor, a genius in reality, so pedantic while
correcting your exam exercises and so rigorous with students [1],
that one day begun talking about that &lt;em&gt;crazy&lt;/em&gt; calculator (using &lt;em&gt;reverse polish
notation&lt;/em&gt; obviously!) he always had in his pocket.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://4.bp.blogspot.com/-Uew6KeGb3gs/UPGRiLSUCLI/AAAAAAAAEMo/SEI_-eAi054/s1600/calculator.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;258&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-Uew6KeGb3gs/UPGRiLSUCLI/AAAAAAAAEMo/SEI_-eAi054/s400/calculator.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;&quot;You don&#39;t need brackets..&quot; he said, and again &quot;..you don&#39;t even need to enter
the full expression first to evaluate the result;  rather you can start typing
in operands and operators and the evaluation will be done automatically, when
possible&quot;.&lt;/p&gt;

&lt;p&gt;You know, from a student&#39;s point of view, he was too &lt;em&gt;crazy&lt;/em&gt; to catch your
attention, so, as the vast majority of the students attending that class,
I started ignoring his small talk and &lt;em&gt;reverse polish notation&lt;/em&gt; just became
a &lt;em&gt;fairy tale&lt;/em&gt; in my mind.&lt;/p&gt;

&lt;p&gt;Time passed, and one day I stepped into a page of a guy talking about
&lt;em&gt;operators&lt;/em&gt; and their different
&lt;a href=&quot;http://www.cs.man.ac.uk/~pjj/cs2121/fix.html&quot;&gt;notations&lt;/a&gt;.  What really caught
my attention was &lt;strong&gt;postfix&lt;/strong&gt; notation (e.g. &lt;code&gt;1 2 +&lt;/code&gt; to get &lt;code&gt;3&lt;/code&gt;);  and you know
why?  Because it recalled to my mind the &lt;em&gt;reverse polish notation&lt;/em&gt; tale.&lt;/p&gt;

&lt;p&gt;So I started digging on the Internet and found some interesting pages about this
&lt;em&gt;crazy&lt;/em&gt; calculator, about practical implications of that &lt;em&gt;fool&lt;/em&gt; notation and
even about possible implementations, in different programming languages, of RPN
parsers.&lt;/p&gt;

&lt;p&gt;I really liked the idea of a stack used to keep track of the &lt;em&gt;operands&lt;/em&gt; of the
expression to evaluate, and the fact that computations could be executed as as
soon as an &lt;em&gt;operator&lt;/em&gt; was read by the parser, and not at the end.&lt;/p&gt;

&lt;p&gt;I immediately thought:  &lt;strong&gt;How hard will it be to build a naive evaluator of
&lt;em&gt;reverse polish notation&lt;/em&gt; expressions?&lt;/strong&gt;  (with &lt;em&gt;naive&lt;/em&gt; I mean at least to be
able to process the four most important operators &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Well, after a bit of tinkering I come up with the following straightforward
solution:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import operator

OPERATORS = {&#39;+&#39;: operator.add, &#39;-&#39;: operator.sub,
             &#39;*&#39;: operator.mul, &#39;/&#39;: operator.div}

def rpn(it):
    def rpnreduce(operands, value):
        if value in OPERATORS:
            return operands + [OPERATORS[value](
                        *reversed([operands.pop(), operands.pop()]))]
        else:
            return operands + [float(value)]
    return reduce(rpnreduce, it, [])[0]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And it works too!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;assert 3 == rpn(&#39;1 2 +&#39;.split())
assert 9.5 == rpn(&#39;10 2 * 4 5 - + 2 /&#39;.split())
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Can you come up with a shorter or more elegant solution than mine? ;-)&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
[1] If during his lessons he had given us 15 minutes of rest and after 10
minutes all the students were already come back in class to carry on the
lecture, he would have waited in front of the students for 5 interminable
minutes before start speaking again!
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/6954018428144140535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2013/01/rpn.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6954018428144140535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6954018428144140535'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2013/01/rpn.html' title='RPN'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-Uew6KeGb3gs/UPGRiLSUCLI/AAAAAAAAEMo/SEI_-eAi054/s72-c/calculator.jpg" height="72" width="72"/><thr:total>2</thr:total><georss:featurename>Viareggio, Province of Lucca, Italy</georss:featurename><georss:point>43.8516258 10.271229700000049</georss:point><georss:box>43.7600268 10.109868200000049 43.9432248 10.432591200000049</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-6241267199268035003</id><published>2012-11-03T12:35:00.001+01:00</published><updated>2012-11-03T12:35:55.533+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="automate"/><category scheme="http://www.blogger.com/atom/ns#" term="blogger"/><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><title type='text'>Automate: from Vim to Blogger</title><content type='html'>&lt;p&gt;Our days are made of 24 hours, of which lot of them are &lt;strong&gt;rightly&lt;/strong&gt; spent
sleeping, eating, and carrying on social relations etc.  I usually spend the
remaining time in front of a monitor, &lt;em&gt;randomly&lt;/em&gt; typing keys on the keyboard and
doing lot of ...  &lt;strong&gt;repetitive&lt;/strong&gt; tasks.&lt;/p&gt;

&lt;p&gt;I don&#39;t want to waste my time doing repetitive tasks;  computers are so good
at it, so why don&#39;t educate them to do the boring stuff for us?&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://2.bp.blogspot.com/-bNG70wWambI/UJLIwCUpPJI/AAAAAAAAEJY/z1zK_jxWwAA/s1600/all_the_things.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;300&quot; width=&quot;400&quot; src=&quot;http://2.bp.blogspot.com/-bNG70wWambI/UJLIwCUpPJI/AAAAAAAAEJY/z1zK_jxWwAA/s400/all_the_things.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;As an example I will show how, with &lt;a href=&quot;http://www.vim.org/&quot;&gt;Vim&lt;/a&gt;, a couple of key
mappings and one hell simple
&lt;a href=&quot;http://learnvimscriptthehardway.stevelosh.com/&quot;&gt;Vimscript&lt;/a&gt; function saved me
a lot of time while writing new blog entries.&lt;/p&gt;

&lt;h4&gt;A case study: me&lt;/h4&gt;

&lt;p&gt;You know it, my blogging platform is &lt;a href=&quot;http://www.blogger.com&quot;&gt;Blogger&lt;/a&gt;, and for
those of you who never used it, it offers two different ways to compose/edit
posts: via a &lt;a href=&quot;http://en.wikipedia.org/wiki/WYSIWYG&quot;&gt;WYSIWYG&lt;/a&gt; editor, or using
a plain &lt;em&gt;textarea&lt;/em&gt; in which you can see and edit the HTML of the page to be
published.&lt;/p&gt;

&lt;p&gt;I use Vim on a day-to-day basis and I use it to write blog posts too;  HTML is
a very &lt;em&gt;tedious&lt;/em&gt; language to work with, so I usually write new blog entries in
&lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;, type a couple of
commands to produce HTML, and finally copy the generated code into the prior
mentioned Blogger textarea.&lt;/p&gt;

&lt;p&gt;But, what are those &lt;em&gt;couple of commands&lt;/em&gt; I type before switching to Blogger to
paste the content in the textarea?&lt;/p&gt;

&lt;h5&gt;Generate HTML sources&lt;/h5&gt;

&lt;p&gt;For example, generate the HTML for the blog entry, and this is done with
a simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ markdown blog.mdown &amp;gt; blog.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But that&#39;s not all!  If you put one ore more &lt;em&gt;headers&lt;/em&gt; into your source Markdown
file (e.g. &lt;code&gt;# Section&lt;/code&gt;, &lt;code&gt;## Subsection&lt;/code&gt; and so forth), after the conversion,
those will be transformed into HTML &lt;em&gt;header&lt;/em&gt; elements (e.g. &lt;code&gt;&amp;lt;h1&amp;gt;Section&amp;lt;/h1&amp;gt;&lt;/code&gt;,
&lt;code&gt;&amp;lt;h2&amp;gt;Subsection&amp;lt;/h2&amp;gt;&lt;/code&gt; ...).  Do you see the problem now?&lt;/p&gt;

&lt;p&gt;Blogger embeds blog entries into a page which already contains one or more HTML
&lt;em&gt;header&lt;/em&gt; elements, and if you just copy and paste the generated HTML into the
Blogger editor you will end-up with a page containing two &lt;code&gt;h1&lt;/code&gt; elements, maybe
two or more &lt;code&gt;h2&lt;/code&gt; elements and so forth.&lt;/p&gt;

&lt;p&gt;Consequently, before the copy and paste thing I have to edit the file and shift
all the HTML headers &lt;em&gt;4 positions ahead&lt;/em&gt;, so that a generated &lt;code&gt;h1&lt;/code&gt; element will
become a &lt;code&gt;h4&lt;/code&gt;, a generated &lt;code&gt;h2&lt;/code&gt; element will become a &lt;code&gt;h5&lt;/code&gt;, &lt;em&gt;etc&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Don&#39;t ask why I go for &lt;strong&gt;4&lt;/strong&gt; positions: I have always been used to it, and it
works great!  (at least for my theme)&lt;/p&gt;

&lt;h5&gt;Images&lt;/h5&gt;

&lt;p&gt;One good thing about Markup is that you can easily embed HTML code in it:
&lt;code&gt;markdown&lt;/code&gt; will automatically ignore HTML tags and will place them in the right
position of the generated file.&lt;/p&gt;

&lt;p&gt;Why am I saying it?  Because unless you link images using URLs pointing to other
web pages, Blogger wants you to upload files on their image hosting service and
after a successful upload it will automatically link the image to your blog
entry in a form of &lt;code&gt;div&lt;/code&gt; + &lt;code&gt;a&lt;/code&gt; + &lt;code&gt;img&lt;/code&gt; tags.&lt;/p&gt;

&lt;p&gt;As you can see, once you are satisfied with all the images of your post you have
to switch to Blogger, upload all the images and finally copy all the
automatically generated tags from the textarea back to your Markup file.
Unfortunately, this is a &lt;strong&gt;necessary&lt;/strong&gt; step, and till now I have not found yet
a way to automate it.&lt;/p&gt;

&lt;p&gt;So go ahead, and live with it!&lt;/p&gt;

&lt;h5&gt;Preview work in progress&lt;/h5&gt;

&lt;p&gt;Another key point of my workflow is &lt;em&gt;previewing&lt;/em&gt; blog entries.  I often want to
have a look at how the post looks like in a browser (plain HTML, without Blogger
stylesheets), and to do that I usually pipe the generated HTML code into
programs like &lt;a href=&quot;http://rtomayko.github.com/bcat/&quot;&gt;bcat&lt;/a&gt; or
&lt;a href=&quot;https://gist.github.com/3968317/05d167b9686f3afc2bf8a2b6da7e1b0af64ff35b&quot;&gt;browser&lt;/a&gt;,
so that a new tab with the blog entry will be automatically created a presented
to me.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ markdown blog.mdown | browser
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Educate the editor&lt;/h4&gt;

&lt;p&gt;How much of the workflow presented so far could be automated?  A lot, let&#39;s see
how.&lt;/p&gt;

&lt;p&gt;First of all, configure Vim by creating a custom &lt;em&gt;filetype&lt;/em&gt;, &lt;code&gt;blogger&lt;/code&gt;, and make
it automatically loaded for &lt;code&gt;*.blogger&lt;/code&gt; files (remember, the syntax for these
files should be &lt;code&gt;markdown&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;augroup ft_blogger
    au!

    au BufNewFile,BufRead *.blogger setlocal filetype=blogger
    au Filetype blogger setlocal filetype=markdown

    au Filetype blogger setlocal spell
    au Filetype blogger setlocal norelativenumber nonumber

augroup END
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that even if we &lt;em&gt;globally&lt;/em&gt; bind the &lt;code&gt;blogger&lt;/code&gt; filetype to all the files
with a &lt;code&gt;.blogger&lt;/code&gt; extension, internally we still want to use &lt;code&gt;markdown&lt;/code&gt; as our
language type so that we can use its syntax rules and its key mappings.&lt;/p&gt;

&lt;p&gt;Now let&#39;s start to tackle &lt;em&gt;all&lt;/em&gt; the problems presented before.&lt;/p&gt;

&lt;h5&gt;Previews&lt;/h5&gt;

&lt;p&gt;This is easy;  simply create a key-mapping to invoke &lt;code&gt;markdown&lt;/code&gt; and pipe its
output to &lt;code&gt;browser&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;au Filetype blogger nnoremap &amp;lt;buffer&amp;gt; &amp;lt;localleader&amp;gt;p :!markdown % \| browser&amp;lt;cr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can change the mapping to whatever you like, but I think &lt;code&gt;p&lt;/code&gt; (short for
&lt;em&gt;preview&lt;/em&gt;) shouldn&#39;t be too hard to learn for your muscle memory.&lt;/p&gt;

&lt;p&gt;If you are wondering why I chose &lt;code&gt;browser&lt;/code&gt; instead of &lt;code&gt;bcat&lt;/code&gt;, here is the story:
I have not been able to properly display pages containing link to local images,
and I guess the problem is due to the temporary webserver which gets shut down
before all the page content is actually fetched by the browser.  I even tried
&lt;code&gt;-p&lt;/code&gt; and &lt;code&gt;--persist&lt;/code&gt; switches with no success.&lt;/p&gt;

&lt;p&gt;So I switched to &lt;code&gt;browser&lt;/code&gt;, but unfortunately, it had its limitations too.  The
script basically pipes its input into a temporary file which is then opened with
the browser.  The main problem with it is that all the temporary files are
created inside the &lt;code&gt;/tmp&lt;/code&gt; folder, consequently if the page contains relative
references to local images, all of them will end up being screwed up.&lt;/p&gt;

&lt;p&gt;That&#39;s why I used &lt;a href=&quot;https://gist.github.com/3968317/57ae119461be729a62413dafc45fbe3b8d503a3e&quot;&gt;my
forked&lt;/a&gt;
version of &lt;code&gt;browser&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;Generate, transform, copy&lt;/h5&gt;

&lt;p&gt;Now the &lt;em&gt;tricky&lt;/em&gt; section; create a mapping for a procedure in charge of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate HTML from Markdown&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Shift&lt;/em&gt; header numbers&lt;/li&gt;
&lt;li&gt;Copy everything to the clipboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are a bit familiar with &lt;a href=&quot;http://en.wikipedia.org/wiki/List_of_Unix_utilities&quot;&gt;Unix command line
tools&lt;/a&gt;, you should have
noticed that every single step of the previous list can be done using a simple
shell command (i.e. &lt;code&gt;markdown&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;xclip&lt;/code&gt;): we just need to find the
right &lt;em&gt;dash&lt;/em&gt; options!&lt;/p&gt;

&lt;p&gt;The first point of the list is the easiest one;  we just need to invoke
&lt;code&gt;markdown&lt;/code&gt; on the current file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;!markdown %
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now it is the turn of &lt;code&gt;sed&lt;/code&gt;: with it, we have to &lt;em&gt;shift&lt;/em&gt; all the HTML headers by
4 positions:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sed
\       -e &quot;s:\(&amp;lt;/*h\)1\(&amp;gt;\):\14\2:g&quot;
\       -e &quot;s:\(&amp;lt;/*h\)2\(&amp;gt;\):\15\2:g&quot;
\       -e &quot;s:\(&amp;lt;/*h\)3\(&amp;gt;\):\16\2:g&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What&#39;s happening here?  We are passing &lt;code&gt;sed&lt;/code&gt; three different patterns to
transform strings like &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;/h1&amp;gt;&lt;/code&gt; into &lt;code&gt;&amp;lt;h4&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/h4&amp;gt;&lt;/code&gt; respectively,
strings like &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/h2&amp;gt;&lt;/code&gt; into &lt;code&gt;&amp;lt;h5&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/h5&amp;gt;&lt;/code&gt; and so forth: it is all
about some &lt;a href=&quot;http://stackoverflow.com/questions/5261484/regular-expressions-marked-subexpression&quot;&gt;marked
subexpression&lt;/a&gt;
magic!&lt;/p&gt;

&lt;p&gt;Finally we want to copy the generated HTML into the OS clipboard.  Once you have
&lt;code&gt;xclip&lt;/code&gt; installed on your machine, this step is really straightforward:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xclip -selection clip-board
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wondering why I am using &lt;code&gt;-selection clip-board&lt;/code&gt; instead of copying everything
in the &lt;em&gt;X selection&lt;/em&gt;?  Simple, not to lose everything the moment I open the
Blogger page and try to delete all the content of the textarea: if you select
all the text and then press &lt;code&gt;Del&lt;/code&gt;, the X selection will then contain the deleted
text, and all your previously generated HTML code will get lost!&lt;/p&gt;

&lt;p&gt;Finally, create a function chaining all those three commands together:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function! GenerateTransformAndCopy() &quot; {{{
    !markdown %
        \ | sed
        \       -e &quot;s:\(&amp;lt;/*h\)1\(&amp;gt;\):\14\2:g&quot;
        \       -e &quot;s:\(&amp;lt;/*h\)2\(&amp;gt;\):\15\2:g&quot;
        \       -e &quot;s:\(&amp;lt;/*h\)3\(&amp;gt;\):\16\2:g&quot;
        \ | xclip -selection clip-board
endfunction &quot; }}}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And create add a mapping for it (&lt;code&gt;c&lt;/code&gt; short  for &lt;em&gt;copy&lt;/em&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;au Filetype blogger nnoremap &amp;lt;buffer&amp;gt; &amp;lt;localleader&amp;gt;c :call GenerateTransformAndCopy()&amp;lt;cr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Wrapping up&lt;/h4&gt;

&lt;p&gt;After putting all those things together, here is how my &lt;code&gt;.vimrc&lt;/code&gt; file looks
like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&quot; Blogger {{{

augroup ft_blogger
    au!

    au BufNewFile,BufRead *.blogger setlocal filetype=blogger
    au Filetype blogger setlocal filetype=markdown

    au Filetype blogger setlocal spell
    au Filetype blogger setlocal norelativenumber

    &quot; Markdown the current file and pipe the output to bcat/browser
    au Filetype blogger nnoremap &amp;lt;buffer&amp;gt; &amp;lt;localleader&amp;gt;p :!markdown % \| browser&amp;lt;cr&amp;gt;

    &quot; Markdown the current file, prepare it for Blogger, and finally copy it to
    &quot; clipboard
    function! GenerateTransformAndCopy() &quot; {{{
        !markdown %
            \ | sed
            \       -e &quot;s:\(&amp;lt;/*h\)1\(&amp;gt;\):\14\2:g&quot;
            \       -e &quot;s:\(&amp;lt;/*h\)2\(&amp;gt;\):\15\2:g&quot;
            \       -e &quot;s:\(&amp;lt;/*h\)3\(&amp;gt;\):\16\2:g&quot;
            \ | xclip -selection clip-board
    endfunction &quot; }}}
    au Filetype blogger nnoremap &amp;lt;buffer&amp;gt; &amp;lt;localleader&amp;gt;c :call GenerateTransformAndCopy()&amp;lt;cr&amp;gt;
augroup END

}}}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here we are: no more switch back and forth between VIM and your favourite
terminal emulator to preview the current file or prepare the generated code to
be pasted into your favorite blogging platform.&lt;/p&gt;

&lt;p&gt;A couple of keystrokes and your time will not be wasted anymore.  And remember:
focus on the &lt;em&gt;worthy&lt;/em&gt; tasks only!&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/6241267199268035003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/11/automate-from-vim-to-blogger.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6241267199268035003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6241267199268035003'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/11/automate-from-vim-to-blogger.html' title='Automate: from Vim to Blogger'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-bNG70wWambI/UJLIwCUpPJI/AAAAAAAAEJY/z1zK_jxWwAA/s72-c/all_the_things.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Viareggio, Province of Lucca, Italy</georss:featurename><georss:point>43.8516258 10.2712297</georss:point><georss:box>43.8058233 10.1922657 43.8974283 10.350193699999998</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-3568421034844071001</id><published>2012-11-01T11:59:00.001+01:00</published><updated>2012-11-01T11:59:39.849+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="algorithms"/><category scheme="http://www.blogger.com/atom/ns#" term="search"/><category scheme="http://www.blogger.com/atom/ns#" term="ternary"/><title type='text'>Ternary Search</title><content type='html'>&lt;p&gt;Today I would like to talk you about a simple but at the same time very powerful
algorithm handy to minimize (or maximize)
&lt;a href=&quot;http://en.wikipedia.org/wiki/Unimodality#Unimodal_function&quot;&gt;unimodal&lt;/a&gt;
functions: &lt;em&gt;Ternary Search&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The origin of its name comes from the fact that the search domain is reduced by
one third on each iteration : it is not as fast as &lt;em&gt;binary search&lt;/em&gt; but remember
that (1) they serve different purposes and (2) it has still &lt;em&gt;logarithmic&lt;/em&gt;
complexity, so it should be fast &lt;strong&gt;enough&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;I ain&#39;t good at explaining &lt;em&gt;theoretical&lt;/em&gt; stuff so rather than struggle trying
to give you an approximate definition of the algorithm, I will point point you
out to the &lt;a href=&quot;http://en.wikipedia.org/wiki/Ternary_search&quot;&gt;Wikipedia page&lt;/a&gt; and
then spend the rest of the time presenting you a visual explanation of the
algorithm and finally tell you where I happened to use it to solve &lt;em&gt;non-trivial&lt;/em&gt;
tasks.&lt;/p&gt;

&lt;h4&gt;Just a bit of theory&lt;/h4&gt;

&lt;p&gt;For the lazy readers who haven&#39;t clicked the previous link, the following
&lt;em&gt;quoted&lt;/em&gt; paragraphs should be enough to understand what is coming next:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A ternary search algorithm is a technique in computer science for finding the
minimum or maximum of a unimodal function (function that is either strictly
increasing and then strictly decreasing or vice versa).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A ternary search determines either that the minimum or maximum cannot be in
the first third of the domain or that it cannot be in the last third of the
domain, then repeats on the remaining two-thirds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In particular, the last paragraph tells us that in order to find the &lt;em&gt;minima&lt;/em&gt; of
the function, we have to iteratively split the input domain into three separate
regions and discard the one for which we know for sure the &lt;em&gt;minima&lt;/em&gt; won&#39;t be in
there.&lt;/p&gt;

&lt;p&gt;Seems easy, isn&#39;t it?  But how can we decide which of the three sub-domains is
the one to be discarded?  How can we compare the output of our function on those
&lt;em&gt;boundary&lt;/em&gt; points?  Let&#39;s try to analyze all the possible scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;func(left_third) &amp;gt; func(right_third)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://1.bp.blogspot.com/-b4mKKhybhRQ/UI955pdFWvI/AAAAAAAAEHQ/Sz22Bj0APfc/s1600/left_more_right.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://1.bp.blogspot.com/-b4mKKhybhRQ/UI955pdFWvI/AAAAAAAAEHQ/Sz22Bj0APfc/s400/left_more_right.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;What can we assert knowing that?  Well, if we combine it with the fact that we
  are trying to find the minimum value of a &lt;em&gt;unimodal&lt;/em&gt; function, for sure we can
  assert that we won&#39;t find that &lt;em&gt;minima&lt;/em&gt; in between the range &lt;code&gt;[left,
  left_third]&lt;/code&gt;.  Why are we so confident about that?  Because if the &lt;em&gt;minima&lt;/em&gt;
  was there, &lt;code&gt;func(left_third)&lt;/code&gt; should have been smaller than
  &lt;code&gt;func(right_third)&lt;/code&gt; (remember, the function is &lt;em&gt;unimodal&lt;/em&gt;)!  Summarizing, we
  define the new search domain as: &lt;code&gt;[left_third, right]&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;func(left_third) &amp;lt; func(right_third)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://3.bp.blogspot.com/-RqbMfBvclic/UI956e58dpI/AAAAAAAAEHc/C_dCBkMOScg/s1600/left_less_right.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://3.bp.blogspot.com/-RqbMfBvclic/UI956e58dpI/AAAAAAAAEHc/C_dCBkMOScg/s400/left_less_right.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;If you are still with me, you would have noticed that this is the opposite of
  the previous case;  this time however, we know for sure that the &lt;em&gt;minima&lt;/em&gt;
  won&#39;t lie in between &lt;code&gt;[right_third, right]&lt;/code&gt; and consequently we will cast our
  search domain into &lt;code&gt;[left, right_third]&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;func(left_third) = func(right_third)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://3.bp.blogspot.com/-0V7yvpcE5nk/UI957IkhQyI/AAAAAAAAEHo/sJKZ29t-2CY/s1600/left_equal_right.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://3.bp.blogspot.com/-0V7yvpcE5nk/UI957IkhQyI/AAAAAAAAEHo/sJKZ29t-2CY/s400/left_equal_right.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;That is the easiest scenario: the &lt;em&gt;minima&lt;/em&gt; is in between &lt;code&gt;left_third&lt;/code&gt; and
  &lt;code&gt;right_third&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Wrapping up, the idea is very simple: you have to minimize a function for
a given domain and you know that in such domain the function is &lt;em&gt;unimodal&lt;/em&gt;;  you
split the current domain in three parts and discard the one for which you know
for sure the &lt;em&gt;minima&lt;/em&gt; won&#39;t lie in there; finally you carry on the process until
the size of current search domain lessens a &lt;em&gt;pre-defined&lt;/em&gt; threshold (e.g.
desired precision).&lt;/p&gt;

&lt;h4&gt;Practice&lt;/h4&gt;

&lt;p&gt;Let&#39;s start with some practical stuff: imagine we want to minimize the function
&lt;code&gt;3 * (x - 0.25)**2 - 1&lt;/code&gt; in the domain &lt;code&gt;[0, 1]&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://4.bp.blogspot.com/-YZK_1pleSD8/UJJSxKrTkKI/AAAAAAAAEJE/tMDlCC7eRno/s1600/function.dat.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://4.bp.blogspot.com/-YZK_1pleSD8/UJJSxKrTkKI/AAAAAAAAEJE/tMDlCC7eRno/s400/function.dat.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;The algorithm tells us to split the function domain in three slices and to
evaluate the output of the function on those cut-off points.&lt;/p&gt;

&lt;p&gt;Because we are going to implement the algorithm &lt;em&gt;step-by-step&lt;/em&gt; using the Python
interpreter, let&#39;s start defining our function to minimize and its &lt;em&gt;initial&lt;/em&gt;
domain:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from __future__ import division
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; def func(x):
...     return 3 * (x - 0.25) ** 2 - 1
...
&amp;gt;&amp;gt;&amp;gt; (left, right) = [0, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now calculate the cut-off points used to create the three domain sections:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; left_third  = ((2 * left) + right) / 3 # left + ((right - left) / 3)
&amp;gt;&amp;gt;&amp;gt; left_third
0.3333333333333333
&amp;gt;&amp;gt;&amp;gt; right_third = (left + (2 * right)) / 3 # left + ((right - left) * 2 / 3)
0.6666666666666666
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, evaluate the function in such points:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; func(left_third)
-0.9791666666666666
&amp;gt;&amp;gt;&amp;gt; func(right_third)
-0.47916666666666674
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://3.bp.blogspot.com/-YpmrJ4bmg3E/UI958KS5l9I/AAAAAAAAEH0/ztY1oWPHtAE/s1600/function_first.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://3.bp.blogspot.com/-YpmrJ4bmg3E/UI958KS5l9I/AAAAAAAAEH0/ztY1oWPHtAE/s400/function_first.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;As you can see, &lt;code&gt;func(left_third)&lt;/code&gt; is smaller than &lt;code&gt;func(right_third)&lt;/code&gt;,
consequently we need to strip the range &lt;code&gt;[right_third, right]&lt;/code&gt; from our search
domain and then carry on the processing:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; right = right_third
&amp;gt;&amp;gt;&amp;gt; left_third  = ((2 * left) + right) / 3
&amp;gt;&amp;gt;&amp;gt; left_third
0.2222222222222222
&amp;gt;&amp;gt;&amp;gt; right_third = (left + (2 * right)) / 3
&amp;gt;&amp;gt;&amp;gt; right_third
0.4444444444444444
&amp;gt;&amp;gt;&amp;gt; func(left_third)
-0.9976851851851852
&amp;gt;&amp;gt;&amp;gt; func(right_third)
-0.8865740740740741
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://1.bp.blogspot.com/-CSHCHx2-9h4/UI958q3pllI/AAAAAAAAEIA/2hy9HFlHaao/s1600/function_second.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; width=&quot;400&quot; src=&quot;http://1.bp.blogspot.com/-CSHCHx2-9h4/UI958q3pllI/AAAAAAAAEIA/2hy9HFlHaao/s400/function_second.svg.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;OK, same situation as before, but I think you got the point.&lt;/p&gt;

&lt;p&gt;Why don&#39;t we create a function that implements all the reasoning explained so
far?  On Wikipedia there is already an
&lt;a href=&quot;http://en.wikipedia.org/wiki/Ternary_search#Recursive_algorithm&quot;&gt;implementation&lt;/a&gt;
of the algorithm based on recursion, so I wrote my own &lt;em&gt;iterative&lt;/em&gt; version:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; def minimize(func, a, b, precision):
...    (left, right) = (a, b)
...    while True:
...        if right - left &amp;lt;= precision:
...            return (left + right) / 2
...        left_third = ((2 * left) + right) / 3
...        right_third = (left + (2 * right)) / 3
...        if func(left_third) &amp;gt; func(right_third):
...            (left, right) = (left_third, right)
...        else:
...            (left, right) = (left, right_third)
...
&amp;gt;&amp;gt;&amp;gt; minimize(func, 0, 1, 10e-7)
0.24999996832273685
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you are wondering why the result is not a sharp &lt;code&gt;0.25&lt;/code&gt; (as expected), bear in
mind that we are looking for the &lt;em&gt;minima&lt;/em&gt; using an &lt;em&gt;iterative&lt;/em&gt; approach and not
a &lt;em&gt;mathematical&lt;/em&gt; approach: imprecisions are likely to be there!  However, you
can always use strings formatting magic to beatify the result:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; print(&quot;{0:.7f}&quot;.format(minimize(func, 0, 1, 10e-7)))
0.2500000
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Bingo!  What if, instead of looking for the &lt;em&gt;minima&lt;/em&gt;, we wanted to &lt;em&gt;maximize&lt;/em&gt;
the function?  There is a straightforward solution for that: &lt;em&gt;closures&lt;/em&gt;.  We can
use them to change the sign of all the values returned by the function we want
to maximize, and then use &lt;code&gt;minimize()&lt;/code&gt; to do all the dirty job:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; def neg(func):
...    def inner(*args, **kwargs):
...        return -1 * func(*args, **kwargs)
...    return inner
...
&amp;gt;&amp;gt;&amp;gt; def func1(x):
...    return -abs(x - .5)
...
&amp;gt;&amp;gt;&amp;gt; minimize(neg(func1), 0, 1, 10e-7)
0.5000000933420725
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or if you prefer, we can just create a &lt;code&gt;maximize()&lt;/code&gt; function doing all the
wrapping for us:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; def maximize(func, a, b, precision):
...     def neg(func):
...         def inner(*args, **kwargs):
...             return -1 * func(*args, **kwargs)
...         return inner
...     return minimize(neg(func), a, b, precision)
...
&amp;gt;&amp;gt;&amp;gt; maximize(func1, 0, 1, 10e-7)
0.5000000933420725
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Other applications&lt;/h4&gt;

&lt;p&gt;The first time I read about this algorithm was during the &lt;a href=&quot;http://code.google.com/codejam/&quot;&gt;Google Code
Jam&lt;/a&gt; edition of 2009.  I spent so much time
trying to implement the right math algorithm to solve the puzzle &lt;a href=&quot;http://code.google.com/codejam/contest/189252/dashboard#s=p1&amp;amp;a=1&quot;&gt;Center of
Mass&lt;/a&gt;, and
when the contest analysis was out, I found out that a &lt;strong&gt;stupid-simple&lt;/strong&gt; &lt;em&gt;ternary
search&lt;/em&gt; algorithm could have been used to solve that.&lt;/p&gt;

&lt;p&gt;The same happened to me three years later during the Google Code Jam edition
organized for the &lt;a href=&quot;https://ep2012.europython.eu/&quot;&gt;EuroPython 2012&lt;/a&gt;.  This time
the name of the puzzle was &lt;a href=&quot;http://code.google.com/codejam/contest/1985486/dashboard#s=p1&quot;&gt;Shoot the
Target&lt;/a&gt; and as
happened before, I wasted all my time trying to implement a solution which was
completely wrong (fuck!).  I even thought about using &lt;em&gt;ternary search&lt;/em&gt; to solve
the problem, but I was so sure about the correctness of my solution that I spent
all the available time trying to find rounding bugs and error like those (duh).
One week later, when the contest was over, I tried to solve the problem using
&lt;em&gt;ternary search&lt;/em&gt; and in a handful of minutes I had a solution running.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Always strive for the simplest solution that could possibly work.  Once you
have something working, if necessary, start to optimize it!&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;Gimme the code&lt;/h4&gt;

&lt;p&gt;All the code I presented so far can be downloaded from this
&lt;a href=&quot;https://gist.github.com/3978678&quot;&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cheers.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/3568421034844071001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/11/ternary-search.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3568421034844071001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3568421034844071001'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/11/ternary-search.html' title='Ternary Search'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-b4mKKhybhRQ/UI955pdFWvI/AAAAAAAAEHQ/Sz22Bj0APfc/s72-c/left_more_right.svg.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Viareggio, Province of Lucca, Italy</georss:featurename><georss:point>43.8516258 10.2712297</georss:point><georss:box>43.8058233 10.1922657 43.8974283 10.350193699999998</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-2248616496102442266</id><published>2012-09-23T11:46:00.000+02:00</published><updated>2012-09-23T11:46:34.627+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apple"/><category scheme="http://www.blogger.com/atom/ns#" term="insert"/><category scheme="http://www.blogger.com/atom/ns#" term="keyboard"/><category scheme="http://www.blogger.com/atom/ns#" term="shift"/><title type='text'>Linux and the Apple wireless keyboard: the insert key</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://4.bp.blogspot.com/-J6YwPjYOmec/UF7QY80YHlI/AAAAAAAADe0/fUusZuki8Gg/s1600/27217460.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;300&quot; width=&quot;400&quot; src=&quot;http://4.bp.blogspot.com/-J6YwPjYOmec/UF7QY80YHlI/AAAAAAAADe0/fUusZuki8Gg/s400/27217460.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Are you a user of the &lt;a href=&quot;http://www.apple.com/keyboard/&quot;&gt;Apple Wireless
keyboard&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you bored with the missing &lt;code&gt;Insert&lt;/code&gt; key?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you a Linux user?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Please, use &lt;a href=&quot;http://linux.die.net/man/1/xmodmap&quot;&gt;Xmodmap&lt;/a&gt; to map the combination &lt;code&gt;Shift+BackSpace&lt;/code&gt; to the &lt;code&gt;Insert&lt;/code&gt; key and go on with pasting things in the terminal
without using your mouse!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to do that?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following to your Xmodmap configuration file (i.e. &lt;code&gt;~/.Xmodmap&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;keycode  22 = BackSpace Insert
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then start/restart Xmodmap:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ xmodmap ~/.Xmodmap
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;How it works?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What Xmodmap will do under the hoods is to map the &lt;code&gt;BackSpace&lt;/code&gt; key (which on my
keyboard correspond to keycode &lt;code&gt;22&lt;/code&gt;) to the &lt;code&gt;BackSpace&lt;/code&gt;
&lt;a href=&quot;http://www.oasis-open.org/docbook/documentation/reference/html/keysym.html&quot;&gt;keysym&lt;/a&gt;
if used without modifiers, and to the &lt;code&gt;Insert&lt;/code&gt; keysym while used together with
the &lt;code&gt;Shift&lt;/code&gt; modifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I find the specific keycode for my keyboard?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is &lt;a href=&quot;http://www.xfree86.org/current/xev.1.html&quot;&gt;xev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open a terminal and type &lt;code&gt;xev | grep keycode&lt;/code&gt;, then look at the output of the
program while typing keys on your keyboard:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ xev | grep keycode
state 0x10, keycode 26 (keysym 0x65, e), same_screen YES,
state 0x10, keycode 22 (keysym 0xff08, BackSpace), same_screen YES,
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see the output contains both the information of the keycode (e.g.
&lt;code&gt;26&lt;/code&gt; and &lt;code&gt;22&lt;/code&gt;) and the name of the associated keysym (e.g. &lt;code&gt;e&lt;/code&gt; and &lt;code&gt;BackSpace&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;That&#39;s all folks!&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/2248616496102442266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/09/linux-and-apple-wireless-keyboard.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/2248616496102442266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/2248616496102442266'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/09/linux-and-apple-wireless-keyboard.html' title='Linux and the Apple wireless keyboard: the insert key'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-J6YwPjYOmec/UF7QY80YHlI/AAAAAAAADe0/fUusZuki8Gg/s72-c/27217460.jpg" height="72" width="72"/><thr:total>2</thr:total><georss:featurename>Viareggio, Province of Lucca, Italy</georss:featurename><georss:point>43.8657267 10.2513103</georss:point><georss:box>43.8199352 10.172346300000001 43.9115182 10.3302743</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-4578622738769473490</id><published>2012-09-15T14:18:00.000+02:00</published><updated>2012-09-15T14:18:46.583+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="facebook"/><category scheme="http://www.blogger.com/atom/ns#" term="oauth"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="webpy"/><title type='text'>Facebook login on top of webpy</title><content type='html'>&lt;p&gt;Have you ever tried to add a Facebook login link to your
&lt;a href=&quot;http://webpy.org&quot;&gt;webpy&lt;/a&gt; application?  If not, today I will try and explain
how to do that, without the aid of any external module.&lt;/p&gt;

&lt;p&gt;I found this useful
&lt;a href=&quot;http://facebook-python-library.docs-library.appspot.com/facebook-python/examples/oauth.html&quot;&gt;page&lt;/a&gt;
describing the steps to implement the Facebook Authorization hand-shake on top
of the Google App Engine framework, so I adapted it to to make it work with
webpy.&lt;/p&gt;

&lt;h4&gt;Authentication workflow&lt;/h4&gt;

&lt;p&gt;First and foremost, have a look at this
&lt;a href=&quot;https://developers.facebook.com/docs/authentication/server-side/&quot;&gt;page&lt;/a&gt; if you
are not familiar with the Facebook OAuth2 authethication flow:  it contains the
basic steps to allow applications to make requests to the Facebook Graph API.&lt;/p&gt;

&lt;p&gt;When done, go ahead.&lt;/p&gt;

&lt;h4&gt;Hello world!&lt;/h4&gt;

&lt;p&gt;Let&#39;s start with a &lt;em&gt;classic&lt;/em&gt;:  the beloved &lt;em&gt;Hello world!&lt;/em&gt; page, with a small
login link on top of it.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://1.bp.blogspot.com/-645YVzC8_sM/UFRvF9J3qLI/AAAAAAAADds/Ndac55En3vI/s1600/home.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;105&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-645YVzC8_sM/UFRvF9J3qLI/AAAAAAAADds/Ndac55En3vI/s320/home.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;To achieve that, create the main page handler:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class MainHandler(object):
    def GET(self):
        return render.index()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a file &lt;code&gt;index.html&lt;/code&gt; with the following content (I use
&lt;a href=&quot;http://jinja.pocoo.org/docs&quot;&gt;Jinja2&lt;/a&gt; as template engine:  see this
&lt;a href=&quot;http://webpy.org/cookbook/template_jinja&quot;&gt;guide&lt;/a&gt; if you want to configure
webpy application to use it):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;p style=&quot;font-size: small&quot;&amp;gt;&amp;lt;a href=&quot;/login&quot;&amp;gt;Facebook login&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Hello, world! &amp;lt;/p&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Done!&lt;/p&gt;

&lt;h4&gt;Login handler&lt;/h4&gt;

&lt;p&gt;Now, it is time to enter the &lt;em&gt;hot&lt;/em&gt; section of the post:  write a request
handler implementing the OAuth2 authentication flow.&lt;/p&gt;

&lt;h5&gt;Redirection&lt;/h5&gt;

&lt;p&gt;The first step of the procedure is to redirect the user to the Facebook
Authorization URL (&lt;code&gt;http://www.facebook.com/dialog/oauth&lt;/code&gt;), where she will be
shown the application authorization screen and she will be able to authorize -- or not --
our application.  The Authorization URL should include the &lt;em&gt;id&lt;/em&gt; of your application
(given by Facebook during the App registration procedure) and the &lt;em&gt;redirect URL&lt;/em&gt; used
by the auth server to drive the user back to your site after a successfull hand-shake.&lt;/p&gt;

&lt;p&gt;At the time being, webpy does not expose a built-in function to get the
&lt;code&gt;path_url&lt;/code&gt; of the current request, so we create a helper function for that:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def path_url():
    return web.ctx.home + web.ctx.fullpath
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we start to fill our &lt;code&gt;LoginHandler&lt;/code&gt; class with the redirection logic:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;args = dict(client_id=FACEBOOK_APP_ID, redirect_uri=path_url())
web.seeother(
        &#39;http://www.facebook.com/dialog/oauth?&#39; + urllib.urlencode(args))
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Access token&lt;/h5&gt;

&lt;p&gt;The user will be redirected to the page specified inside &lt;code&gt;redirect_uri&lt;/code&gt; after
she has granted permissions to our application.  The server doing such redirection
will add an addictional &lt;code&gt;code&lt;/code&gt; argument to the request:  such argument will
be used to get the &lt;em&gt;access token&lt;/em&gt; to make API requests.&lt;/p&gt;

&lt;p&gt;Once inside the handler, we need to read the input &lt;code&gt;code&lt;/code&gt; value, add the
application &lt;em&gt;secret token&lt;/em&gt;, and finally make a request to
&lt;code&gt;https://graph.facebook.com/oauth/access_token&lt;/code&gt; to get the access token.&lt;/p&gt;

&lt;p&gt;If you implement the logic above enhancing the previous snippet, you will end up
with something similar to this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;data = web.input(code=None)
args = dict(client_id=FACEBOOK_APP_ID, redirect_uri=path_url())
if data.code is None:
    web.seeother(
            &#39;http://www.facebook.com/dialog/oauth?&#39; + urllib.urlencode(args))
    return

args[&#39;code&#39;] = data.code
args[&#39;client_secret&#39;] = FACEBOOK_APP_SECRET
response = urlparse.parse_qs(
        urllib.urlopen(
            &quot;https://graph.facebook.com/oauth/access_token?&quot; +
                urllib.urlencode(args)).read())
access_token = response[&quot;access_token&quot;][-1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the presence of the &lt;code&gt;code&lt;/code&gt; argument differenciates the first step from
the second.&lt;/p&gt;

&lt;h5&gt;Facebook profile&lt;/h5&gt;

&lt;p&gt;We have the &lt;code&gt;access_token&lt;/code&gt;, then we are allowed to query the Graph API for
information about the user&#39;s profile:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;profile = json.load(
        urllib.urlopen(
            &quot;https://graph.facebook.com/me?&quot; +
            urllib.urlencode(dict(access_token=access_token))))
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Session management&lt;/h5&gt;

&lt;p&gt;The login procedure has come to its end but before redirecting the user back to
the home page we need somehow to store login information in a way that it can be
accessible while viewing other pages.&lt;/p&gt;

&lt;p&gt;We will use cookies for that.  In particular, we will store the &lt;code&gt;id&lt;/code&gt; and the
&lt;code&gt;name&lt;/code&gt; of the user so that the home page will be able display the name and the
profile picture of the authorized user.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;web.setcookie(
        &#39;fb_userid&#39;, str(profile[&#39;id&#39;]), expires=time.time() + 7 * 86400)
web.setcookie(
        &#39;fb_username&#39;, str(profile[&#39;name&#39;]), expires=time.time() + 7 * 86400)
return web.seeother(&#39;/&#39;)
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Logout handler&lt;/h4&gt;

&lt;p&gt;There ain&#39;t a &lt;code&gt;login&lt;/code&gt; page without a &lt;code&gt;logout&lt;/code&gt; one, so let&#39;s create the
&lt;code&gt;LogoutHandler&lt;/code&gt; class containing the necessary lines to clear cookies set in the
previous step.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class LogoutHandler():
    def GET(self):
        web.setcookie(&#39;fb_user&#39;, &#39;&#39;, expires=time.time() - 86400)
        web.seeother(&#39;/&#39;)
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Modified Hello, world!&lt;/h4&gt;

&lt;p&gt;We are almost done.  We just need to apply some changes to the &lt;code&gt;MainHandler&lt;/code&gt;
class and to the &lt;code&gt;index.html&lt;/code&gt; template page to take into account logged-in users.&lt;/p&gt;

&lt;p&gt;To do so, we create a helper class (inherited by all request handlers) which
exposes the method &lt;code&gt;current_user()&lt;/code&gt;.  Such method reads the cookies associated
with the connection and returns a dictionary containing the &lt;code&gt;id&lt;/code&gt; and the &lt;code&gt;name&lt;/code&gt;
of the logged-in user, if any.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class BaseHandler():
    def current_user(self):
        if not hasattr(self, &quot;_current_user&quot;):
            self._current_user = dict()
            user_id = web.cookies().get(&#39;fb_userid&#39;)
            user_name = web.cookies().get(&#39;fb_username&#39;)
            if user_id and user_name:
                self._current_user = {&#39;id&#39;: user_id, &#39;name&#39;: user_name}

        return self._current_user
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We modify &lt;code&gt;MainHandler&lt;/code&gt; to extend the &lt;code&gt;BaseHandler&lt;/code&gt; helper class and
to pass the current logged-in user to the template engine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class MainHandler(BaseHandler):
    def GET(self):
        return render.index(user=self.current_user())
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, we modify the &lt;code&gt;index.html&lt;/code&gt; template page to add support for the input
&lt;code&gt;user&lt;/code&gt; dictionary:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        {% if user %}
            &amp;lt;p style=&quot;font-size: small&quot;&amp;gt;&amp;lt;a href=&quot;/logout&quot;&amp;gt;Logout&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Hello, &amp;lt;a href=&quot;{{ user.profile_url }}&quot;&amp;gt;{{ user[&#39;name&#39;] }}&amp;lt;/a&amp;gt;!&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;img src=&quot;https://graph.facebook.com/{{ user[&#39;id&#39;] }}/picture&quot; /&amp;gt;&amp;lt;/p&amp;gt;
        {% else %}
            &amp;lt;p style=&quot;font-size: small&quot;&amp;gt;&amp;lt;a href=&quot;/login&quot;&amp;gt;Facebook login&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Hello, world! &amp;lt;/p&amp;gt;
        {% endif %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you open again the index page, click on the Facebook login link and wait for
the authentication hand-shake to be over, the page will be automatically
reloaded and you should be able to see your Facebook name and profile picture.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://1.bp.blogspot.com/-bYbqRJ-OVVU/UFRsgLxSdgI/AAAAAAAADdg/iwakFkwCYnw/s1600/home-logged.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;144&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-bYbqRJ-OVVU/UFRsgLxSdgI/AAAAAAAADdg/iwakFkwCYnw/s320/home-logged.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h4&gt;Wrapping up&lt;/h4&gt;

&lt;p&gt;You have just learned how to integrate a Facebook login link to your webpy
application.  Simple, isn&#39;t it?&lt;/p&gt;

&lt;p&gt;Where to go next?  Use a database to keep track of the users connected with your
application (I created
a &lt;a href=&quot;https://bitbucket.org/iamFIREcracker/webpy-facebook-login&quot;&gt;Bitbucket&lt;/a&gt; example
project implementing that), or try to integrate
&lt;a href=&quot;https://github.com/DaGoodBoy/python-oauth2&quot;&gt;python-oauth2&lt;/a&gt; within your
application.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/4578622738769473490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/09/facebook-login-on-top-of-webpy.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/4578622738769473490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/4578622738769473490'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/09/facebook-login-on-top-of-webpy.html' title='Facebook login on top of webpy'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-645YVzC8_sM/UFRvF9J3qLI/AAAAAAAADds/Ndac55En3vI/s72-c/home.png" height="72" width="72"/><thr:total>1</thr:total><georss:featurename>Viareggio, Province of Lucca, Italy</georss:featurename><georss:point>43.8516258 10.2712297</georss:point><georss:box>43.8058233 10.1922657 43.8974283 10.350193699999998</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-6350611928191009145</id><published>2012-06-03T12:57:00.000+02:00</published><updated>2012-06-03T12:57:25.466+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="thread"/><category scheme="http://www.blogger.com/atom/ns#" term="tkinter"/><title type='text'>Threading with Tkinter, done properly</title><content type='html'>&lt;p&gt;Lately, I spent my spare time working on
&lt;a href=&quot;http://pypi.python.org/pypi/logfilter&quot;&gt;logfilter&lt;/a&gt;, a mix of &lt;code&gt;tail -f&lt;/code&gt; and &lt;code&gt;grep
-e PATTERN&lt;/code&gt; alike commands, with a &lt;em&gt;rocky&lt;/em&gt; GUI to display results.&lt;/p&gt;

&lt;p&gt;In a nutshell, &lt;strong&gt;logfilter&lt;/strong&gt; starts to poll a file waiting for new content; when
a new line is added, configured filters are applied to it and then passed to the
GUI, which in turn colors matching part of that line and displays the result
inside a text widget.&lt;/p&gt;

&lt;p&gt;It was my first time programming with
&lt;a href=&quot;http://docs.python.org/library/tkinter.html&quot;&gt;Tkinter&lt;/a&gt;, so before starting to
code, I dug the documentation page looking for tips and advices.  The most
important I come up with was:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Never ever update your widgets outside the mainloop thread!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I made a bit of GTK programming before, so I was already used to this:  do your
CPU intensive processing in a separate thread/process, and when done, &lt;em&gt;schedule&lt;/em&gt;
a GUI update (e.g. &lt;code&gt;gobect.idle_add(func, args...)&lt;/code&gt;).  After some Googling,
I found &lt;a href=&quot;http://www.pythonware.com/library/tkinter/introduction/x9507-alarm-handlers-and-other.htm&quot;&gt;this
page&lt;/a&gt;,
and found out that &lt;code&gt;tk.after_idle&lt;/code&gt; was the function I was looking for:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Register an idle callback which is called when the system is idle (that is,
when there are no more events to process in the mainloop). The callback is
only called once for each call to &lt;code&gt;after_idle&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With all that knowledge gathered so far, I finally started to code, and in
a couple of days the first &lt;em&gt;alpha&lt;/em&gt; version of &lt;strong&gt;logfilter&lt;/strong&gt;  was there, ready to
be tested.  As you could imagine, problems were waiting me, just behind the
corner.  Indeed, as soon as I started to stress the application, I immediately
noticed strange behaviours, a clear sign that something has been done wrong.&lt;/p&gt;

&lt;p&gt;First and foremost, the application was crashing over and over again if run on
Windows.  In particular, if the file analyzed contained &lt;em&gt;a lot&lt;/em&gt; of matching
lines, the GUI was able to execute a handful of text widget updates before
crashing (obviously, without leaving any useful error messages).  Given that on
Ubuntu everything was working flawlessly, I tried to update Python and Tkinter
libraries to a more recent version.  I also tried to implement some
&lt;em&gt;coalescing-alike&lt;/em&gt; mechanism to limit the amount of GUI updates done per unit of
time, but unfortunately, none of those were helpful to solve the issue.&lt;/p&gt;

&lt;p&gt;Second problem, a systematic deadlock while quitting the application during
updates of the text widget.  No matter how much effort did I put to implement
the &lt;em&gt;graceful&lt;/em&gt; shutdown of the application;  every time I pressed that window&#39;s
&lt;em&gt;X&lt;/em&gt; button while updating the text widget, one of the active threads (the one in
charge of schedule GUI updates using &lt;code&gt;tk.after_idle&lt;/code&gt;), did not stop correctly.
I tried to debug the application using both log messages and also the Eclipse
debugger, but again, I was still stuck at the same point.&lt;/p&gt;

&lt;p&gt;As my last call, I decided to write a couple of emails to the
&lt;a href=&quot;http://mail.python.org/pipermail/tkinter-discuss/2012-May/003165.html&quot;&gt;tkinter-discuss&lt;/a&gt;
and
&lt;a href=&quot;http://mail.python.org/pipermail/python-list/2012-May/624718.html&quot;&gt;python-list&lt;/a&gt;
mailing lists, and luckily, after some investigation, it turned out that my use
of &lt;code&gt;tk.after_idle&lt;/code&gt; was wrong, and that it was by no means &lt;em&gt;thread-safe&lt;/em&gt; as
I thought it to be (the function, is indeed expected to be invoked inside the
mainloop thread).&lt;/p&gt;

&lt;p&gt;The solution was simple (you can find the implementation
&lt;a href=&quot;https://bitbucket.org/iamFIREcracker/logfilter/changeset/9e1b571f90eb&quot;&gt;here&lt;/a&gt;):
threads in need of GUI updates, will push function calls into a synchronized
queue shared with the GUI;  the GUI, periodically, will pop items from such
a queue, and &lt;em&gt;safely&lt;/em&gt; invoke the method &lt;code&gt;tk.after_idle&lt;/code&gt; to actually schedule the
update. That&#39;s it!&lt;/p&gt;

&lt;p&gt;It goes without saying that the above implementation solved both the issues
presented before!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/6350611928191009145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/06/threading-with-tkinter-done-properly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6350611928191009145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/6350611928191009145'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/06/threading-with-tkinter-done-properly.html' title='Threading with Tkinter, done properly'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-7065577723310237507</id><published>2012-01-15T11:02:00.000+01:00</published><updated>2012-01-15T11:02:42.797+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="file"/><category scheme="http://www.blogger.com/atom/ns#" term="prompt"/><category scheme="http://www.blogger.com/atom/ns#" term="while"/><title type='text'>Prompt user for input while processing a file, line by line, in Bash</title><content type='html'>&lt;p&gt;Have you ever had the need in Bash of looping on a file (or the output of a
&lt;em&gt;piped&lt;/em&gt; command), line by line, and then in the middle of such process, ask
user for inputs (e.g. a confirmation of an action to be taken)? Well solving
this has not been as easy as I expected.&lt;/p&gt;

&lt;h4&gt;The task&lt;/h4&gt;

&lt;p&gt;I come into this problem while writing a Bash script for &lt;em&gt;semi-automatic&lt;/em&gt; merge of two
subversion repositories (henceforth &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt;). What I had
in mind was very simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run a &lt;em&gt;recursive&lt;/em&gt; &lt;code&gt;diff&lt;/code&gt; between the two repositories;&lt;/li&gt;
&lt;li&gt;Parse the command &lt;em&gt;output&lt;/em&gt; so that we are able to distinguish between files to be
merged and files to be imported from &lt;em&gt;development&lt;/em&gt; to &lt;em&gt;release&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;Ask user for confirmation, and then act consequently;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let&#39;s start with the function which &lt;strong&gt;actually&lt;/strong&gt; does the merge:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function do_merge
{
    local src=$1
    local dst=$2

    if [ -f ${dst} ]; then
        read -p &quot;Merge ${dst}? [N/y] &quot;
        if [ &quot;${REPLY}&quot; = &#39;y&#39; ]; then
            # prefer the destination file to be on the left
            vimdiff ${dst} ${src}
        fi
    else
        read -p &quot;Import ${src}? [N/y] &quot;
        if [ &quot;${REPLY}&quot; = &#39;y&#39; ]; then
            cp -R ${src} ${dst}
        fi
    fi
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Its logic is very simple: if the destination file (&lt;code&gt;${dst}&lt;/code&gt;) exists, we
are asked to merge two files, consequently ask the user for confirmation and then fire up &lt;code&gt;vimdiff&lt;/code&gt;. On the other hand, if the file does not exist and the user answered &lt;code&gt;y&lt;/code&gt;, &lt;em&gt;copy&lt;/em&gt; the file to the new location.&lt;/p&gt;

&lt;p&gt;Place the above code inside a temporary file (&lt;strong&gt;~/tmp/foo.sh&lt;/strong&gt;) and test it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; cd ~/tmp
&amp;gt; mkdir a b
&amp;gt; fortune &amp;gt; a/hello
&amp;gt; (source ~/tmp/foo.sh; do_merge a/hello b/hello)
Import a/hello? [N/y] y
&amp;gt; fortune &amp;gt; a/hello
&amp;gt; (source ~/tmp/foo.sh; do_merge a/hello b/hello)
Merge b/hello? [N/y]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As expected, on the first run file &lt;strong&gt;a/hello&lt;/strong&gt; is imported into &lt;strong&gt;b/hello&lt;/strong&gt;; on the second run, the two files are merged.&lt;/p&gt;

&lt;p&gt;Have a look now, at the code of the function which analyzes the two repositories
and calls &lt;code&gt;do_merge&lt;/code&gt; (when needed) to synchronize them:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function merge
{
    local line src dst
    local development=$1
    local release=$2

    diff -urq ${development} ${release} | \
    grep -v &#39;.svn&#39; | \
    while read line; do
        set -- ${line}
        # For files exising only inside one of the two directories, ``diff``
        # output a line such as &#39;Only in directory: file&#39;.
        # In this case $3 is equal to the directory containing the new file $4;
        # such file should be imported only if it belongs to the ${development}
        # directory
        if [[ &quot;${line}&quot; =~ &#39;Only in&#39; ]]; then
            if [[ $3 =~ ${development} ]]; then
                # remove trailing &#39;:&#39; from directory and append filename
                src=${3%%:}/$4
                dst=${release}/${src##${development}/}
                do_merge ${src} ${dst}
            fi
        # For existing files which differ, ``diff`` output a message like &#39;Files
        # /path/file1 and /path/file2 differ.&#39;
        # Files to be merged are inside $2 and $4 respectively.
        elif [[ &quot;${line}&quot; =~ &#39;Files &#39; ]]; then
            src=$2
            dst=$4
            do_merge ${src} ${dst}
        else
            echo &#39;Aw shit!&#39;
        fi
    done
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Even if comments should be &lt;em&gt;self-explanatory&lt;/em&gt; (I hope so), here we run
&lt;code&gt;diff&lt;/code&gt; between the two &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; repositories, exluding &lt;strong&gt;.svn&lt;/strong&gt; entries, preparing &lt;code&gt;do_merge&lt;/code&gt; arguments based on
the output of previous commands, and finally invoke the function.&lt;/p&gt;

&lt;p&gt;Let&#39;s see if it works as we expect:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; (source ~/tmp/foo.sh; merge a b)
&amp;gt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nothing happens!&lt;/p&gt;

&lt;p&gt;However, &lt;em&gt;echoing&lt;/em&gt; &lt;code&gt;do_merge&lt;/code&gt; calls produces the right output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; (source ~/tmp/foo.sh; merge a b)
do_merge a/hello b/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What is happening here? Why aren&#39;t we prompted for confirmation?&lt;/p&gt;

&lt;h4&gt;The problem&lt;/h4&gt;

&lt;p&gt;Have you ever heard about
&lt;a href=&quot;http://tldp.org/LDP/abs/html/subshells.html&quot;&gt;subshells&lt;/a&gt;? If not, please spend a couple of minutes reading the previous link: it is surely worth it.&lt;/p&gt;

&lt;p&gt;Inside the while loop (and consequently inside the body of the function
&lt;code&gt;do_merge&lt;/code&gt;) we are actually inside a &lt;em&gt;subshell&lt;/em&gt; which is using &lt;code&gt;diff&lt;/code&gt; piped
output as input; consequently, there is no hope for the aforementioned function
to wait for user &lt;em&gt;key-press&lt;/em&gt; actions because kind of &lt;em&gt;detached&lt;/em&gt; from
the parent shell (the one running the function &lt;code&gt;merge&lt;/code&gt;, and the one enable to intercept user input).&lt;/p&gt;

&lt;h4&gt;The solution&lt;/h4&gt;

&lt;p&gt;The solution is pretty simple, once you have found
&lt;a href=&quot;http://stackoverflow.com/questions/8422476/bash-user-prompt-while-reading-file&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to to force all the &lt;code&gt;read -p&lt;/code&gt; operations inside the &lt;em&gt;subshell&lt;/em&gt; to be
fed directly by the keyboard; on Unix this is achieved appending
&lt;code&gt;&amp;lt;/dev/tty&lt;/code&gt; to those &lt;em&gt;guilty&lt;/em&gt; lines. Another option (which I prefer most), is
to append &lt;em&gt;redirects&lt;/em&gt; to function calls and leave the function &lt;code&gt;do_call&lt;/code&gt; as
is (as presented below):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;...
dst=${release}/${src##${development}/}
do_merge ${src} ${dst} &amp;lt;/dev/tty
...
dst=$4
do_merge ${src} ${dst} &amp;lt;/dev/tty
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now run the script again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ (. ~/tmp/foo.sh; merge a b)
Import a/hello? [N/y]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and be joyful! ;-)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/7065577723310237507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/01/prompt-user-for-input-while-processing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/7065577723310237507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/7065577723310237507'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/01/prompt-user-for-input-while-processing.html' title='Prompt user for input while processing a file, line by line, in Bash'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-1384204971234092472</id><published>2012-01-08T10:36:00.000+01:00</published><updated>2012-01-08T10:36:52.121+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cakephp"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><category scheme="http://www.blogger.com/atom/ns#" term="vagrant"/><title type='text'>About CakePHP and Vagrant!</title><content type='html'>Weeks ago I found &lt;a href=&quot;http://bakery.cakephp.org/articles/connrs/2011/11/04/testing_with_virtualbox_and_vagrant&quot;&gt;this&lt;/a&gt;, an article describing how to test your CakePHP application using a &lt;a href=&quot;http://vagrantup.com/&quot;&gt;Vagrant-powered&lt;/a&gt; virtual environment; unfortunately the author makes use of a custom &lt;a href=&quot;http://vagrantup.com/docs/boxes.html&quot;&gt;box&lt;/a&gt; already configured with PHP, MySQL, etc, which is a solution I don&#39;t find quite reliable (and the fact that the author decided to create a &lt;em&gt;backup&lt;/em&gt; torrent with that &lt;strong&gt;box&lt;/strong&gt;, is an action that speaks for itself).&lt;br /&gt;
&lt;br /&gt;
In this post I would like to describe how to create a CakePHP testing environment (on top of Vagrant), &lt;strong&gt;from scratch&lt;/strong&gt;, starting from a standard Vagrant box and leverage the power of the Puppet provisioning system to install binaries and configure daemons automatically.&lt;br /&gt;
&lt;br /&gt;
Here follows the agenda of the article:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#repo_layout&quot;&gt;Repository layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#vagrant&quot;&gt;Vagrant&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#simple_config&quot;&gt;Simple configuration file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#puppet&quot;&gt;Puppet&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#update&quot;&gt;Update everything&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mysql&quot;&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#apache&quot;&gt;Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#php&quot;&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#misc&quot;&gt;Miscellaneous&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sync&quot;&gt;Synchronize all&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fire&quot;&gt;Fire up the machine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#devel&quot;&gt;Develop ...&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#test&quot;&gt;... Test!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h4&gt;&lt;a id=&quot;repo_layout&quot;&gt;Repository layout&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;
Before starting with the &lt;em&gt;technical&lt;/em&gt; details about the setup of a Vagrant virtual environment, let me spend a couple of words describing how I usually arrange the files inside a repository containing a &lt;em&gt;Vagrant-aware&lt;/em&gt; CakePHP application:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;`-- puppet/
|   `-- base.pp
`-- src/
|   `-- cakephp/
|   `-- app/
`-- README
`-- Vagrantfile
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
As you can see from the ASCII art above, I have a &lt;strong&gt;puppet&lt;/strong&gt; directory containing various configuration files for the Vagrant provisioning system, a &lt;strong&gt;src&lt;/strong&gt; directory with a CakePHP &lt;a href=&quot;http://mercurial.selenic.com/wiki/Subrepository&quot;&gt;subrepository&lt;/a&gt; and the directory containing our app, and finally the &lt;strong&gt;README&lt;/strong&gt; and the Vagrant configuration file.&lt;br /&gt;
&lt;br /&gt;
You could complain that adding &lt;em&gt;testing environment stuff&lt;/em&gt; inside your application repository is not the right way to do things, but what if you are working in a team? How to share all those configuration files with all the other members of the team? Using emails? Using a wiki page? I think the repository containing the application is the best place for those files!&lt;br /&gt;
&lt;br /&gt;
In the following section I will talk briefly about Vagrant, focusing on its configuration files and how to modify them to create your own CakePHP testing environment.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id=&quot;vagrant&quot;&gt;Vagrant&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;
At a first glance, &lt;a href=&quot;http://vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt; could be considered as a simple &lt;a href=&quot;https://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt; wrapper, letting its users easily create and manage virtual machines. Vagrant is &lt;strong&gt;more&lt;/strong&gt; than this: it is shipped with different &lt;a href=&quot;http://vagrantup.com/docs/getting-started/provisioning.html&quot;&gt;provisioning systems&lt;/a&gt; enabling automatic configuration of services and binaries inside virtual machines, it automatically configures shared folders between the &lt;em&gt;host&lt;/em&gt; and the &lt;em&gt;guest&lt;/em&gt; environments, it automatically setup port forwarding to enable the &lt;em&gt;host&lt;/em&gt; to &lt;em&gt;ssh&lt;/em&gt; into the guest machine, etc.&lt;br /&gt;
&lt;br /&gt;
From the &lt;a href=&quot;https://github.com/mitchellh/vagrant&quot;&gt;readme&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;Vagrant is a tool for building and distributing virtualized development environments.&lt;br /&gt;
&lt;br /&gt;
By providing automated creation and provisioning of virtual machines using Oracle&#39;s VirtualBox, Vagrant provides the tools to create and configure lightweight, reproducible, and portable virtual environments.&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
As you could imagine, this tool comes in handy for our purposes. Imagine you are carrying on your CakePHP application together with 3 or 4 other developers; to work on it, each member of the team is in need of a machine running MySQL, Apache and (obviously) with the PHP interpreter installed. At this point you have to choose between two different options:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Leave each member of the team responsible of setting up its development machine, and hope for the versions of the installed binaries and libraries to be the same.&lt;/li&gt;
&lt;li&gt;Ship a Vagrant image (the configuration file, not the image itself!) inside the repository containing your app so that each developer is guaranteed working with the same tools as the other members of the team.&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
If you chose the former, then stop reading: you will end up being very annoyed! If you chose the latter, please go on, you are in the right place!&lt;br /&gt;
&lt;br /&gt;
&lt;h5&gt;&lt;a id=&quot;simple_config&quot;&gt;Simple configuration file&lt;/a&gt;&lt;/h5&gt;&lt;br /&gt;
The Vagrant configuration file is written in &lt;a href=&quot;ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt;, but don&#39;t be scared if you (like me) don&#39;t know its syntax: edit the file is really straightforward! Here is how my &lt;strong&gt;VagrantFile&lt;/strong&gt; looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;Vagrant::Config.run do |config|
    config.vm.host_name = &quot;app&quot;
    config.vm.box = &quot;lucid32&quot;
    config.vm.box_url = &quot;http://files.vagrantup.com/lucid32.box&quot;
    #config.vm.boot_mode = :gui

    #                              guest &amp;lt;-- host
    config.vm.forward_port &quot;http&quot;, 80,       8080
    config.vm.network(&quot;33.33.33.10&quot;)

    config.vm.provision :puppet do |puppet|
        puppet.manifests_path = &quot;puppet&quot;
        puppet.manifest_file  = &quot;base.pp&quot;
    end
end
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Let me explain the meaning of the most &lt;em&gt;interesting&lt;/em&gt; lines:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;config.vm.host_name&lt;/code&gt;: set the name of the virtual machine, and its internal &lt;em&gt;hostname&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.vm.box&lt;/code&gt;: set the name of the &lt;em&gt;box&lt;/em&gt; to use for this Vagrant image; all Vagrant images use so called &lt;em&gt;boxes&lt;/em&gt; as their base OS setup, so that multiple Vagrant images can share the same base operating system, and specialize it for example, configuring and tweaking the provisioning system;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.vm.box_url&lt;/code&gt;: url from which to fetch the chosen &lt;em&gt;box&lt;/em&gt;, if not yet downloaded;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.vm.boot_mode&lt;/code&gt;: start the virtual machine with or without the GUI (this is something I learned since I started using Vagrant: VirtualBox images can be fired up also in &lt;a href=&quot;http://www.virtualbox.org/manual/ch07.html#vboxheadless&quot;&gt;headless&lt;/a&gt; mode!);&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.vm.forward_port&lt;/code&gt;: enable port forwarding for HTTP requests, given that we are going to install Apache inside our Vagrant machine and send to it HTTP requests and display received results, and that we want to avoid possible conflicts with other web-servers running on the &lt;em&gt;host&lt;/em&gt; machine;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.vm.network()&lt;/code&gt;: assign a static IP the interface of the virtual machine, so that we can for example log into such machine using the given IP without using Vagrant CLI interface;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;puppet.manifests_path&lt;/code&gt;: choose the provisioning system;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;puppet.manifest_file&lt;/code&gt;: set the location of provisioning system &lt;em&gt;entry point&lt;/em&gt; configuration file;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Now that our Vagrant configuration file has been completely filled, it is time to move on and learn how to configure the chosen provisioning system.&lt;br /&gt;
&lt;br /&gt;
&lt;h5&gt;&lt;a id=&quot;puppet&quot;&gt;Puppet&lt;/a&gt;&lt;/h5&gt;&lt;br /&gt;
&lt;a href=&quot;http://projects.puppetlabs.com/projects/puppet/&quot;&gt;Puppet&lt;/a&gt; is very simple and at the same time powerful &lt;em&gt;provisioning system&lt;/em&gt;. What is its aim? Simplify the life of system administrators, automating the whole process which, starting from a default &lt;em&gt;operating&lt;/em&gt; system installation, leads to 100% &lt;strong&gt;operating&lt;/strong&gt; system containing all the tools and services already configured and ready to be used.&lt;br /&gt;
&lt;br /&gt;
From the project &lt;a href=&quot;http://projects.puppetlabs.com/&quot;&gt;home page&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;Puppet is an open source data center automation and configuration management framework. Puppet provides system administrators with a simplified platform that allows for consistent, transparent, and flexible systems management.&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
Nice, but how can we use it to configure our Vagrant virtual machines automatically? Fortunately, as explained before, Vagrant is shipped with the support for the Puppet provisioning system, which means our effort to configure everything is reduced to the minimum.&lt;br /&gt;
&lt;br /&gt;
In the following sections I will show you the minimal configuration file enabling Puppet to install most of the binaries needed by CakePHP to serve requests and run the &lt;a href=&quot;http://book.cakephp.org/2.0/en/development/testing.html?highlight=testsuite&quot;&gt;tests&lt;/a&gt;. Unfortunately, I will not spend any time explaining Puppet syntax, so please have a look at the &lt;a href=&quot;http://docs.puppetlabs.com/&quot;&gt;official documentation&lt;/a&gt; before going on.&lt;br /&gt;
&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;update&quot;&gt;Update everything&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
The first thing to do, once you are ready with your Vagrant image, is to update the list of available packages; if you skip this step, you are likely to end up receiving lot of error messages saying that some of requested packages cannot be downloaded: a simple &lt;code&gt;sudo apt-get update&lt;/code&gt; should fix the problem but obviously, if you want to make a step further and automate this operation with Puppet, you will need put some lines inside the Puppet configuration file (&lt;strong&gt;puppet/base.pp&lt;/strong&gt;).&lt;br /&gt;
&lt;br /&gt;
How could I tell Puppet to &lt;em&gt;update the list of packages only once&lt;/em&gt;? Well, the simplest solution I come up with, is presented in the following snippet of code:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec {&quot;apt-get-update&quot;:
    creates =&amp;gt; &quot;/updated&quot;,
    command =&amp;gt; &quot;apt-get update &amp;amp;&amp;amp; touch /updated&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
This basically tell Puppet that such &lt;code&gt;exec { ... }&lt;/code&gt; command &lt;em&gt;creates&lt;/em&gt; the file &lt;strong&gt;/updated&lt;/strong&gt; and that it won&#39;t be run again once that file has been created. The second line, is nothing more than the &lt;em&gt;command&lt;/em&gt; Puppet will execute in order to create the aforementioned file.&lt;br /&gt;
&lt;br /&gt;
Now put that snippet inside a Puppet class (similar to a function used to wrap multiple operations), together with other OS standard operations (e.g. user permissions, or default directory creation), as presented below:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class ubuntu {
    group { &quot;puppet&quot;: ensure =&amp;gt; &quot;present&quot;; } -&amp;gt;
    group { &quot;vagrant&quot;: ensure =&amp;gt; &quot;present&quot;; } -&amp;gt;
    user { &quot;vagrant&quot;: ensure =&amp;gt; &quot;present&quot;; } -&amp;gt;
    file { &quot;/home/vagrant/bin&quot;:
        ensure  =&amp;gt; &quot;directory&quot;,
        owner   =&amp;gt; &quot;vagrant&quot;,
        group   =&amp;gt; &quot;vagrant&quot;,
        mode    =&amp;gt; &quot;755&quot;,
    }

    # update once
    exec {&quot;apt-get-update&quot;:
        creates =&amp;gt; &quot;/updated&quot;,
        command =&amp;gt; &quot;apt-get update &amp;amp;&amp;amp; touch /updated&quot;,
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The use of &lt;strong&gt;-&gt;&lt;/strong&gt; between Puppet actions, makes possible to synchronize actions one another (some sort of dependency): in the snippet, we are telling Puppet to create the user &lt;code&gt;vagrant&lt;/code&gt; only after the group &lt;code&gt;vagrant&lt;/code&gt;, and again, after the group &lt;code&gt;puppet&lt;/code&gt;, and so forth.&lt;br /&gt;
&lt;br /&gt;
On the next reboot of your Vagrant machine, the list of packages will be automatically updated and the file &lt;strong&gt;updated&lt;/strong&gt; will be created inside your root folder.&lt;br /&gt;
&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;mysql&quot;&gt;MySQL&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
I know it, CakePHP can be configured to run with &lt;a href=&quot;http://matteolandi.blogspot.com/2011/10/about-cakephp-and-sqlite.html&quot;&gt;SQLite databases&lt;/a&gt;, but for this article I will assume your application has been thought to work with a MySQL database. Moreover, for the sake of simpleness and readability, I will wrap all the instructions needed by Puppet to install MySQL (and later on Apache, PHP, etc), inside a Puppet class:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class mysql {
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
At this point we need to tell Puppet to install MySQL, which is provided by the &lt;code&gt;mysql-server&lt;/code&gt; package; to do so, add the following line inside your Puppet class:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;package {&quot;mysql-server&quot;: ensure =&amp;gt; installed}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Now, ensure that the MySQL daemon gets loaded each time the virtual machine is booted:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;service { &quot;mysql&quot;:
    ensure =&amp;gt; running,
    hasstatus =&amp;gt; true,
    hasrestart =&amp;gt; true,
    require =&amp;gt; Package[&quot;mysql-server&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The line containing &lt;code&gt;require =&amp;gt; ...&lt;/code&gt;, tells Puppet that the current &lt;code&gt;service { ... }&lt;/code&gt; operation needs the package &lt;code&gt;mysql-server&lt;/code&gt; installed to be be executed correctly.&lt;br /&gt;
&lt;br /&gt;
Configure now, the Administrator&#39;s password:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;$password = &quot;vagrant&quot;
exec { &quot;mysql-set-password&quot;:
    subscribe =&amp;gt; Service[&quot;mysql&quot;],
    unless =&amp;gt; &quot;mysqladmin -uroot -p${password} status&quot;,
    command =&amp;gt; &quot;mysqladmin -uroot password ${password}&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
With these lines, we are telling Puppet that the command &lt;code&gt;mysqladmin -uroot password ${password}&lt;/code&gt; should be executed &lt;strong&gt;after&lt;/strong&gt; the service &lt;code&gt;mysql&lt;/code&gt; has been properly started and only if &lt;code&gt;mysqladmin -uroot -p${password} status&lt;/code&gt; exits with a &lt;em&gt;non-zero&lt;/em&gt; value.&lt;br /&gt;
&lt;br /&gt;
Finally create the tables of our application and populate them with some dummy record, if any (replace the path of the SQL script containing tables definitions):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec { &quot;mysql-db-init&quot;:
    command =&amp;gt; &quot;mysql -uroot -p${password} &amp;lt; /vagrant/src/app/Config/Schema/schema.sql&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Done! Here is the final &lt;em&gt;mysql&lt;/em&gt; Puppet class (with some explicit synchronization rules):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class mysql {
    package { &quot;mysql-server&quot;: ensure =&amp;gt; installed }

    $password = &quot;setup&quot;
    service { &quot;mysql&quot;:
        ensure =&amp;gt; running,
        hasstatus =&amp;gt; true,
        hasrestart =&amp;gt; true,
        require =&amp;gt; Package[&quot;mysql-server&quot;],
    } -&amp;gt;
    exec { &quot;mysql-set-password&quot;:
        subscribe =&amp;gt; Service[&quot;mysql&quot;],
        unless =&amp;gt; &quot;mysqladmin -uroot -p${password} status&quot;,
        command =&amp;gt; &quot;mysqladmin -uroot password ${password}&quot;,
    } -&amp;gt;
    exec { &quot;mysql-db-init&quot;:
        command =&amp;gt; &quot;mysql -uroot -p${password} &amp;lt; /vagrant/src/app/Config/Schema/schema.sql&quot;,
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;apache&quot;&gt;Apache&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
Now that we learned how to configure MySQL, it would be easy to adapt the previous steps and tell Puppet to install and configure an Apache server inside our development virtual machine. Let&#39;s start with an empty Puppet class:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class apache2 {
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Then place inside inside of it the code to automatically download and install all the needed binaries:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;package { &quot;apache2&quot;: ensure =&amp;gt; installed }
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
At this point, it&#39;s time to configure the daemon; depending of the installed version, we likely are going to receive warnings or errors messages if a &lt;strong&gt;ServerName&lt;/strong&gt; is not specified inside the Apache configuration file; to avoid that, add the following (change &lt;code&gt;app&lt;/code&gt; with whatever you want) to the Puppet class:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec { &quot;servername&quot;:
    subscribe =&amp;gt; Package[&quot;apache2&quot;],
    unless  =&amp;gt; &quot;grep &#39;^ServerName&#39; /etc/apache2/apache2.conf&quot;,
    command =&amp;gt; &quot;echo ServerName app &amp;gt;&amp;gt; /etc/apache2/apache2.conf&quot;,
    notify  =&amp;gt; Service[&quot;apache2&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
In the snippet above we are telling Puppet to add &lt;code&gt;Servername app&lt;/code&gt; to &lt;strong&gt;/etc/apache2/apache2.conf&lt;/strong&gt; (if not already present) and notify the service &lt;code&gt;apache2&lt;/code&gt; about possible configuration changes (a.k.a. restart the service!).&lt;br /&gt;
&lt;br /&gt;
CakePHP needs &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_rewrite.html&quot;&gt;mod_rewrite&lt;/a&gt; enabled; achieve that with the following lines:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec { &quot;mod-rewrite&quot;:
    unless =&amp;gt; &quot;ls /etc/apache2/mods-enabled/rewrite*&quot;,
    command =&amp;gt; &quot;a2enmod rewrite&quot;,
    notify  =&amp;gt; Service[&quot;apache2&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Moreover, we need to set &lt;a href=&quot;http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride&quot;&gt;AllowOverride&lt;/a&gt; equal to &lt;code&gt;All&lt;/code&gt; for the &lt;em&gt;default&lt;/em&gt; Apache virtual host; the following is a bit tricky because the configuration file contains three lines with those strings, but we need to take into account the only one referring the document root which in my case, is found at line 11; consequently:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;$config = &#39;/etc/apache2/sites-available/default&#39;
exec { &quot;allow-override&quot;:
    unless =&amp;gt; &quot;sed -n &#39;11 p&#39; $config | grep &#39;AllowOverride All&#39;&quot;,
    command =&amp;gt; &quot;sed -i &#39;11 s/None/All/&#39; $config&quot;,
    notify  =&amp;gt; Service[&quot;apache2&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
When Apache gets installed, the user &lt;code&gt;www-data&lt;/code&gt; is created and used to serve HTTP requests; to make sure we don&#39;t get &lt;em&gt;permissions&lt;/em&gt; errors running our CakePHP application, we ensure that such user is part of the group &lt;strong&gt;vagrant&lt;/strong&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;user { &quot;www-data&quot;:
    groups =&amp;gt; [&quot;vagrant&quot;],
    notify  =&amp;gt; Service[&quot;apache2&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Create a symbolic link inside the server document root pointing to the source directory of our application which (if have followed the tutorial so far) is inside &lt;strong&gt;/vagrant/src/app&lt;/strong&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;file { &quot;/var/www/app&quot;:
    ensure =&amp;gt; link,
    target =&amp;gt; &quot;/vagrant/src/app&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Finally, make sure the server starts automatically at boot:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;service { &quot;apache2&quot;:
    ensure =&amp;gt; running,
    hasstatus =&amp;gt; true,
    hasrestart =&amp;gt; true,
    require =&amp;gt; Package[&quot;apache2&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
This is how the &lt;em&gt;apache&lt;/em&gt; Puppet class looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class apache2 {
    package { &quot;apache2&quot;: ensure =&amp;gt; installed }

    $config = &#39;/etc/apache2/sites-available/default&#39;
    exec { &quot;servername&quot;:
        subscribe =&amp;gt; Package[&quot;apache2&quot;],
        unless  =&amp;gt; &quot;grep &#39;^ServerName&#39; /etc/apache2/apache2.conf&quot;,
        command =&amp;gt; &quot;echo ServerName app &amp;gt;&amp;gt; /etc/apache2/apache2.conf&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    exec { &quot;mod-rewrite&quot;:
        unless =&amp;gt; &quot;ls /etc/apache2/mods-enabled/rewrite*&quot;,
        command =&amp;gt; &quot;a2enmod rewrite&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    exec { &quot;allow-override&quot;:
        unless =&amp;gt; &quot;sed -n &#39;11 p&#39; $config | grep &#39;AllowOverride All&#39;&quot;,
        command =&amp;gt; &quot;sed -i &#39;11 s/None/All/&#39; $config&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    user { &quot;www-data&quot;:
        groups =&amp;gt; [&quot;vagrant&quot;],
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    file { &quot;/var/www/app&quot;:
        ensure =&amp;gt; link,
        target =&amp;gt; &quot;/vagrant/src/app&quot;,
    } -&amp;gt;
    service { &quot;apache2&quot;:
        ensure =&amp;gt; running,
        hasstatus =&amp;gt; true,
        hasrestart =&amp;gt; true,
        require =&amp;gt; Package[&quot;apache2&quot;],
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;php&quot;&gt;PHP&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
Let&#39;s start with the standard &lt;em&gt;boilerplate&lt;/em&gt; for required binaries:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;$php = [&quot;php5-cli&quot;, &quot;php5-mysql&quot;, &quot;libapache2-mod-php5&quot;, &quot;php-pear&quot;]
package { $php: ensure =&amp;gt; &quot;installed&quot; }
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Once the interpreter has been installed, we need to modify the PHP&#39;s &lt;code&gt;include_path&lt;/code&gt; variable to automatically include the directory containing CakePHP core libraries; PHP looks for different configuration files depending on how the interpreter has been fired up: if it is being issued from the command line, it will look inside &lt;strong&gt;/etc/php5/cli/cli.php&lt;/strong&gt;, otherwise, if it was Apache who invoked the interpreter, the file &lt;strong&gt;/etc/php5/apache2/php.ini&lt;/strong&gt; will be used; consequently we need to modify both of them to cover all possible scenarios.&lt;br /&gt;
&lt;br /&gt;
By default the configuration line defining &lt;code&gt;include_path&lt;/code&gt; is commented out, so it is easy to understand if we need to tweak &lt;em&gt;php.ini&lt;/em&gt; files or not:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;$include = &#39;.:/usr/share/php:/vagrant/src/cakephp/lib/&#39;
$cliini = &#39;/etc/php5/cli/php.ini&#39;
$apacheini = &#39;/etc/php5/apache2/php.ini&#39;
exec { &quot;cli-include-path&quot;:
    subscribe =&amp;gt; Package[&quot;php5-cli&quot;],
    unless =&amp;gt; &quot;grep -q ^include_path $cliini&quot;,
    command =&amp;gt; &quot;echo include_path = $include &amp;gt;&amp;gt; $cliini&quot;,
    notify  =&amp;gt; Service[&quot;apache2&quot;],
} -&amp;gt;
exec { &quot;apache-include-path&quot;:
    unless =&amp;gt; &quot;grep -q ^include_path $apacheini&quot;,
    command =&amp;gt; &quot;echo include_path = $include &amp;gt;&amp;gt; $apacheini&quot;,
    notify  =&amp;gt; Service[&quot;apache2&quot;],
} -&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Kinda hackish! However, it is now the turn to enable the PHP extension for Apache; do it with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec { &quot;mod-php&quot;:
    command =&amp;gt; &quot;a2enmod php5&quot;,
    unless =&amp;gt; &quot;ls /etc/apache2/mods-enabled/php5*&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Finally, in order to be able to run CakePHP tests, we need PHPUnit properly configured on the machine; unfortunately, a bare &lt;code&gt;apt-get install phpunit&lt;/code&gt; will not be sufficient to install needed binaries because there are a couple of dependencies which are not made available through &lt;code&gt;apt&lt;/code&gt;; consequently we are going to install everything using &lt;code&gt;pear&lt;/code&gt; and its external (and more updated) repositories:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;exec { &quot;phpunit&quot;:
    command =&amp;gt; &quot;pear upgrade pear &amp;amp;&amp;amp; \
                pear channel-discover pear.phpunit.de &amp;amp;&amp;amp; \
                pear channel-discover components.ez.no &amp;amp;&amp;amp; \
                pear channel-discover pear.symfony-project.com &amp;amp;&amp;amp; \
                pear install --alldeps phpunit/PHPUnit&quot;,
    creates =&amp;gt; &quot;/usr/bin/phpunit&quot;,
    require =&amp;gt; Package[&quot;php-pear&quot;],
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The &lt;em&gt;php&lt;/em&gt; Puppet class, inclusive of all the synchronization rules, will look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class php {
    $php = [&quot;php5-cli&quot;, &quot;php5-mysql&quot;, &quot;libapache2-mod-php5&quot;, &quot;php-pear&quot;]
    package { $php: ensure =&amp;gt; &quot;installed&quot; }

    $include = &#39;.:/usr/share/php:/vagrant/src/cakephp/lib/&#39;
    $cliini = &#39;/etc/php5/cli/php.ini&#39;
    $apacheini = &#39;/etc/php5/apache2/php.ini&#39;
    exec { &quot;cli-include-path&quot;:
        subscribe =&amp;gt; Package[&quot;php5-cli&quot;],
        unless =&amp;gt; &quot;grep -q ^include_path $cliini&quot;,
        command =&amp;gt; &quot;echo include_path = $include &amp;gt;&amp;gt; $cliini&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    exec { &quot;apache-include-path&quot;:
        unless =&amp;gt; &quot;grep -q ^include_path $apacheini&quot;,
        command =&amp;gt; &quot;echo include_path = $include &amp;gt;&amp;gt; $apacheini&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    exec { &quot;mod-php&quot;:
        unless =&amp;gt; &quot;ls /etc/apache2/mods-enabled/php5*&quot;,
        command =&amp;gt; &quot;a2enmod php5&quot;,
        notify  =&amp;gt; Service[&quot;apache2&quot;],
    } -&amp;gt;
    exec { &quot;phpunit&quot;:
        creates =&amp;gt; &quot;/usr/bin/phpunit&quot;,
        command =&amp;gt; &quot;pear upgrade pear &amp;amp;&amp;amp; \
                    pear channel-discover pear.phpunit.de &amp;amp;&amp;amp; \
                    pear channel-discover components.ez.no &amp;amp;&amp;amp; \
                    pear channel-discover pear.symfony-project.com &amp;amp;&amp;amp; \
                    pear install --alldeps phpunit/PHPUnit&quot;,
        require =&amp;gt; Package[&quot;php-pear&quot;],
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;misc&quot;&gt;Miscellaneous&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
Create now a new Puppet class for all the utilities which are not strictly related to CakePHP, but still handy. Modify the &lt;code&gt;niceties&lt;/code&gt; array with whatever package you want:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class misc {
    $niceties = [&quot;htop&quot;, &quot;sudo&quot;, &quot;vim&quot;, &quot;curl&quot;, &quot;cowsay&quot;, &quot;figlet&quot;,
            &quot;inotify-tools&quot;]
    package { $niceties: ensure =&amp;gt; &quot;installed&quot; }

    file { &quot;/usr/local/bin/cowsay&quot;:
        ensure =&amp;gt; link,
        target =&amp;gt; &quot;/usr/games/cowsay&quot;,
        require =&amp;gt; Package[&#39;cowsay&#39;],
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h6&gt;&lt;a id=&quot;sync&quot;&gt;Synchronize all&lt;/a&gt;&lt;/h6&gt;&lt;br /&gt;
Well, so far we learned how to create Puppet classes containing commands to install packages, configure daemons, create files, etc; now we need to put everything together and possibly create some further synchronization between those operations.&lt;br /&gt;
&lt;br /&gt;
If you remember, we created a command to update the list of available packages &lt;strong&gt;once&lt;/strong&gt; but never told the &lt;em&gt;php&lt;/em&gt; or &lt;em&gt;apache&lt;/em&gt; class to wait for the update process to be done. To do so, create a new class executing all the class objects defined so far (&lt;em&gt;mysql&lt;/em&gt;, &lt;em&gt;apache&lt;/em&gt;, etc) and implementing the synchronization logic described before (&lt;strong&gt;update first, install later!&lt;/strong&gt;):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class cakephpbox {
    class { &quot;ubuntu&quot;: }
    class { &quot;apache2&quot;: require =&amp;gt; Exec[&#39;apt-get-update&#39;], }
    class { &quot;mysql&quot;:  require =&amp;gt; Exec[&#39;apt-get-update&#39;], }
    class { &quot;php&quot;: require =&amp;gt; Exec[&#39;apt-get-update&#39;], }
    class { &quot;misc&quot;: require =&amp;gt; Exec[&#39;apt-get-update&#39;], }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Finally tell Puppet to run the &lt;em&gt;cakephpbox&lt;/em&gt; class at boot time::&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class { &quot;cakephpbox&quot;: }
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h5&gt;&lt;a id=&quot;fire&quot;&gt;Fire up the machine&lt;/a&gt;&lt;/h5&gt;&lt;br /&gt;
It&#39;s time to fire up our &lt;strong&gt;carefully configured&lt;/strong&gt; Vagrant image. Issue the following command from the repository containing your application and cross your fingers:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
If everything went fine, the terminal from which you issued the previous command would look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-_w5XziLIepk/TwgT_IuJr_I/AAAAAAAACaI/7A9zuVa703I/s1600/vagrant-log.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;225&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-_w5XziLIepk/TwgT_IuJr_I/AAAAAAAACaI/7A9zuVa703I/s320/vagrant-log.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Moreover, you should be able to ping the virtual machine ...&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; ping 33.33.33.10
PING 33.33.33.10 (33.33.33.10) 56(84) bytes of data.
64 bytes from 33.33.33.10: icmp_req=1 ttl=64 time=2.73 ms
64 bytes from 33.33.33.10: icmp_req=2 ttl=64 time=0.210 ms
64 bytes from 33.33.33.10: icmp_req=3 ttl=64 time=0.508 ms
64 bytes from 33.33.33.10: icmp_req=4 ttl=64 time=0.493 ms
^C
--- 33.33.33.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.210/0.985/2.732/1.015 ms
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
... and issue HTTP requests against the Apache web-server running inside virtual machine (&lt;strong&gt;http://localhost:8080/app&lt;/strong&gt;):&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-8eJqE95e1bM/TwgTrgYMCxI/AAAAAAAACZ8/WGKdGvPQw-c/s1600/vagrant-apache.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;214&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-8eJqE95e1bM/TwgTrgYMCxI/AAAAAAAACZ8/WGKdGvPQw-c/s320/vagrant-apache.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h4&gt;&lt;a id=&quot;devel&quot;&gt;Develop ...&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;
As mentioned above, Vagrant automatically setup shared folders between &lt;em&gt;host&lt;/em&gt; and &lt;em&gt;guest&lt;/em&gt; machines: in particular it shares the &lt;em&gt;host&lt;/em&gt; folder containing &lt;strong&gt;VagrantFile&lt;/strong&gt; and makes it available inside the &lt;em&gt;guest&lt;/em&gt; environment under the directory &lt;strong&gt;/vagrant&lt;/strong&gt;. The obvious consequence of this, is that you can still develop your applications inside the &lt;em&gt;host&lt;/em&gt; environment, with your preferred &lt;em&gt;gui-enabled&lt;/em&gt; editor, and make use of all the &lt;em&gt;niceties&lt;/em&gt; your operating system is offering to you.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id=&quot;test&quot;&gt;... Test!&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;
CakePHP let developers run application test suites in two different ways: on the one hand you can run tests directly from the browser; on the other, you can use your &lt;em&gt;beloved&lt;/em&gt; console. If you followed all the steps described so far, both methods should work flawlessly.&lt;br /&gt;
&lt;br /&gt;
Do you feel more &lt;em&gt;gui-friendly&lt;/em&gt;? Then open your favorite browser and point to &lt;strong&gt;http://localhost:8080/app/test&lt;/strong&gt;; follow one of the links, and you should be presented with a page similar to the following:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-i23l3yNoToY/TwiCjfMrYaI/AAAAAAAACaU/-eK5zBFuxwg/s1600/web-test.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;284&quot; width=&quot;320&quot; src=&quot;http://3.bp.blogspot.com/-i23l3yNoToY/TwiCjfMrYaI/AAAAAAAACaU/-eK5zBFuxwg/s320/web-test.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Do you prefer &lt;em&gt;CLI&lt;/em&gt; over GUI? Then spawn a new terminal, &lt;em&gt;ssh&lt;/em&gt; into the virtual machine (&lt;code&gt;ssh vagrant@33.33.33.10&lt;/code&gt; or move into your repository and run &lt;code&gt;vagrant ssh&lt;/code&gt;), go inside the directory containing your application sources (&lt;code&gt;cd /vagrant/src/app&lt;/code&gt;) and finally run tests with &lt;code&gt;Console/cake testsuite app&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-JyveqSCuQuM/TwiEAQeduhI/AAAAAAAACag/8t3JUBBFf2o/s1600/cli-test.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;222&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-JyveqSCuQuM/TwiEAQeduhI/AAAAAAAACag/8t3JUBBFf2o/s320/cli-test.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h4&gt;&lt;a id=&quot;conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;
That&#39;s all folks, we finally arrived to the &lt;em&gt;conclusions&lt;/em&gt; section.&lt;br /&gt;
&lt;br /&gt;
I apologize for all the inaccuracies I left in the article but as I said on top, Internet is full of well-written documentation analyzing with much more details all the different arguments touched in the article: what Internet was missing in my opinion, was an article putting all that information together and shading most of the technical details to better focus on how to combine all those tools, in a very practical way.&lt;br /&gt;
&lt;br /&gt;
Thanks for reading.&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/1384204971234092472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2012/01/about-cakephp-and-vagrant.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1384204971234092472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1384204971234092472'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2012/01/about-cakephp-and-vagrant.html' title='About CakePHP and Vagrant!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-_w5XziLIepk/TwgT_IuJr_I/AAAAAAAACaI/7A9zuVa703I/s72-c/vagrant-log.png" height="72" width="72"/><thr:total>6</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-3962847941042514974</id><published>2011-11-28T11:13:00.000+01:00</published><updated>2011-11-28T12:38:40.392+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bluetooth"/><category scheme="http://www.blogger.com/atom/ns#" term="freebsd"/><title type='text'>Bluetooth on FreeBSD</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-vuRsa5gOlEo/TtNd4_BwGgI/AAAAAAAACLE/ViBncQEqoWM/s1600/grail.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;142&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-vuRsa5gOlEo/TtNd4_BwGgI/AAAAAAAACLE/ViBncQEqoWM/s320/grail.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
I found the holy Grail! Add these lines inside your kernel config:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;device          netgraph
options         NETGRAPH
options         NETGRAPH_SOCKET
options         NETGRAPH_BLUETOOTH              # ng_bluetooth(4)
options         NETGRAPH_BLUETOOTH_UBT          # ng_ubt(4)
options         NETGRAPH_BLUETOOTH_HCI          # ng_hci(4)
options         NETGRAPH_BLUETOOTH_SOCKET       # ng_btsocket(4)
options         NETGRAPH_BLUETOOTH_L2CAP        # ng_l2cap(4)
&lt;/pre&gt;&lt;br /&gt;
For additional information: &lt;a href=&quot;http://svnweb.freebsd.org/base/head/sys/conf/NOTES?view=markup&quot;&gt;sys/conf/NOTES&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/3962847941042514974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/11/bluetooth-on-freebsd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3962847941042514974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3962847941042514974'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/11/bluetooth-on-freebsd.html' title='Bluetooth on FreeBSD'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-vuRsa5gOlEo/TtNd4_BwGgI/AAAAAAAACLE/ViBncQEqoWM/s72-c/grail.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Pisa, Italy</georss:featurename><georss:point>43.7161354 10.3965843</georss:point><georss:box>43.5325099 10.080727300000001 43.8997609 10.7124413</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-8245765612801966170</id><published>2011-10-15T09:40:00.001+02:00</published><updated>2011-10-15T09:40:37.231+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cakephp"/><category scheme="http://www.blogger.com/atom/ns#" term="sqlite"/><title type='text'>About CakePHP and sqlite</title><content type='html'>&lt;br /&gt;
I would like to shed some light on how to make &lt;a href=&quot;http://cakephp.org&quot;&gt;CakePHP 2.0RC2&lt;/a&gt; correctly work with a &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;sqlite&lt;/a&gt; database (really useful for &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;debugging&lt;/em&gt; purposes).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-JyzDSv2hQZU/TpWvsneiuLI/AAAAAAAABhM/LgovzL24csA/s1600/cake-fail.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;194&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-JyzDSv2hQZU/TpWvsneiuLI/AAAAAAAABhM/LgovzL24csA/s320/cake-fail.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
First of all, &lt;b&gt;CakePHP 2.0 DOES support Sqlite3 &lt;a href=&quot;https://github.com/cakephp/cakephp/blob/2.0/lib/Cake/Model/Datasource/Database/Sqlite.php&quot;&gt;by default&lt;/a&gt;&lt;/b&gt;; so please, stop wasting your time trying to install stale 3rd party plugins as explained &lt;a href=&quot;http://stackoverflow.com/questions/1021980/cakephp-sqlite&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://stackoverflow.com/questions/2722383/using-sqlite3-with-cakephp&quot;&gt;here&lt;/a&gt;: everything you need is already present in the distribution package.&lt;br /&gt;
However, if you are still getting troubles trying to connect to the database, keep in mind that the new CakePHP release uses the &lt;a href=&quot;http://www.php.net/manual/en/class.pdo.php&quot;&gt;PDO&lt;/a&gt; library to talk to databases, hence double check that your PHP interpreter has been built with such library:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ php - i  | grep &#39;PDO support&#39;
PDO support =&amp;gt; enbled
&lt;/pre&gt;&lt;br /&gt;
and that you have the specific driver to connect to &lt;b&gt;sqlite&lt;/b&gt; databases:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ php -i | grep &#39;PDO drivers&#39;
PDO drivers =&amp;gt; mysql, sqlite
&lt;/pre&gt;&lt;br /&gt;
At this point, it is time to configure how your CakePHP application is be able to communicate with the database.&lt;br /&gt;
Edit &lt;b&gt;app/Config/database.php&lt;/b&gt; (if not present, copy it from &lt;b&gt;app/Config/database.php.default&lt;/b&gt;), and change your &lt;code&gt;$default&lt;/code&gt; settings for the keys &lt;code&gt;datasource&lt;/code&gt; and &lt;code&gt;database&lt;/code&gt; as presented below:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;class DATABASE_CONFIG {
    public $default = array(
        ..
        &#39;datasource&#39; =&amp;gt; &#39;Database/Sqlite&#39;,
        ..
        &#39;database&#39; =&amp;gt; &#39;yourdatabasename.sqlite&#39;,
        ..
    );
}
&lt;/pre&gt;&lt;br /&gt;
Finally, remember that when the &lt;code&gt;datasource&lt;/code&gt; setting is set to &lt;code&gt;Database/Sqlite&lt;/code&gt;, CakePHP&#39;s database engine expects to find inside &lt;code&gt;database&lt;/code&gt; the &lt;b&gt;path&lt;/b&gt; of your database file; moreover, bear in mind that if you specify a &lt;b&gt;relative path&lt;/b&gt;, that should be &lt;em&gt;relative&lt;/em&gt; to the &lt;b&gt;app/webroot&lt;/b&gt; directory.&lt;br /&gt;
&lt;br /&gt;
After all these steps, (hopefully) all the &lt;em&gt;bars&lt;/em&gt; will be green!&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-clc_K-6lMRI/TpWvy-f3UEI/AAAAAAAABhY/XiSA6W85NAQ/s1600/cake-ok.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;194&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-clc_K-6lMRI/TpWvy-f3UEI/AAAAAAAABhY/XiSA6W85NAQ/s320/cake-ok.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/8245765612801966170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/10/about-cakephp-and-sqlite.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8245765612801966170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8245765612801966170'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/10/about-cakephp-and-sqlite.html' title='About CakePHP and sqlite'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-JyzDSv2hQZU/TpWvsneiuLI/AAAAAAAABhM/LgovzL24csA/s72-c/cake-fail.png" height="72" width="72"/><thr:total>1</thr:total><georss:featurename>Facoltà di Ingegneria, Via Diotisalvi, 2, 56126 Pisa, Italy</georss:featurename><georss:point>43.7211488 10.3898478</georss:point><georss:box>43.7197143 10.3873803 43.722583300000004 10.3923153</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-8471111627405618003</id><published>2011-10-03T21:49:00.000+02:00</published><updated>2011-10-03T21:49:32.109+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="curriculum"/><category scheme="http://www.blogger.com/atom/ns#" term="cv"/><category scheme="http://www.blogger.com/atom/ns#" term="vitae"/><title type='text'>Curriculum Vitae</title><content type='html'>I finally managed to finish the &lt;a href=&quot;http://www.matteolandi.net/cv.html&quot;&gt;first draft&lt;/a&gt; of my &lt;b&gt;curriculum vitae&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-4lLC02dEyRQ/TojZibrc1yI/AAAAAAAABgs/J00180qHInw/s1600/cv.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-4lLC02dEyRQ/TojZibrc1yI/AAAAAAAABgs/J00180qHInw/s320/cv.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
What do you think about that? Have you got any advices to make it better? I would really appreciate it!&lt;br /&gt;
&lt;br /&gt;
Someone told me to stick to the &lt;a href=&quot;http://europass.cedefop.europa.eu/europass/home/vernav/Europass+Documents/Europass+CV.csp&quot;&gt;Europass Curriculum Vitae&lt;/a&gt; format, but I don&#39;t really understand &lt;b&gt;why&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
I see the curriculum vitae as something really personal, so why should I have to make &lt;b&gt;mine&lt;/b&gt; equal to &lt;b&gt;other&lt;/b&gt; ones? Do recruiters really care about that? Is my CV likely to be discarded just because it does not follow that &lt;a href=&quot;http://europass.cedefop.europa.eu/img/dynamic/c1346/type.FileContent.file/CVExamples_en_GB.pdf&quot;&gt;AWFUL&lt;/a&gt; template?&lt;br /&gt;
&lt;br /&gt;
I think what really matters is the &lt;b&gt;content&lt;/b&gt;: your &lt;em&gt;education&lt;/em&gt;, your &lt;em&gt;job experience&lt;/em&gt; and your &lt;em&gt;personal interests&lt;/em&gt;. Everything else is just &lt;b&gt;noise&lt;/b&gt;. That&#39;s why I went for a very &lt;em&gt;plain&lt;/em&gt; template.&lt;br /&gt;
&lt;br /&gt;
Hope I&#39;m not the only one thinking like that.</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/8471111627405618003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/10/curriculum-vitae.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8471111627405618003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8471111627405618003'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/10/curriculum-vitae.html' title='Curriculum Vitae'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-4lLC02dEyRQ/TojZibrc1yI/AAAAAAAABgs/J00180qHInw/s72-c/cv.jpg" height="72" width="72"/><thr:total>1</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-3470663901604377402</id><published>2011-10-02T22:40:00.000+02:00</published><updated>2011-10-02T22:40:09.883+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cygwin"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><title type='text'>Windows, Cygwin and PHP</title><content type='html'>&lt;br /&gt;
Those of you who constantly try to use Windows as a standard Linux/Unix OS, will agree with me that &lt;a href=&quot;http://www.cygwin.com/&quot;&gt;cygwin&lt;/a&gt; represents the &lt;b&gt;only&lt;/b&gt; solution to make Microsoft operating systems friendlier for users coming from different OSs.&lt;br /&gt;
&lt;br /&gt;
Some days ago I needed to install &lt;b&gt;PHP&lt;/b&gt; and &lt;b&gt;mysql&lt;/b&gt; on my machine; I fired cygwin &lt;b&gt;setup.exe&lt;/b&gt; application, updated the list of available packages, and finally looked for aforementioned binaries.&lt;br /&gt;
&lt;br /&gt;
With my surprise, &lt;b&gt;none&lt;/b&gt; of them was available for download.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Do I need to install a them by myself as &lt;b&gt;standalone&lt;/b&gt; binaries?&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
Fortunately, &lt;b&gt;not&lt;/b&gt;! Thanks to Google, I come to &lt;a href=&quot;http://sourceware.org/cygwinports/&quot;&gt;this page&lt;/a&gt; and followed the steps listed inside; in particular, I added a new &lt;a href=&quot;ftp://ftp.cygwinports.org/pub/cygwinports&quot;&gt;mirror&lt;/a&gt; to the list of cygwin mirrors, updated the list of available packages, and in the end, on the &lt;em&gt;package selection&lt;/em&gt; page I was able to find both &lt;b&gt;PHP&lt;/b&gt; and &lt;b&gt;mysql&lt;/b&gt; binaries.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Bingo!&lt;/em&gt;&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/3470663901604377402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/10/windows-cygwin-and-php.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3470663901604377402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/3470663901604377402'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/10/windows-cygwin-and-php.html' title='Windows, Cygwin and PHP'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-5058092674175655484</id><published>2011-09-14T11:34:00.000+02:00</published><updated>2011-09-14T12:01:48.502+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freebsd"/><category scheme="http://www.blogger.com/atom/ns#" term="hacks"/><category scheme="http://www.blogger.com/atom/ns#" term="kernel"/><category scheme="http://www.blogger.com/atom/ns#" term="picobsd"/><title type='text'>FreeBSD hacking!</title><content type='html'>Time has come to spend some time describing the workflow I followed for about 9 months when working on my &lt;a href=&quot;http://info.iet.unipi.it/~luigi/netmap/&quot;&gt;master&#39;s degree thesis&lt;/a&gt;; in that period I implemented a new &lt;a href=&quot;http://people.freebsd.org/~picobsd/old/picobsd.html&quot;&gt;character device&lt;/a&gt; for the FreeBSD Kernel, hence I have been busy all the time modifying the Kernel, compiling it, and finally testing it.&lt;br /&gt;
&lt;br /&gt;
In the first section of this post I will show you how to compile a custom FreeBSD Kernel, then how to speed-up the &lt;em&gt;debugging&lt;/em&gt; and &lt;em&gt;testing&lt;/em&gt; phases of the developing process making use of a very powerful tool called &lt;code&gt;picobsd&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#build_kernel&quot;&gt;Build a Kernel&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#download_tree&quot;&gt;Download the source tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#compile_toolchain&quot;&gt;Compile the toolchain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#configure_build_install&quot;&gt;Configure it, build it, and install it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;#picobsd&quot;&gt;picobsd&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#where&quot;&gt;Where can I find the script?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#bootstrap&quot;&gt;Bootstrap&lt;/a&gt;&lt;/h5&gt;&lt;li&gt;&lt;a href=&quot;#configure_image&quot;&gt;Configure an image and build it&lt;/a&gt;&lt;/h5&gt;&lt;li&gt;&lt;a href=&quot;#run&quot;&gt;Run&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;li&gt;&lt;a href=&quot;#tweaks&quot;&gt;Further tweaks&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;#conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;&lt;a name=&quot;build_kernel&quot;&gt;Build a Kernel&lt;/a&gt;&lt;/h4&gt;Extracted from the official &lt;a href=&quot;http://www.freebsd.org/doc/handbook/kernelconfig-custom-kernel.html&quot;&gt;handbook&lt;/a&gt;:  &lt;blockquote&gt;Building a custom kernel is one of the most important rites of passage for advanced BSD users. This process, while time consuming, will provide many benefits to your FreeBSD system. &lt;/blockquote&gt;And again:  &lt;blockquote&gt;This has a number of benefits, such as: 
- Faster boot time. Since the kernel will only probe the hardware you have on your system, the time it takes your system to boot can decrease dramatically. 
- Lower memory usage. A custom kernel often uses less memory than the GENERIC kernel by omitting unused features and device drivers. This is important because the kernel code remains resident in physical memory at all times, preventing that memory from being used by applications. For this reason, a custom kernel is especially useful on a system with a small amount of RAM. 
- Additional hardware support. A custom kernel allows you to add in support for devices which are not present in the GENERIC kernel, such as sound cards. &lt;/blockquote&gt;In my specific case, I was essentially moved by the need of creating a new Kernel module; but let&#39;s cut this sort of introduction, and move to the next section.   &lt;h5&gt;&lt;a name=&quot;download_tree&quot;&gt;Download the source tree&lt;/a&gt;&lt;/h5&gt;The first thing needed is obviously a Kernel source tree; depending on the steps you followed while installing your OS, you are likely to find that directly inside &lt;b&gt;/usr/src/sys&lt;/b&gt;; otherwise, proceed to download the source tree and move it somewhere on your hard disk. I usually prefer to checkout a OS snapshot directly from the official &lt;a href=&quot;http://svn.freebsd.org&quot;&gt;subversion repository&lt;/a&gt; and dump it in my home folder, inside the directory &lt;b&gt;~/workspace/FreeBSD/head&lt;/b&gt;:  &lt;pre&gt;cd ~/workspace/FreeBSD
svn co http://svn.freebsd.org/base/head
&lt;/pre&gt;Usually this is a very long operation, so I advise you go out for a walk: on your come back, everything will be there waiting for you.   &lt;h5&gt;&lt;a name=&quot;compile_toolchain&quot;&gt;Compile the toolchain&lt;/a&gt;&lt;/h5&gt;Once you are ready with the source tree, you are in need of building all the necessary tools you are going to use later on; indeed, this step will prevent you from trying to compile new KERNELS (or even worst a &lt;a href=&quot;http://www.freebsd.org/doc/handbook/makeworld.html&quot;&gt;&lt;b&gt;WHOLE NEW WORLD&lt;/b&gt;&lt;/a&gt;) using old compilers or linkers and eventually get strange errors like &lt;a href=&quot;http://freebsd.1045724.n5.nabble.com/buildkernel-error-td4044879.html&quot;&gt;this&lt;/a&gt;. Then issue:  &lt;pre&gt;cd head
make kernel-toolchain
&lt;/pre&gt;After that, you will be able to compile your first &lt;em&gt;hacked&lt;/em&gt; FreeBSD Kernel.   &lt;h5&gt;&lt;a name=&quot;configure_build_install&quot;&gt;Configure it, build it, and install it&lt;/a&gt;&lt;/h5&gt;Slowly, we are getting at the heart of the matter, indeed it&#39;s now time to take a Kernel configuration file from the downloaded source tree and start to tweak it in order to match our needs. Kernel &lt;em&gt;configs&lt;/em&gt; are usually placed inside the directory &lt;b&gt;sys/$arch/conf&lt;/b&gt; of the source tree, hence to start from the default configuration file for a &lt;b&gt;i386&lt;/b&gt; machine, issue:  &lt;pre&gt;cp sys/i386/conf/GENERIC sys/i386/conf/MYKERNEL
&lt;/pre&gt;One important thing to take into account while tweaking a FreeBSD Kernel config is that if you want to remove the support for a specific device (e.g. Realtek network adapters, or Sound Blaster audio cards), it is not sufficient to remove the specific lines (e.g &lt;code&gt;device re&lt;/code&gt;, or &lt;code&gt;device snd_emu10k1&lt;/code&gt;) from the aforementioned configuration file; by default, everything contained inside the Kernel &lt;em&gt;config&lt;/em&gt; is indeed built statically inside the Kernel image, while everything commented out is compiled as an external module. Hence, to force the Kernel tool-chain not to compile external modules, edit (or create) the file &lt;b&gt;/etc/make.conf&lt;/b&gt;, and add the following line:  &lt;pre&gt;NO_MODULES=1
&lt;/pre&gt;Committed this to memory, enjoy tweaking your own new Kernel. 

Once you feel enough confident of the new Kernel &lt;em&gt;config&lt;/em&gt;, compile it (and possibly fix dependencies errors):  &lt;pre&gt;make buildkernel KERNCONF=MYKERNEL -j8
&lt;/pre&gt;(the &lt;b&gt;-j&lt;/b&gt; specifies the number of parallel processes involved in the building process. As a rule of thumb, that value should be set equal to &lt;em&gt;two times the number of cores&lt;/em&gt;, hence in this case I&#39;m assuming you are running a quad-core processor.) 

At this point, if the previous command finished without any error, then install the new Kernel image, reboot the machine, and &lt;em&gt;cross your fingers&lt;/em&gt;:  &lt;pre&gt;sudo make installkernel KERNCONF=MYKERNEL
&lt;/pre&gt;If something goes wrong (i.e. unable to boot the machine), please have a look at &lt;a href=&quot;http://www.freebsd.org/doc/handbook/kernelconfig-trouble.html&quot;&gt;this&lt;/a&gt;: there is a solution for &lt;em&gt;everything&lt;/em&gt;! 

Well, the first section of this post has come to its end; let&#39;s move on the next one, and talk about a useful tool which is going to speed-up your Kernel &lt;em&gt;debug&lt;/em&gt; and &lt;em&gt;testing&lt;/em&gt; phases.   &lt;h4&gt;&lt;a name=&quot;picobsd&quot;&gt;picobsd&lt;/a&gt;&lt;/h4&gt;&lt;a href=&quot;http://people.freebsd.org/~picobsd/old/picobsd.html&quot;&gt;Originally&lt;/a&gt;, &lt;b&gt;PicoBSD&lt;/b&gt; was a &lt;em&gt;one floppy version of FreeBSD 3.0-current, which in its different variations allows you to have secure dialup access, small diskless router or even a dial-in server&lt;/em&gt;. &lt;a href=&quot;http://www.freebsd.org/cgi/man.cgi?query=picobsd&quot;&gt;Nowadays&lt;/a&gt;, &lt;code&gt;picobsd&lt;/code&gt; is THE script which produces such minimal implementations of FreeBSD. 

From the man page:  &lt;blockquote&gt;The &lt;code&gt;picobsd&lt;/code&gt; utility was originally created to build simple standalone systems such as firewalls or bridges, but because of the ability to cross-build images with different source trees than the one in the server, it can be extremely useful to developers to test their code with- out having to reinstall the system. &lt;/blockquote&gt;As you can see from the quoted paragraph, this turned out to be the perfect tool for my needs: instead of installing a new Kernel each time I touched a module, reboot, and hope modifications did not break anything, I could create a new &lt;b&gt;PicoBSD&lt;/b&gt; image (the Kernel would indeed be automatically built) and either dump it on a USB stick and boot a test machine or fire a new virtual machine with that image as hard disk.   &lt;h5&gt;&lt;a name=&quot;where&quot;&gt;Where can I find the script?&lt;/a&gt;&lt;/h5&gt;If you are reading this paragraph and you followed all the steps described so far, you will be able to find the &lt;code&gt;picobsd&lt;/code&gt; utility inside the source tree folder, in particular inside the directory &lt;b&gt;$tree/release/picobsd&lt;/b&gt;. Otherwise, checkout a copy of the &lt;a href=&quot;http://svn.freebsd.org/base/head/release/picobsd&quot;&gt;utility folder&lt;/a&gt; from the official subversion repository.  &lt;h5&gt;&lt;a name=&quot;bootstrap&quot;&gt;Bootstrap&lt;/a&gt;&lt;/h5&gt;It is now time to setup the &lt;b&gt;PicoBSD&lt;/b&gt; environment. This part is very similar to the &lt;a href=&quot;#compile_toolchain&quot;&gt;one&lt;/a&gt; in which we built the &lt;b&gt;kernel-toolchain&lt;/b&gt;, with the difference that now we need to build a &lt;em&gt;NEW OPERATING SYSTEM&lt;/em&gt;. Luckily, the new OS is very minimal, and consequently the building process is not supposed to take too long to complete; in addiction, the following is a &lt;em&gt;one-shot&lt;/em&gt; procedure, which means you need to do it only once, before to build the first &lt;b&gt;PicoBSD&lt;/b&gt; image. 

The utility, in order to start to build all the necessary stuff need to be instructed about the position of a FreeBSD source tree; this is done using by the option &lt;code&gt;--src&lt;/code&gt; (i.e. &lt;b&gt;--src ~/workspace/FreeBSD/usr&lt;/b&gt;); hence, assuming you checked out the &lt;code&gt;picobsd&lt;/code&gt; utility together with the FreeBSD source tree, to prepare the &lt;b&gt;PicoBSD&lt;/b&gt; environment issue a simple:  &lt;pre&gt;cd ~/workspace/FreeBSD/head/release/picobsd/build
./picobsd --src ~/workspace/FreeBSD/head -n --init
&lt;/pre&gt;(The option &lt;code&gt;-n&lt;/code&gt; disables &lt;em&gt;interactive mode&lt;/em&gt;.) 

If earlier I advised you to exit for a walk while downloading a fresh new OS tree, this time you are better off going off the records for a nap! 

Once you find the following message on your prompt, then you are ready to move on.  &lt;pre&gt;*** Type  NOT FOUND
=== Press enter to continue
---&gt; fail: Error &lt;1&gt; error code &lt;bad_type&gt; in &lt;&gt;
Error: unknown floppy type
---&gt; Aborting ./picobsd
&lt;/pre&gt;&lt;h5&gt;&lt;a name=&quot;configure_image&quot;&gt;Configure an image and build it&lt;/a&gt;&lt;/h5&gt;&lt;code&gt;picobsd&lt;/code&gt; is shipped with a couple of &lt;em&gt;pre-configured&lt;/em&gt; images you can use as starting points. Those can be found inside the &lt;code&gt;picobsd&lt;/code&gt; root folder (e.g.&lt;b&gt;bridge&lt;/b&gt; and &lt;b&gt;qemu&lt;/b&gt; directories):  &lt;pre&gt;cd ~/workspace/FreeBSD/head/release/picobsd
cp -R bridge mypimage
&lt;/pre&gt;For the moment let&#39;s keep the image as is: just try to compile it and fix possible errors (the utility is indeed very &lt;em&gt;oldish&lt;/em&gt; and barely updated):  &lt;pre&gt;cd ~/workspace/FreeBSD/head/release/picobsd/build
./picobsd --src ~/workspace/FreeBSD/head -n mypimage
&lt;/pre&gt;If the previous operations exits with the following error don&#39;t worry, there is an easy fix.  &lt;pre&gt;(.text+0xd4d): undefined reference to `ulog_login&#39;
&lt;/pre&gt;The error tell us that the compiler has not been able to find the library &lt;b&gt;ulog&lt;/b&gt;, hence we need to modify the configuration file of the image to instruct the compiler to include such library. To do so issue:  &lt;pre&gt;echo &#39;libs_so -lulog&#39; &amp;gt;&amp;gt; ../mypimage/crunch.conf
&lt;/pre&gt;and re-try to build the image; this time everything should be OK and inside the directory &lt;b&gt;build_dir-mypimage&lt;/b&gt; you will be able to find the &lt;b&gt;PicoBSD&lt;/b&gt; binary image:  &lt;pre&gt;ls -al build_dir-mypimage/*bin
-rw-r--r--  1 matteo  matteo  8388608 Sep 13 14:19 build_dir-netmap/picobsd.bin
&lt;/pre&gt;&lt;h5&gt;&lt;a name=&quot;run&quot;&gt;Run&lt;/a&gt;&lt;/h5&gt;We are now ready to test the new image. 

To do so, you can choose either to run it using a Virtual Machine like &lt;a href=&quot;http://qemu.org&quot;&gt;QEMU&lt;/a&gt;:  &lt;pre&gt;qemu build_dir-netmap/picobsd.bin -net nic,model=e1000
&lt;/pre&gt;or even better, dump the binary on a usb-stick:  &lt;pre&gt;dd if=build_dir-mypimage/picobsd.bin of=/dev/da0
&lt;/pre&gt;plug it into a &lt;em&gt;real machine&lt;/em&gt; and finally turn it on. In both cases, in a few seconds you will be presented a screen like this:  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-l7jyw2Uj4xw/Tm9rfVLjzyI/AAAAAAAABek/4OZ1u3hvAmo/s1600/qemu.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;178&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-l7jyw2Uj4xw/Tm9rfVLjzyI/AAAAAAAABek/4OZ1u3hvAmo/s320/qemu.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h5&gt;&lt;a name=&quot;tweaks&quot;&gt;Further tweaks&lt;/a&gt;&lt;/h5&gt;Let me finish with a couple tips for those who want to build a &lt;em&gt;fully customized&lt;/em&gt; &lt;b&gt;PicoBSD&lt;/b&gt; image.  &lt;ul&gt;&lt;li&gt;&lt;em&gt;How is it possible to include a certain application (i.e. &lt;code&gt;vi&lt;/code&gt;) inside the binary image?&lt;/em&gt;&lt;/li&gt;

The &lt;code&gt;picobsd&lt;/code&gt; utility, to fill the OS image with all the needed binaries makes use of a tool called &lt;a href=&quot;http://www.freebsd.org/cgi/man.cgi?query=crunchgen&amp;sektion=1&quot;&gt;crunchgen(1)&lt;/a&gt;: without entering too much into details, this tool compress an application and all the linked libraries together into a single executable. To add any program to your binary image you need to add the string &lt;code&gt;progs&lt;/code&gt; followed by the name of the desired application at the end of the configuration file located in the root folder of your custom &lt;b&gt;PicoBSD&lt;/b&gt; image:  &lt;pre&gt;cd ~/workspace/FreeBSD/head/release/picobsd
echo &#39;progs vi&#39; &amp;gt;&amp;gt; mypimage/crunch.conf
&lt;/pre&gt;Rebuild the image, boot it, and after the login you will be able to run your application. 

&lt;li&gt;&lt;em&gt;How is it possible to include a third-party application not included in the operating system source tree?&lt;/em&gt;&lt;/li&gt;

The answer to this question, is more or less linked to the previous one; in particular we have to tell &lt;code&gt;crunchgen&lt;/code&gt; how to compile our &lt;em&gt;special&lt;/em&gt; application, and where to find all the needed sources. 

Imagine we have the directory &lt;b&gt;foo&lt;/b&gt; containing all the sources of the application and the Makefile with the rules for the compiler; let&#39;s move it inside the root folder of our image:  &lt;pre&gt;mv /path/to/foo mypimage/
&lt;/pre&gt;and edit the &lt;code&gt;cruchgen&lt;/code&gt; configuration file and add the following lines:  &lt;pre&gt;progs foo
special foo srcdir @__CWD__@/bar
&lt;/pre&gt;which tell the script that in order to compile the application &lt;code&gt;foo&lt;/code&gt; there is a special directory &lt;b&gt;bar&lt;/b&gt; inside the root of the &lt;b&gt;PicoBSD&lt;/b&gt; image (i.e. &lt;b&gt;~/workspace/FreeBSD/head/release/picobsd/mypimage&lt;/b&gt;). 

Rebuild your image and run it: there it is!  &lt;/ul&gt;&lt;h4&gt;&lt;a name=&quot;conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/h4&gt;That&#39;s all folks; I hope you enjoyed the ride. &lt;br /&gt;
For more details about any specific utilities and procedures, please read the links spread all around the post or leave a comment. &lt;br /&gt;
&lt;br /&gt;
Cheers.</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/5058092674175655484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/09/freebsd-hacking.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/5058092674175655484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/5058092674175655484'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/09/freebsd-hacking.html' title='FreeBSD hacking!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-l7jyw2Uj4xw/Tm9rfVLjzyI/AAAAAAAABek/4OZ1u3hvAmo/s72-c/qemu.png" height="72" width="72"/><thr:total>2</thr:total><georss:featurename>Via Giunta Pisano, 1, 56126 Pisa, Italy</georss:featurename><georss:point>43.721241829683052 10.390427112579346</georss:point><georss:box>43.71980732968305 10.387959612579346 43.722676329683054 10.392894612579346</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-802600090828029166</id><published>2011-09-03T12:02:00.001+02:00</published><updated>2011-09-07T19:04:27.088+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="email"/><category scheme="http://www.blogger.com/atom/ns#" term="mercurial"/><category scheme="http://www.blogger.com/atom/ns#" term="notification"/><title type='text'>More about mercurial notify extension..</title><content type='html'>This morning, working on a personal mercurial repository hosted on my home server, I found out that the email commit notification system (described on this &lt;a href=&quot;http://matteolandi.blogspot.com/2011/07/setting-up-mercurial-server.html&quot;&gt;previous post&lt;/a&gt;) was not working anymore; on each &lt;b&gt;commit&lt;/b&gt; I got back the following message:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;changegroup.notify hook raised an exception: [Errno 2] No such file or directory: &#39;../subscription.conf&#39;
&lt;/pre&gt;&lt;br /&gt;
I checked the root folder of the repo, and the &lt;b&gt;subscription&lt;/b&gt; file was there.&lt;br /&gt;
&lt;br /&gt;
I checked the &lt;b&gt;configuration&lt;/b&gt; file of the repo, and it was correctly pointing to the &lt;i&gt;right&lt;/i&gt; subscription file.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;What has happened to my mercurial repository? I didn&#39;t touch anything!&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;The solution!&lt;/h4&gt;After a couple of shots in the dark, I eventually managed to solve the issue. As described in the previous post, I used a &lt;b&gt;relative&lt;/b&gt; path to make the repository configuration file aware of the list of subscribers, and for some strange reasons (honestly, I did not investigate it too much...), this path was not followed anymore. Consequently I tried with an &lt;b&gt;absolute&lt;/b&gt; path, and commits started to be notified again. Yay!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/802600090828029166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/09/more-about-mercurial-notify-extension.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/802600090828029166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/802600090828029166'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/09/more-about-mercurial-notify-extension.html' title='More about mercurial notify extension..'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.2568888</georss:point><georss:box>43.828751999999994 10.177924800000001 43.920322 10.3358528</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-5608209530646906739</id><published>2011-07-24T22:45:00.001+02:00</published><updated>2011-09-03T12:04:15.345+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="email"/><category scheme="http://www.blogger.com/atom/ns#" term="mercurial"/><category scheme="http://www.blogger.com/atom/ns#" term="notification"/><category scheme="http://www.blogger.com/atom/ns#" term="server"/><title type='text'>Setting up a Mercurial server.</title><content type='html'>[Update: if you get error messages from the email notification extension, read &lt;a href=&quot;http://matteolandi.blogspot.com/2011/09/more-about-mercurial-notify-extension.html&quot;&gt;this&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
Today I&#39;m going to write down a list of notes and tips I&#39;ve collected these days while trying to setup a &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; server. Even if Internet is full of well-written guides and tutorials describing how to approach the task, I chose to keep gathered information in a single place (i.e. this post) so that to simplify the life of others pursuing the same goal.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;My needs were very simple: &lt;b&gt;share&lt;/b&gt; Mercurial repositories by mean of my humble EEEBox (which is actually hosting &lt;a href=&quot;http://matteolandi.net&quot;&gt;matteolandi.net&lt;/a&gt;), make committers log in with the &lt;b&gt;same system account&lt;/b&gt; (using &lt;b&gt;public-keys&lt;/b&gt;), send &lt;b&gt;email notifications&lt;/b&gt; on each repositories&#39; &lt;b&gt;push&lt;/b&gt; and finally make it possible to &lt;b&gt;browse&lt;/b&gt; aforementioned repositories via &lt;b&gt;web&lt;/b&gt; (in particular using Apache).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Create a new user and configure access keys&lt;/h4&gt;As stated above, my intention was to map all the committers on a single system user, &lt;b&gt;hg&lt;/b&gt;, and use different &lt;b&gt;public-keys&lt;/b&gt; to access the repository.&lt;br /&gt;
&lt;br /&gt;
To do so, create the new system user issuing the command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ sudo useradd hg&lt;/pre&gt;&lt;br /&gt;
and follow the instructions printed on screen:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Adding user `hg&#39; ...
Adding new group `hg&#39; (1003) ...
Adding new user `hg&#39; (1003) with group `hg&#39; ...
Creating home directory `/home/hg&#39; ...
Copying files from `/etc/skel&#39; ...
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for hgg
Enter the new value, or press ENTER for the default
        Full Name []: 
        Room Number []: 
        Work Phone []: 
        Home Phone []: 
        Other []: 
Is the information correct? [Y/n] Y
&lt;/pre&gt;&lt;br /&gt;
After that, edit the file &lt;b&gt;authorized_keys&lt;/b&gt; contained inside the directory &lt;b&gt;.ssh&lt;/b&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ sudo su hg
$ vim ~/.ssh/authorized_keys
&lt;/pre&gt;&lt;br /&gt;
and add as many public-keys as needed, associated with a command telling the ssh daemon to execute the program &lt;b&gt;hg-ssh&lt;/b&gt; on incoming connections:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;# Matteo Landi, laptop
command=&quot;hg-ssh /home/hg/*&quot; ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3yL4G9dOPZ30koZ6v7UKzA2LnfbvH1orel9Gyj7+P0WtYGj1TXhUPU6znREoQ7CcHlABQ5sNfC1tg3rZ2RimmCL4XjK9XNQdHx67TqxAOfcfChnetKb0MMWdrd8ZRMKPC1m2rVdrvXtFO19BUrc+M3JFSY5ZNXZCz3bznC+fNNLqrhE3hdkpCyUNhFClMpLrQMu9i+LvcclurCdQlcn39UvBh8RyIrPIB4QWd43R536TUjYZ+1rRBIyilYIeQ4naaxrQxsxfnmcuaKjrycgz2tWunGC2oRx6odMR2RBsW9BaIH7rMAfK4EC7/tibRxJsmRWtM26qlSAbUlZNsJQBEQ== matteo@COMet
&lt;/pre&gt;&lt;br /&gt;
At this point, trying to reach the remote machine hosting the Mercurial server with the command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ ssh hg@code.matteolandi.net&lt;/pre&gt;&lt;br /&gt;
you should get back the following error:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Illegal command &#39;?&#39;
Connection to code.matteolandi.net closed.
&lt;/pre&gt;&lt;br /&gt;
which is nothing but the evidence that (till now) everything has been done correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Get Mercurial&lt;/h4&gt;Now that the system is almost ready to host the Mercurial server (later we are going to configure Apache), we need to get Mercurial installed on the system (note: Mercurial client version is enough, for it includes all the extensions and scripts necessary for our purposes).&lt;br /&gt;
&lt;br /&gt;
On Debian-like systems (for other ones, please refer to the official &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;site&lt;/a&gt;), to install such a package you simply have to issue the command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ sudo apt-get install mercurial&lt;/pre&gt;&lt;br /&gt;
and everything should be ready in a couple of minutes:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ hg
Mercurial Distributed SCM

basic commands:

 add        add the specified files on the next commit
 annotate   show changeset information by line for each file
 clone      make a copy of an existing repository
 commit     commit the specified files or all outstanding changes
 diff       diff repository (or selected files)
 export     dump the header and diffs for one or more changesets
 forget     forget the specified files on the next commit
 init       create a new repository in the given directory
 log        show revision history of entire repository or files
 merge      merge working directory with another revision
 pull       pull changes from the specified source
 push       push changes to the specified destination
 remove     remove the specified files on the next commit
 serve      start stand-alone webserver
 status     show changed files in the working directory
 summary    summarize working directory state
 update     update working directory (or switch revisions)

use &quot;hg help&quot; for the full list of commands or &quot;hg -v&quot; for details
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Create and configure repository&lt;/h4&gt;As a rule, we want to host all Mercurial repositories inside &lt;b&gt;/home/hg&lt;/b&gt;, hence let&#39;s start by creating a new &lt;b&gt;foo&lt;/b&gt; (duh) repository (expected user: &lt;b&gt;hg&lt;/b&gt;; expected directory: &lt;b&gt;/home/hg&lt;/b&gt;):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ hg init foo&lt;/pre&gt;&lt;br /&gt;
and then enable email notifications for each &lt;em&gt;push&lt;/em&gt; action. To do so, edit the file &lt;b&gt;.hg/hgrc&lt;/b&gt; contained inside the repository folder, and add the text below:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;[web]
# the baseurl is used for constructing hgweb-URLs for the mails 
baseurl = http://code.matteolandi.net/hg

[extensions]
hgext.notify =

[hooks]
# send one email for each push
changegroup.notify = python:hgext.notify.hook

[email]
# address sending notifications
from = Code | matteolandi.net &amp;lt;noreply@code.matteolandi.net&amp;gt;

[smtp]
# host of the smtp server; use localhost, if local
host = smtp.tiscali.it

[notify]
# Presumably an email will be sent if an action corresponding to any of
# the listed keywords is performed.
sources = serve push pull bundle

# if True, print email content instead of actually sending it
test = False

# subscription information: which user is interested in changes of which
# part of the repository
config = ../subscription.conf

# changeset template
template = Subject: {webroot}: changeset {rev}\n\ndetails: {baseurl}/{webroot|basename}/rev/{node|short}\nchangeset: {rev}:{node|short}\nuser: {author}\ndate: {date|date}\ndescription:\n{desc}\n

# max lines of diffs to include (0=none, -1=all)
maxdiff = 300
&lt;/pre&gt;&lt;br /&gt;
Moreover, create the subscription file inside the root of the repository with the following content:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;[reposubs]
# key is a global matching part of a repo, value is a comma separated
# list of subscribers
* = Matteo Landi &amp;lt;you@myourdomain.net&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
This tells Mercurial that the user Matteo Landi (a.k.a. me) is interested in receiving notifications about &lt;b&gt;all&lt;/b&gt; the changes committed to the repository (for additional information about the notify extension, have a look at &lt;a href=&quot;http://mercurial.selenic.com/wiki/NotifyExtension&quot;&gt;its documentation&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Configure Apache&lt;/h4&gt;The amount of effort needed to configure Apache or other web-servers to display Mercurial repositories is very limited. The following is a configuration based on &lt;em&gt;cgi-scripts&lt;/em&gt;, however there are plenty &lt;a href=&quot;http://mercurial.selenic.com/wiki/PublishingRepositories&quot;&gt;alternatives lying around&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;hgweb&lt;/b&gt;, our HTTP repository viewer, consists of a cgi-script invoked by the web-server, and a configuration file which tells the application where to find repositories.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s start by copying the script (shipped with Mercurial) inside the home directory of the user &lt;b&gt;hg&lt;/b&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ cp /usr/share/doc/mercurial-common/examples/hgweb.cgi ~&lt;/pre&gt;&lt;br /&gt;
and modify its content to match our needs; in particular edit the &lt;b&gt;config&lt;/b&gt; variable to make it point to our configuration file (we are going to create it later):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;...
config = &quot;/home/hg/hgweb.conf&quot;
...
&lt;/pre&gt;&lt;br /&gt;
Create then, such &lt;b&gt;hgweb.conf&lt;/b&gt; file and fill it with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;[collections]
repos = /home/hg
&lt;/pre&gt;&lt;br /&gt;
Those lines tell &lt;b&gt;hgweb&lt;/b&gt; that each folder contained inside &lt;b&gt;/home/hg&lt;/b&gt; corresponds to a Mercurial repository.&lt;br /&gt;
&lt;br /&gt;
Finally modify your Apache virtual host to make it fire the cgi-script on &lt;b&gt;/hg/...&lt;/b&gt; URL requests:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;ScriptAlias /hg /home/hg/hgweb.cgi&lt;/pre&gt;&lt;br /&gt;
At this point, after a reboot of the web-server:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;sudo apache2ctl graceful&lt;/pre&gt;&lt;br /&gt;
you should be able to browse your Mercurial repositories with no problems, as shown in the figure below.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-RvBhIRgEL10/Tirg3WwoMqI/AAAAAAAAAXU/slCF5j8Qg3g/s1600/hgweb.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;228&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-RvBhIRgEL10/Tirg3WwoMqI/AAAAAAAAAXU/slCF5j8Qg3g/s320/hgweb.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
In addition, if you want &lt;b&gt;hgweb&lt;/b&gt; to disable web-pushes (default configuration enables them only for SSL connections), edit the file &lt;b&gt;.hg/hgrc&lt;/b&gt; with the repository, and add:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;[web]
push_ssl = False
&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Test: clone, edit, commit and push&lt;/h4&gt;The time has come to verify the correctness of all operations executed so far; on your local machine or laptop, move to a temporary directory:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ cd /tmp&lt;/pre&gt;&lt;br /&gt;
and clone the &lt;b&gt;foo&lt;/b&gt; repository:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ hg clone ssh://hg@code.matteolandi.net/foo&lt;/pre&gt;&lt;br /&gt;
Move inside the cloned repo and create a new file with random content:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ cd foo
$ fortune &amp;gt;&amp;gt; bar
&lt;/pre&gt;&lt;br /&gt;
Commit changes to the local repository with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ hg add bar
$ hg ci bar -m &quot;`fortune`&quot;
&lt;/pre&gt;&lt;br /&gt;
and finally propagate changes to the remote repository:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ hg push&lt;/pre&gt;&lt;br /&gt;
If everything is ok (and I hope so), you should get back a message similar to this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;pushing to ssh://hg@code.matteolandi.net/foo
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
remote: notify: sending 1 subscribers 1 changes
&lt;/pre&gt;&lt;br /&gt;
and your inbox should have a new message waiting for your attention:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-0Sd6jIW5cWc/TiyBvzi3waI/AAAAAAAAAXc/A6SRnFIcn3E/s1600/commit-shot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;137&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-0Sd6jIW5cWc/TiyBvzi3waI/AAAAAAAAAXc/A6SRnFIcn3E/s320/commit-shot.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/5608209530646906739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/07/setting-up-mercurial-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/5608209530646906739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/5608209530646906739'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/07/setting-up-mercurial-server.html' title='Setting up a Mercurial server.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-RvBhIRgEL10/Tirg3WwoMqI/AAAAAAAAAXU/slCF5j8Qg3g/s72-c/hgweb.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>55049 Viareggio Lucca, Italy</georss:featurename><georss:point>43.874537 10.25688880000007</georss:point><georss:box>43.833963999999995 10.21486080000007 43.91511 10.29891680000007</georss:box></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-574501878799682076</id><published>2011-02-20T19:44:00.005+01:00</published><updated>2011-02-21T00:17:13.168+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="books"/><category scheme="http://www.blogger.com/atom/ns#" term="events"/><category scheme="http://www.blogger.com/atom/ns#" term="music"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="thesis"/><title type='text'>I&#39;m still alive .. (cont.)</title><content type='html'>Sorry for the silence of the last month, but I&#39;ve been busy carrying on my master&#39;s degree thesis. When you are really focused on something for the 70% of the day, it&#39;s hard to find the time to keep you up to date: all you need is a couch and some silence.&lt;br /&gt;&lt;a href=&quot;http://matteolandi.blogspot.com/2011/02/im-still-alive.html&quot;&gt;Yesterday&lt;/a&gt;, I decided to break the silence with an image; today I&#39;m going to talk about a couple of things.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Books&lt;/h4&gt;&lt;br /&gt;I can&#39;t believe it! At the &lt;span style=&quot;font-style:italic;&quot;&gt;tender&lt;/span&gt; age of 24, I started enjoying reading: reading in the train, reading before to sleep, reading everywhere every time you are passively waiting for something. I began with some tech books about &lt;a href=&quot;http://amzn.com/0596007973&quot;&gt;programming languages&lt;/a&gt;, &lt;a href=&quot;http://amzn.com/059652983X&quot;&gt;editors&lt;/a&gt; and &lt;a href=&quot;http://amzn.com/1449381561&quot;&gt;databases&lt;/a&gt;, and then I switched to something different like the one I&#39;m currently reading: &lt;a href=&quot;http://amzn.com/8804554509&quot;&gt;Gomorra&lt;/a&gt;.&lt;br /&gt;Finally, a personal advice: &lt;a href=&quot;http://amzn.com/0307463745&quot;&gt;Rework&lt;/a&gt; from &lt;a href=&quot;http://37signals.com&quot;&gt;37signals&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Music&lt;/h4&gt;&lt;br /&gt;After 5 years of inactivity, I decided to put my hands on my electric-bass again: I did the dusting, bought a new pick and finally switched on the amp! What a joy!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Events&lt;/h4&gt;&lt;br /&gt;2011 seems to be an year full of tech events.&lt;br /&gt;Two weeks ago I moved to Milan for the 1st &lt;a href=&quot;http://hackitaly.org&quot;&gt;Hackitaly&lt;/a&gt; day. 10 hours of pure programming with one single goal: create mashups making use of the APIs offered by a list of registered companies. I worked in team with &lt;a href=&quot;http://twitter.com/kinglizard86&quot;&gt;@kinglizard86&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/lawrenceoluyede&quot;&gt;@lawrenceoluyede&lt;/a&gt;, but unfortunately we didn&#39;t succeeded in presenting our creations. However, I do really enjoyed the ride: here is a slide-show of the event.&lt;br /&gt;&lt;br /&gt;&lt;iframe title=&quot;YouTube video player&quot; width=&quot;560&quot; height=&quot;349&quot; src=&quot;http://www.youtube.com/embed/jFwUNsgvFXk&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Upcoming events:&lt;br /&gt;- &lt;a href=&quot;http://www.codemotion.it/&quot;&gt;Codemotion&lt;/a&gt;, held in Rome the 5th of March.&lt;br /&gt;- &lt;a href=&quot;http://ep2011.europython.eu/&quot;&gt;EuroPython&lt;/a&gt;, held in Florance from 20th to 26th of June.&lt;br /&gt;- &lt;a href=&quot;http://www.bettersoftware.it/&quot;&gt;BetterSoftware&lt;/a&gt;, held in Florance from 27th to 28th of June.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Projects&lt;/h4&gt;&lt;br /&gt;As I told you before, it&#39;s hard to find time for projects other than my thesis. However, in a couple of days I created a &lt;a hfref=&quot;https://github.com/iamFIREcracker/pomodoro&quot;&gt;simple application&lt;/a&gt; which helped me a lot in keeping focus on the things I&#39;m working on: it&#39;s based on the &lt;a href=&quot;http://www.pomodorotechnique.com/&quot;&gt;pomorodo technique&lt;/a&gt;.&lt;br /&gt;Another funny script (thought with &lt;a href=&quot;http://twitter.com/kinglizard86&quot;&gt;@kinglizard86&lt;/a&gt;) I would like to share is &lt;a href=&quot;http://code.matteolandi.net/viewvc/hacks/svn-tools/s.sh&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;s&lt;/span&gt;&lt;/a&gt;, a &lt;span style=&quot;font-style:italic;&quot;&gt;svn&lt;/span&gt; wrapper that tries to enhance the behavior of a couple of subversion commands: in particular the wrapper was created to make it possible to display the changes of the current commit while writing the log message.&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/-cR-baE9DXB4/TWGUfePibRI/AAAAAAAAAOg/Q5nSbLbFM-I/s1600/s.PNG&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 194px;&quot; src=&quot;http://3.bp.blogspot.com/-cR-baE9DXB4/TWGUfePibRI/AAAAAAAAAOg/Q5nSbLbFM-I/s320/s.PNG&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5575901082076474642&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Thesis&lt;/h4&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Dulcis in fundo&lt;/span&gt;, a couple of updates about the ongoing work of my master&#39;s degree thesis.&lt;br /&gt;After more than month spent in researching the state of art of packet filtering and kernel interaction from userspace, I finally started to hack the kernel. In particular I learned how to create character devices, how to modify the Intel lem driver to make it aware of my device for userland communication, and finally how to implement a very simple and inefficient memory allocator.&lt;br /&gt;Even though there are a lot of things that need to be done such as asynchronous I/O and integration with other drivers such as em and igb (unfortunately not yet supported by qemu), I&#39;m very happy with the work done!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That&#39;s all folks for tonight. Thanks for reading!</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/574501878799682076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/02/im-still-alive-cont.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/574501878799682076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/574501878799682076'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/02/im-still-alive-cont.html' title='I&#39;m still alive .. (cont.)'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/jFwUNsgvFXk/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-1724880257773926386</id><published>2011-02-19T17:19:00.002+01:00</published><updated>2011-02-19T17:22:57.058+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freebsd"/><category scheme="http://www.blogger.com/atom/ns#" term="kernel"/><category scheme="http://www.blogger.com/atom/ns#" term="userspace"/><title type='text'>I&#39;m still alive..</title><content type='html'>&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/-YTBQnIt5qtY/TV_t7hsV8zI/AAAAAAAAAOY/t8zE274v564/s1600/rxtxNICrings.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 247px; height: 320px;&quot; src=&quot;http://1.bp.blogspot.com/-YTBQnIt5qtY/TV_t7hsV8zI/AAAAAAAAAOY/t8zE274v564/s320/rxtxNICrings.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5575436470620386098&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;..stay tuned!</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/1724880257773926386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2011/02/im-still-alive.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1724880257773926386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1724880257773926386'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2011/02/im-still-alive.html' title='I&#39;m still alive..'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-YTBQnIt5qtY/TV_t7hsV8zI/AAAAAAAAAOY/t8zE274v564/s72-c/rxtxNICrings.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-8094174945324849184</id><published>2010-12-30T12:28:00.001+01:00</published><updated>2010-12-30T12:42:32.345+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hitsmash"/><category scheme="http://www.blogger.com/atom/ns#" term="summary"/><title type='text'>Hitsmash: conclusion (part 7)</title><content type='html'>It&#39;s been a month since the &lt;a href=&quot;http://matteolandi.blogspot.com/2010/11/hitsmash-introduction-part-1.html&quot;&gt;first&lt;/a&gt; blog entry about &lt;a href=&quot;http://hitsmash.matteolandi.net/&quot;&gt;Hitsmash&lt;/a&gt;. The time has come give a brief summary of this funny game.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Analytics&lt;/h4&gt;&lt;br /&gt;Have my readers enjoyed Hitsmash?&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQ3uE0FsAoI/AAAAAAAAANg/4A4_X9Yuhso/s1600/hitsmash-analytics.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 278px;&quot; src=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQ3uE0FsAoI/AAAAAAAAANg/4A4_X9Yuhso/s320/hitsmash-analytics.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5552355682087076482&quot; /&gt;&lt;/a&gt;&lt;br /&gt;They do ... on the first day.&lt;br /&gt;This is perfectly aligned with my expectations; indeed I created anything innovative or attractive. How is it supposed to capture the attention of the reader and make him come back a second time and vote a couple of videos? It does not.&lt;br /&gt; &lt;br /&gt;&lt;h4&gt;Difficulties&lt;/h4&gt;&lt;br /&gt;Difficulties arise when you work with something you are not used to; PHP in my case. I&#39;m not used to PHP, and the quality of the &lt;a href=&quot;http://code.matteolandi.net/viewvc/hacks/hitsmash/document_root/index.php?view=markup&quot;&gt;code&lt;/a&gt; is the evidence of this. However, it seems to do the trick, isn&#39;t it?&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Google it!&lt;/h4&gt;&lt;br /&gt;Please, stop &lt;span style=&quot;font-style:italic;&quot;&gt;reinventing the wheel&lt;/span&gt; (Matteo, I&#39;m also talking to you!), especially if you are interested in presenting a working prototype of your creation as soon as possible.&lt;br /&gt;I needed to parse an HTML table and I was not the first one facing up to this problem. If you were me, what would you have done? Knowing my not so strong skills with &lt;a href=&quot;http://en.wikipedia.org/wiki/Regular_expression&quot;&gt;regexps&lt;/a&gt;, I decided to use &lt;a href=&quot;http://projects.nigelsim.org/svn/table_parser/table_parser.py&quot;&gt;someone else&#39;s wheel&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Source code&lt;/h4&gt;&lt;br /&gt;Finally, you can find all the scripts of the previous posts, and the ones I have not published yet (like the &lt;a href=&quot;http://hitsmash.matteolandi.net/chart.php&quot;&gt;chart page&lt;/a&gt;), inside the &lt;span style=&quot;font-weight:bold;&quot;&gt;hacks&lt;/span&gt; repository (&lt;a href=&quot;http://code.matteolandi.net/viewvc/hacks/hitsmash/&quot;&gt;hitsmash&lt;/a&gt; sub-folder).&lt;br /&gt;Feel free to find any bugs (Locks? MySQL injection? ..) so I can try fix them, eventually.</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/8094174945324849184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2010/12/hitsmash-conclusion-part-7.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8094174945324849184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8094174945324849184'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2010/12/hitsmash-conclusion-part-7.html' title='Hitsmash: conclusion (part 7)'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_DsTRtFoKWX4/TQ3uE0FsAoI/AAAAAAAAANg/4A4_X9Yuhso/s72-c/hitsmash-analytics.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-8763330303582437786</id><published>2010-12-13T16:15:00.002+01:00</published><updated>2010-12-13T16:52:18.720+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freebsd"/><category scheme="http://www.blogger.com/atom/ns#" term="kernel"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><title type='text'>FreeBSD: kernel networking for dummies</title><content type='html'>Today I would like to share with you some notes I collected in the last month during my research study for the final work of my master&#39;s degree. At the time of writing, the title of my thesis is something like &lt;span style=&quot;font-style:italic;&quot;&gt;&quot;Design and Development of an API for Packet Filtering in Userland&quot;&lt;/span&gt;, hence I will try to hack the actual FreeBSD network stack in order to improve, hopefully, the performance of operations such as packet filtering and packet routing.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;br /&gt;In order to keep things as simple as possible, let&#39;s start with a list of events that describe what happens when a frame is received by the network adapter.&lt;br /&gt;First of all, the received frame is queued inside a FIFO; when the FIFO is full, new packets will be dropped. Afterward, one frame is extracted from the FIFO and placed inside the memory of the OS. At this point an interrupt is raised and the OS is notified about the new frame (in reality it&#39;s not true that an interrupt is raised per received frame: indeed, a mechanism called &lt;span style=&quot;font-style:italic;&quot;&gt;&quot;Interrupt Mitigation&quot;&lt;/span&gt;, is used to prevent the CPU from spending too much time handling such interrupts instead of doing &quot;useful&quot; work). In the end the kernel serves this interrupt by scheduling a task which is going to collect these frames.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Network Device&lt;/h4&gt;&lt;br /&gt;Let&#39;s try to analyze now, how network devices work, what kind of data structures they need and how they are supposed to move data to and from the OS memory.&lt;br /&gt;We could think of a network device as a collection of registers with no memory at all. We only need to know how to modify those registers, the shape of &lt;span style=&quot;font-style:italic&quot;&gt;wrappers&lt;/span&gt; created around the received frames and obviously how to register notifications for different events.&lt;br /&gt;Network devices are usually designed to work with ring-buffers in order to simplify things: instead of allocate a new buffer each time a frame is received, we pre-allocate a bunch of them, and use pointers to mark the available ones. As a result we only need to tell the device where the ring is located, where to put a new upcoming frame and where is the oldest received frame not yet processed by the kernel.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYnShRPMcI/AAAAAAAAANI/mR2Xwe_bdg8/s1600/ring-buffer-1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYnShRPMcI/AAAAAAAAANI/mR2Xwe_bdg8/s320/ring-buffer-1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166789902512578&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In the example above, we have pre-allocated nine slots, each one wide enough to contain a wrapper for the frame. Furthermore, five of nine slots contain useful data for the OS. Once the kernel collects these frames, it will increment the tail register, so that the network device is notified about the availability of new slots (when the kernel modifies these pointers, it actually sets the registers of the network device).&lt;br /&gt;I spoke about wrappers because once a network device receives a new frame, it needs some extra-space to store information about received data such as length, status and so forth. Moreover, the chunk of memory used to store incoming data is not contained inside the wrapper, but is reachable by a pointer contained into the slot. A more realistic view of the ring-buffer is shown in the figure below.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQYnSqh4LeI/AAAAAAAAANA/EyKCWQ86hmI/s1600/ring-buffer-2.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQYnSqh4LeI/AAAAAAAAANA/EyKCWQ86hmI/s320/ring-buffer-2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166792388226530&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Kernel&#39;s point of view&lt;/h4&gt;&lt;br /&gt;At this point, an interrupt is raised and a task is scheduled to collect these frames.&lt;br /&gt;From the kernel&#39;s point of view, packets are wrapped inside &lt;span style=&quot;font-style:italic;&quot;&gt;mbufs&lt;/span&gt;, structures that contain both information about the header and about the data of a packet. Moreover, mbufs are arranged into lists, so that it is possible to have packets larger than a single network frame. The structure that handles all these mbufs is close to the one shown in the figure above: we have a ring of slots, each with a pointer to an mbuf containing the data of the frame, and we have a pointer to the next slot to collect.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://4.bp.blogspot.com/_DsTRtFoKWX4/TQYm0zRA0YI/AAAAAAAAAM4/QEYBLIvLHKc/s1600/ring-buffer-3.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;http://4.bp.blogspot.com/_DsTRtFoKWX4/TQYm0zRA0YI/AAAAAAAAAM4/QEYBLIvLHKc/s320/ring-buffer-3.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166279337333122&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In the figure above, I intentionally used two different colors to mark that the ring holds two different packets: the first contained inside SLOT[1:3] and the second inside SLOT[4:6].&lt;br /&gt;The task, once scheduled, will join together the frames of a given packet, detach the mbufs from the ring, send the chain to the upper level of the network stack and increment the pointer.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQYm0hkAJXI/AAAAAAAAAMw/-TJD-xlQ-gc/s1600/ring-buffer-4.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 257px;&quot; src=&quot;http://3.bp.blogspot.com/_DsTRtFoKWX4/TQYm0hkAJXI/AAAAAAAAAMw/-TJD-xlQ-gc/s320/ring-buffer-4.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166274585142642&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In reality, a new buffer is allocated and linked to the freed slot before &lt;span style=&quot;font-style:italic&quot;&gt;next_to_check&lt;/span&gt; is incremented.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;NIC &amp; Kernel&lt;/h4&gt;&lt;br /&gt;So far we have seen how network devices are supposed to work, and how the kernel reacts to inputs from such devices. Let&#39;s try now to join these two views together and show how everything actually works inside the FreeBSD kernel.&lt;br /&gt;We have two rings of data: does this means that we have two copies of the same frame stored in different buffers? No! In order to explain why, you have to know that we have two ways to store data inside mbufs: the first one is put data inside the structure itself and the second one is to allocate an external buffer and point to it.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYm0b2YDRI/AAAAAAAAAMo/Wdk99o8W-KM/s1600/mbuf-1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 220px;&quot; src=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYm0b2YDRI/AAAAAAAAAMo/Wdk99o8W-KM/s320/mbuf-1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166273051594002&quot; /&gt;&lt;/a&gt;&lt;br /&gt;The latter could be used for our purposes: we can make the slots reserved for the network device, point to the external memory associated to the mbuf. This way, there is no need to copy data between the device-reserved memory and the kernel one: network devices will automatically dump frames inside kernel memory.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYm0KxxfmI/AAAAAAAAAMg/pdRu2kkcDYI/s1600/summary-1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 134px;&quot; src=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYm0KxxfmI/AAAAAAAAAMg/pdRu2kkcDYI/s320/summary-1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166268468887138&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Reality&lt;/h4&gt;&lt;br /&gt;As you can imagine, things are not as easy as I pictured above. Network devices don&#39;t know anything about virtual memory: they write directly inside physical memory (Direct Memory Access). For this reason we have to make a couple of changes to the pictures above: the ring buffer used by the network device  and the buffers pointed by both the network slots and the mbufs, have to be DMA-mapped.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYmz-BiMQI/AAAAAAAAAMY/MnMH1hSht8s/s1600/summary-2.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 288px;&quot; src=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYmz-BiMQI/AAAAAAAAAMY/MnMH1hSht8s/s320/summary-2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5550166265045332226&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Transmit&lt;/h4&gt;&lt;br /&gt;While transmitting data, things are not that different from the ones shown above.&lt;br /&gt;First of all, when the kernel needs to send some data, it creates a chain of mbufs and it queues them inside a FIFO. Then, a chain is extracted from the FIFO, broken down into single mbufs, and attached to a ring buffer (more or less equivalent to the structure &lt;span style=&quot;font-style:italic;&quot;&gt;rx_buffers&lt;/span&gt; shown in the picture above). At this point, we notify the network device about the presence of a new frame so that it can send it.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Author&#39;s Note&lt;/h4&gt;&lt;br /&gt;For more information about the subject, checkout the kernel, take a deep breath, and start diving.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Thanks for reading&lt;br /&gt;&lt;br /&gt;P.S. Feel free to comment for questions and/or errors.</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/8763330303582437786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2010/12/freebsd-kernel-networking-for-dummies.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8763330303582437786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/8763330303582437786'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2010/12/freebsd-kernel-networking-for-dummies.html' title='FreeBSD: kernel networking for dummies'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_DsTRtFoKWX4/TQYnShRPMcI/AAAAAAAAANI/mR2Xwe_bdg8/s72-c/ring-buffer-1.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-1825043987369776060</id><published>2010-11-23T14:56:00.002+01:00</published><updated>2010-11-23T15:50:33.448+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="deployment"/><category scheme="http://www.blogger.com/atom/ns#" term="hitsmash"/><title type='text'>Hitsmash: deployment (part 6)</title><content type='html'>Are you ready to take the plunge? I hope so!&lt;br /&gt;&lt;br /&gt;Clear the developement database, and populate it with new data.&lt;br /&gt;Create a new Google Analytics profile, and paste the activation code inside the PHP scripts.&lt;br /&gt;Register a new 3rd level domain pointing to your homemade server.&lt;br /&gt;Configure Apache in order to accept requests addressed to the new registered domain.&lt;br /&gt;Restart Apache.&lt;br /&gt;Cross your fingers.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:20px;&quot;&gt;&lt;a href=&quot;http://hitsmash.matteolandi.net&quot;&gt;http://hitsmash.matteolandi.net&lt;/a&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/1825043987369776060/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2010/11/hitsmash-deployment-part-6.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1825043987369776060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/1825043987369776060'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2010/11/hitsmash-deployment-part-6.html' title='Hitsmash: deployment (part 6)'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3860519759344647288.post-7608004911511641470</id><published>2010-11-23T13:25:00.003+01:00</published><updated>2010-11-23T14:56:41.413+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hitsmash"/><category scheme="http://www.blogger.com/atom/ns#" term="interface"/><category scheme="http://www.blogger.com/atom/ns#" term="ui"/><category scheme="http://www.blogger.com/atom/ns#" term="user"/><title type='text'>Hitsmash: ui (part 5)</title><content type='html'>I finally finished the user interface for Hitsmash.&lt;br /&gt;It&#39;s very simple: a title, two boxes , two videos, two buttons and one footer. The hard part runs in the background.&lt;br /&gt;&lt;br /&gt;When you visit the homepage, two random hits are extracted from the database and new fight entry is added to the &lt;span style=&quot;font-style:italic;&quot;&gt;fights&lt;/span&gt; table; finally the page described above is shown to the user.&lt;br /&gt;&lt;br /&gt;Once you make your decision and click one of the two &lt;span style=&quot;font-style:italic;&quot;&gt;Vote&lt;/span&gt; buttons, a bunch of operations are executed on the server side:&lt;br /&gt;* check if the fight identifier is present in the database;&lt;br /&gt;* update the rank values of the challenging hits using the Elo rating system [1];&lt;br /&gt;* remove both the current and old fights from the database in order to discard fake votes;&lt;br /&gt;&lt;br /&gt;The code I wrote is kind of a mess: I avoided either &lt;span style=&quot;font-style:italic;&quot;&gt;templating systems&lt;/span&gt; or &lt;span style=&quot;font-style:italic;&quot;&gt;object-oriented database management systems&lt;/span&gt; in order to keep things as simple as possible.&lt;br /&gt;I preferred to concentrate on the logic rather than on code aesthetics.&lt;br /&gt;&lt;br /&gt;Here is a preview of the site: SLAM!&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TOu-tvp6IdI/AAAAAAAAAKg/ZU8RhJgozx8/s1600/hitsmash.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 182px;&quot; src=&quot;http://2.bp.blogspot.com/_DsTRtFoKWX4/TOu-tvp6IdI/AAAAAAAAAKg/ZU8RhJgozx8/s320/hitsmash.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5542733459505160658&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;[1] http://en.wikipedia.org/wiki/Elo_rating_system</content><link rel='replies' type='application/atom+xml' href='http://matteolandi.blogspot.com/feeds/7608004911511641470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://matteolandi.blogspot.com/2010/11/hitsmash-ui-part-5.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/7608004911511641470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3860519759344647288/posts/default/7608004911511641470'/><link rel='alternate' type='text/html' href='http://matteolandi.blogspot.com/2010/11/hitsmash-ui-part-5.html' title='Hitsmash: ui (part 5)'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_DsTRtFoKWX4/TOu-tvp6IdI/AAAAAAAAAKg/ZU8RhJgozx8/s72-c/hitsmash.png" height="72" width="72"/><thr:total>0</thr:total></entry></feed>