<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Bluerail Weblog</title>
    <link>http://www.bluerail.nl/weblog/</link>
    <atom:link href="http://www.bluerail.nl/rss.xml" rel="self" type="application/rss+xml" />
    <description>Bluerail Weblog</description>
    <language>nl-nl</language>
    <pubDate>Fri, 20 Jan 2012</pubDate>
    <lastBuildDate>Fri, 20 Jan 2012 20:04:52 CET</lastBuildDate>

    
    <item>
      <title>Beheer van ontwikkelomgeving met Foreman</title>
      <link>http://www.bluerail.nl/weblog/2012/01/20/Beheer-van-ontwikkelomgeving-met-Foreman.html</link>
      <pubDate>Fri, 20 Jan 2012 00:00:00 CET</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2012/01/20/Beheer-van-ontwikkelomgeving-met-Foreman</guid>
      <description>&lt;p&gt;De ontwikkeling van een applicatie vereist tegenwoordig meer dan enkel een teksteditor en een &lt;em&gt;rails server&lt;/em&gt;. Door het gebruik van bijvoorbeeld verschillende databases, achtergrondtaken of afhandeling van e-mail is het noodzakelijk bij de ontwikkeling de beschikking te hebben over software die hiertoe in staat is.&lt;/p&gt;

&lt;p&gt;Nu is het hebben van deze software enkel een preconditie die vastgelegd kan worden bij het project en daarmee ook niet echt een probleem. Wanneer verschillende projecten verschillende verzamelingen software vereisen ontstaat de behoefte om de ontwikkelaar hierin te ondersteunen. De Rubygem &lt;a href='http://rubygems.org/gems/foreman'&gt;Foreman&lt;/a&gt; komt hierbij bijzonder goed van pas!&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id='procfile'&gt;Procfile&lt;/h2&gt;

&lt;p&gt;De Foreman gem heeft als insteek het starten van de applicatie eenvoudiger te maken door de precondities op het gebied van software die gestart moet worden vast te leggen in een &lt;code&gt;Procfile&lt;/code&gt;. Dit bestand bevat enkel een naam en een uitvoerregel.&lt;/p&gt;

&lt;p&gt;Een Rails project dat gebruik maakt van &lt;a href='http://www.mongodb.org/'&gt;mongoDB&lt;/a&gt; voor opslag van gegevens, &lt;a href='http://redis.io/'&gt;Redis&lt;/a&gt; voor snelle toegang tot key-value opslag, &lt;a href='https://github.com/defunkt/resque'&gt;Resque&lt;/a&gt; voor de uitvoer taken in de achtergrond, &lt;a href='https://github.com/bvandenbos/resque-scheduler'&gt;Resque scheduler&lt;/a&gt; voor het periodiek starten van taken en &lt;a href='https://github.com/bluerail/post_office'&gt;Post Office&lt;/a&gt; voor het beschikbaar stellen van een mailserver in de ontwikkelomgeving heeft een volgend &lt;code&gt;Procfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;web:            bundle &lt;span class='nb'&gt;exec &lt;/span&gt;unicorn
mongodb:        mongod --quiet --dbpath&lt;span class='o'&gt;=&lt;/span&gt;db/mongo/
redis:          redis-server /opt/local/etc/redis.conf
worker:         bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake environment resque:work &lt;span class='nv'&gt;QUEUE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;*
scheduler:      bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake resque:scheduler &lt;span class='nv'&gt;QUEUE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;*
post_office:    post_office --smtp 10025 --pop3 10110
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='foreman'&gt;Foreman&lt;/h2&gt;

&lt;p&gt;Normaal gesproken zou het een hele klus zijn om alle software te starten om zo aan ontwikkeling te beginnen. Met Foreman kunnen we aan de slag na het uitvoeren van één commando:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;foreman start
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Vervolgens worden de verschillende onderdelen van &lt;code&gt;Procfile&lt;/code&gt; gestart en wordt het resultaat daarvan weergegeven:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;my_project git:&lt;span class='o'&gt;(&lt;/span&gt;master&lt;span class='o'&gt;)&lt;/span&gt; foreman start
10:39:42 web.1          | started with pid 5873
10:39:42 mongodb.1      | started with pid 5874
10:39:42 redis.1        | started with pid 5877
10:39:42 worker.1       | started with pid 5878
10:39:42 scheduler.1    | started with pid 5881
10:39:42 post_office.1  | started with pid 5883
10:39:42 mongodb.1      | Fri Jan 20 10:39:42 &lt;span class='o'&gt;[&lt;/span&gt;initandlisten&lt;span class='o'&gt;]&lt;/span&gt; MongoDB starting : &lt;span class='nv'&gt;pid&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;5874 &lt;span class='nv'&gt;port&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;27017 &lt;span class='nv'&gt;dbpath&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;db/mongo/
10:39:42 web.1          | I, &lt;span class='o'&gt;[&lt;/span&gt;2012-01-20T10:39:46.316708 &lt;span class='c'&gt;#5873]  INFO -- : listening on addr=0.0.0.0:8080 fd=12&lt;/span&gt;
10:39:42 web.1          | I, &lt;span class='o'&gt;[&lt;/span&gt;2012-01-20T10:39:46.317231 &lt;span class='c'&gt;#5873]  INFO -- : worker=0 spawning...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='afhankelijkheden'&gt;Afhankelijkheden&lt;/h2&gt;

&lt;p&gt;Het is mogelijk dat bepaalde software afhankelijk is van andere. Zo zal in bovenstaand voorbeeld Resque niet werken zonder Redis. Foreman biedt op dit moment nog geen ondersteuning aan om aan te geven welke processen van elkaar afhankelijk zijn.&lt;/p&gt;

&lt;p&gt;Een door ons &lt;a href='https://github.com/ddollar/foreman/issues/133'&gt;voorgesteld concept&lt;/a&gt; voegt deze ondersteuning toe door in de &lt;code&gt;Procfile&lt;/code&gt; gebruik te maken van het sluisteken. De onderdelen uit het eerdere voorbeeld worden dan beschreven als:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;web:            bundle &lt;span class='nb'&gt;exec &lt;/span&gt;unicorn
mongodb:        mongod --quiet --dbpath&lt;span class='o'&gt;=&lt;/span&gt;db/mongo/
redis:          redis-server /opt/local/etc/redis.conf
| worker:       bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake environment resque:work &lt;span class='nv'&gt;QUEUE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;*
| scheduler:    bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake resque:scheduler &lt;span class='nv'&gt;QUEUE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;*
post_office:    post_office --smtp 10025 --pop3 10110
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;De onderdelen die afhankelijk zijn van Redis worden op deze manier pas na enkele seconden gestart. Een versie van Foreman met ondersteuning voor dit concept is te downloaden vanaf &lt;a href='https://github.com/rvanlieshout/foreman'&gt;Github&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>E-mail spamfilters in prijs verlaagd</title>
      <link>http://www.bluerail.nl/weblog/2011/11/25/Spamfilters_in_prijs_verlaagd.html</link>
      <pubDate>Fri, 25 Nov 2011 00:00:00 CET</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/11/25/Spamfilters_in_prijs_verlaagd</guid>
      <description>&lt;p&gt;Sinds enige tijd bieden wij de uitstekende spam filters van Spam Experts aan. Bluerail beschikt over een eigen filtercluster wat onder andere scant op ongewenste mail en virussen. De gefilterde mail wordt vervolgens in uw in uw Bluerail hostingaccount afgeleverd, of bijvoorbeeld op uw eigen Exchange server op kantoor. U hoeft hoe dan ook zelf niets in te stellen en geen software te installeren.&lt;/p&gt;

&lt;p&gt;Door het grote succes van deze spamfilters hebben wij de prijs kunnen verlagen van 40 euro naar &lt;strong&gt;slechts 30 euro per jaar&lt;/strong&gt;. Voor bestaande gebruikers wordt bij de volgende verlenging automatisch de nieuwe prijs berekend.&lt;/p&gt;
&lt;div class='large' style='line-height: 1.5;'&gt;Wilt u het spamfilter gratis
uitproberen? &lt;a href='http://twitter.com/BluerailNL'&gt;Volg ons op Twitter&lt;/a&gt;,
stuur ons een tweet en u krijg een maand vrijblijvend een spamfilter op proef
bij uw Bluerail hosting pakket of VPS!&lt;/div&gt;&lt;!--more--&gt;&lt;p style='text-align:center;'&gt;&lt;a href='http://spamexperts.com/'&gt;&lt;img src='/images/blog/spamexperts-logo.png' alt='Spam Experts' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id='doeltreffend_filter'&gt;Doeltreffend filter&lt;/h2&gt;

&lt;p&gt;Alle SpamExperts technologieën zijn uniek, intern ontwikkeld en vormen een uitstekend samengaand geheel om de meest optimale filterresultaten te behalen. De Incoming Email Security Firewall combineert meer dan 50 verschillende filtermechanismen die voortdurend geüpdate, gemonitord en verfijnd worden om u altijd van de doeltreffende resultaten te kunnen verzekeren.&lt;/p&gt;

&lt;p&gt;De filtering is grofweg verdeeld in twee fases. Fase 1, de SMTP-fase, analyseert het gedrag en de reputatie van de zendende server. Ongeveer 95% van alle spam kan al in deze fase opgespoord worden. In fase 2, de inhoudsfase, wordt de werkelijke inhoud van het bericht gecontroleerd en wordt de overige 5% van de spam berichten opgespoord. De twee fases samen berekenen een spam-score plausibiliteit. Op deze persoonlijke score worden berichten geaccepteerd, geweigerd of in quarantaine geplaatst.&lt;/p&gt;

&lt;h2 id='overzichtelijk_control_panel'&gt;Overzichtelijk control panel&lt;/h2&gt;

&lt;p&gt;U krijgt zelf toegang tot een overzichtelijk control panel waar u de instellingen van het spamfilter zelf kunt aanpassen en bovendien direct de e-mail quarantaine kunt inzien. Hieronder ziet u enkele screenshots van het paneel voor de domeinbeheerder. U kunt ook individuele gebruikers toegang geven tot hun eigen deel van het paneel, zij zien dan minder opties.&lt;/p&gt;

&lt;h3 id='dashboard'&gt;Dashboard&lt;/h3&gt;
&lt;p&gt;&lt;a href='/images/blog/spamexperts/domainadmin_dashboard.jpeg'&gt;&lt;img src='/images/blog/spamexperts/domainadmin_dashboard.jpeg' alt='Spam Experts dashboard' width='100%' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id='logbestanden_doorzoeken'&gt;Logbestanden doorzoeken&lt;/h3&gt;
&lt;p&gt;&lt;a href='/images/blog/spamexperts/domainadmin_logsearch.jpeg'&gt;&lt;img src='/images/blog/spamexperts/domainadmin_logsearch.jpeg' alt='Spam Experts log search' width='100%' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id='whitelist_van_afzenders'&gt;&amp;#8216;Whitelist&amp;#8217; van afzenders&lt;/h3&gt;
&lt;p&gt;&lt;a href='/images/blog/spamexperts/domainadmin_senderwhitelist.jpeg'&gt;&lt;img src='/images/blog/spamexperts/domainadmin_senderwhitelist.jpeg' alt='Spam Experts sender whitelist' width='100%' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id='statistieken'&gt;Statistieken&lt;/h3&gt;
&lt;p&gt;&lt;a href='/images/blog/spamexperts/domainadmin_statistics.jpeg'&gt;&lt;img src='/images/blog/spamexperts/domainadmin_statistics.jpeg' alt='Spam Experts statistics' width='100%' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id='ideaal_voor_organisaties_met_een_eigen_mailserver'&gt;Ideaal voor organisaties met een eigen mailserver&lt;/h2&gt;

&lt;p&gt;Bedrijven met een mailserver op hun eigen locatie lopen soms tegen problemen aan omdat providers dit niet toelaten. Bovendien kan er bij storingen in de verbinding geen mail afgeleverd worden. Ons spamfilter kan hierbij een uitkomst bieden. Alle mail van buitenaf wordt op ons filter aangenomen en bewaard. Het filter zal vervolgens proberen om de mail op uw server af te leveren. Dit kan ook op een andere poort dan de standaard poort 25 om beperkingen van providers te omzeilen. Indien uw server tijdelijk niet bereikbaar is wordt de mail op onze filter servers bewaard. Zo heeft u dus het gemak van een lokale mailserver, zonder bereikbaarheidsproblemen.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Applicatieversies met git describe</title>
      <link>http://www.bluerail.nl/weblog/2011/11/22/Applicatieversies_met_git_describe.html</link>
      <pubDate>Tue, 22 Nov 2011 00:00:00 CET</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/11/22/Applicatieversies_met_git_describe</guid>
      <description>&lt;p&gt;Het gebruik van versienummers in Ruby on Rails applicaties is niet erg wijd verspreid. Doordat er van veel applicaties in feite maar een versie van belang is (de versie die op dat moment on-line is) wordt er geen melding gemaakt van een specifieke versie. Desondanks kan het toch nuttig zijn om de releases van de applicatie te nummeren. Dit geeft een handvat om wijzigingen naar de gebruikers te communiceren en bovendien geeft het de gebruikers het gevoel dat de applicatie actief ontwikkeld wordt.&lt;/p&gt;

&lt;p&gt;In deze post laten we zien hoe met behulp van &lt;em&gt;git describe&lt;/em&gt; en enkele regels code zeer eenvoudig versienummers aan een Rails applicatie kunnen worden toegevoegd.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id='git_describe_zoek_een_tag_bij_een_commit'&gt;Git describe: zoek een tag bij een commit&lt;/h2&gt;

&lt;p&gt;Ons belangrijkste hulpmiddel voor deze post is &lt;em&gt;git describe&lt;/em&gt;. We zullen de werking demonstreren aan de hand van een voorbeeld.&lt;/p&gt;

&lt;p&gt;De eerste stap is het opzetten van een repository in een lege directory:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;git init .
    Initialized empty Git repository in /Users/martijn/Developer/versies/.git/
    &lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Dit is nog geen versie&amp;quot;&lt;/span&gt; &amp;gt; README 
    &lt;span class='nv'&gt;$ &lt;/span&gt;git add README 
    &lt;span class='nv'&gt;$ &lt;/span&gt;git commit -m &lt;span class='s2'&gt;&amp;quot;Eerste commit&amp;quot;&lt;/span&gt;
    &lt;span class='o'&gt;[&lt;/span&gt;master &lt;span class='o'&gt;(&lt;/span&gt;root-commit&lt;span class='o'&gt;)&lt;/span&gt; f828a77&lt;span class='o'&gt;]&lt;/span&gt; Eerste commit
     1 files changed, 1 insertions&lt;span class='o'&gt;(&lt;/span&gt;+&lt;span class='o'&gt;)&lt;/span&gt;, 0 deletions&lt;span class='o'&gt;(&lt;/span&gt;-&lt;span class='o'&gt;)&lt;/span&gt;
     create mode 100644 README
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;De eerste commit heeft als hash f828a77. Als we nu git describe uitvoeren op dit repository geeft deze die hash terug:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;git describe --always
    f828a77
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We gebruiken hier de &amp;#8211;always optie zodat git een hash gebruikt als beschrijving indien hij geen tag kan vinden.&lt;/p&gt;

&lt;p&gt;Op het moment dat een versie klaar is om uit te brengen kunnen we een tag aanmaken voor deze versie. In de &lt;a href='http://semver.org'&gt;Semantic Versioning&lt;/a&gt; richtlijnen wordt aanbevolen om versie tags te laten beginnen met een letter v. Tevens wordt het gebruik van &lt;em&gt;annotated tags&lt;/em&gt; in Git over het algemeen aanbevolen. Voor ons voorbeeld is dit ook van belang. We committen en taggen de update daarom als volgt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Versie 0.1.0&amp;quot;&lt;/span&gt; &amp;gt; README ; git add README ; git commit -m &lt;span class='s2'&gt;&amp;quot;Versie update&amp;quot;&lt;/span&gt;
    &lt;span class='o'&gt;[&lt;/span&gt;master 633b8a0&lt;span class='o'&gt;]&lt;/span&gt; Versie update
     1 files changed, 1 insertions&lt;span class='o'&gt;(&lt;/span&gt;+&lt;span class='o'&gt;)&lt;/span&gt;, 1 deletions&lt;span class='o'&gt;(&lt;/span&gt;-&lt;span class='o'&gt;)&lt;/span&gt;
    &lt;span class='nv'&gt;$ &lt;/span&gt;git tag -a &lt;span class='s2'&gt;&amp;quot;v0.1.0&amp;quot;&lt;/span&gt; -m &lt;span class='s2'&gt;&amp;quot;Versie 0.1.0&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Als we nu git describe uitvoeren zien we dat onze tag als omschrijving wordt gebruikt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;git describe
    v0.1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Dit gegeven zouden we dus al kunnen gebruiken om de huidige versie weer te geven in onze applicatie. Daarover straks meer. Wat gebeurt er nu als we een commit maken na deze tag?&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Versie 0.1.1&amp;quot;&lt;/span&gt; &amp;gt; README ; git add README ; git commit -m &lt;span class='s2'&gt;&amp;quot;Versie update&amp;quot;&lt;/span&gt;
    &lt;span class='o'&gt;[&lt;/span&gt;master a85330d&lt;span class='o'&gt;]&lt;/span&gt; Versie update
     1 files changed, 1 insertions&lt;span class='o'&gt;(&lt;/span&gt;+&lt;span class='o'&gt;)&lt;/span&gt;, 1 deletions&lt;span class='o'&gt;(&lt;/span&gt;-&lt;span class='o'&gt;)&lt;/span&gt;
    &lt;span class='nv'&gt;$ &lt;/span&gt;git describe
    v0.1.0-1-ga85330d
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wat deze omschrijving aangeeft is dat er een commit is geweest sinds de tag en dat onze HEAD momenteel op commit a85330d zit. Dit zou geen acceptabel versienummer zijn voor een productie release, dus hier zullen we straks bij het deployen op controleren.&lt;/p&gt;

&lt;p&gt;Om nu een geldige productie versie te maken zullen we nog even een v0.1.1 commit taggen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='nv'&gt;$ &lt;/span&gt;git tag -a &lt;span class='s2'&gt;&amp;quot;v0.1.1&amp;quot;&lt;/span&gt; -m &lt;span class='s2'&gt;&amp;quot;Versie 0.1.1&amp;quot;&lt;/span&gt;
    &lt;span class='nv'&gt;$ &lt;/span&gt;git describe
    v0.1.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In een Git client als &lt;a href='http://www.sourcetreeapp.com/'&gt;SourceTree&lt;/a&gt; zien we de tags netjes op een rij onder elkaar:&lt;/p&gt;
&lt;p&gt;&lt;img src='/images/blog/git-describe-sourcetree.png' alt='Versies in SourceTree' width='100%' /&gt;&lt;/p&gt;
&lt;h2 id='versie_tonen_in_de_rails_applicatie'&gt;Versie tonen in de Rails applicatie&lt;/h2&gt;

&lt;p&gt;Om de versie te kunnen tonen in de Rails applicaite stellen we deze vast tijdens het opstarten van de applicatie. Maak hiervoor een bestaand &lt;strong&gt;config/initializers/app_version.rb&lt;/strong&gt; aan met de volgende inhoud:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='no'&gt;APP_VERSION&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='sb'&gt;`git describe --always`&lt;/span&gt; &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;defined?&lt;/span&gt; &lt;span class='no'&gt;APP_VERSION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Vervolgens kun je op de gewenste plek, bijvoorbeeld in de layout eenvoudig de versie opvragen, bijvoorbeeld door dit op te nemen in de layout view:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;    &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='no'&gt;APP_VERSION&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='versie_controleren_voor_het_deployen_met_capistrano'&gt;Versie controleren voor het deployen met Capistrano&lt;/h2&gt;

&lt;p&gt;Om er nu voor te zorgen dat een productie release altijd een correct getagde versie heeft zullen we in de deploy.rb een controle opnemen die kijkt of er een tag is.&lt;/p&gt;

&lt;p&gt;Hier een voorbeeld stap voor een multistage setup, waarbij we voor elke stage een gelijknamige branch hebben:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:check_tag&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;desc&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='sb'&gt;`git describe &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;stage&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sb'&gt; --exact-match`&lt;/span&gt;
  &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;desc&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/^v(\d+)\.(\d+).(\d+)/&lt;/span&gt;
    &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;say&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class='no'&gt;EOM&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='sh'&gt;    The branch #{stage} does not have a version tag!&lt;/span&gt;

&lt;span class='sh'&gt;    Please tag the release to enable deployment. Last tags were:&lt;/span&gt;
&lt;span class='no'&gt;    EOM&lt;/span&gt;
    &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;say&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;    &amp;quot;&lt;/span&gt;&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='sb'&gt;`git tag -l | head -5`&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;lines&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_a&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;gsub&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;exit&lt;/span&gt;
  &lt;span class='k'&gt;else&lt;/span&gt;
    &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;say&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;    Releasing &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;desc&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;deploy&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;check_tag&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Er wordt een branch en een optie &amp;#8211;exact-match weergegeven. Deze optie zorgt ervoor dat er alleen een resultaat wordt gegeven als er een exacte tag voor de versie bestaat. Als de laatste commit in de branch niet getagged is komt er dus geen resultaat.&lt;/p&gt;

&lt;p&gt;Met een regex controleren we of er een tag is terug gegeven met een geldig formaat. Zo niet, dan krijgt de gebruiker een waarschuwing. Voor de volledigheid geven we de laatste vijf tags weer zodat de gebruiker de volgende versie kan bepalen. Een script om automatisch de versie op te hogen zou hier een mooie toevoeging zijn.&lt;/p&gt;

&lt;p&gt;Indien er wel een geldige tag is gevonden geeft het script hier netjes een melding van. Hierna zal in de productieapplicatie netjes de getagde versie weergegeven worden.&lt;/p&gt;

&lt;p&gt;Door de versienummering te centraliseren in Git is beheer van versies eenvoudig en overzichtelijk. Met &lt;a href='http://semver.org'&gt;Semantic Versioning&lt;/a&gt; krijgen de versienummers een duidelijke mening voor de buitenwereld. Het bijhouden van versienummers lijkt niet overal nuttig, maar wellicht geeft het uw applicatie net dat stukje extra uitstraling.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Includes versus Joins: een vuistregel</title>
      <link>http://www.bluerail.nl/weblog/2011/11/14/Includes_vs_Joins_een_vuistregel.html</link>
      <pubDate>Mon, 14 Nov 2011 00:00:00 CET</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/11/14/Includes_vs_Joins_een_vuistregel</guid>
      <description>&lt;p&gt;In Ruby on Rails applicaties is het gebruik van relaties tussen objecten een vrij algemeen begrip. Een minder bekend gedeelte is de techniek die hier achter schuil gaat: het gebruik van een LEFT OUTER JOIN in de database en het gewicht van een dergelijke operatie. Door een keuze te maken tussen de includes en joins methode van ActiveRecord kun je de gebruikte join beïnvloeden. In dit artikel zullen we de gevolgen van deze operators demonstreren met een testcase en geven we een vuistregel die helpt bij het kiezen tussen includes en joins.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;In SQL zijn de meest gangbare manieren van het koppelen van tabellen de LEFT OUTER JOIN en de INNER JOIN. Het belangrijkste verschil tussen de twee is dat bij een OUTER JOIN het niet verplicht is dat een overeenkomend record tussen de gekoppelde tabellen aanwezig is.&lt;/p&gt;

&lt;h2 id='scenario'&gt;Scenario&lt;/h2&gt;

&lt;p&gt;Het verschil tussen een LEFT OUTER en INNER join is het beste te demonstreren met een testcase. Laten we uitgaan van een omgeving met studenten (Student), klassen (Classroom) en leraren (Teacher). Elke student heeft over een aantal jaar ingeschreven gestaan in een aantal klassen (Enrollment) en een klas wordt geleid door meerdere leraren (ClassroomTeacher).&lt;/p&gt;

&lt;p&gt;In Ruby on Rails ziet onze applicatie er als volgt uit:&lt;/p&gt;

&lt;h3 id='student'&gt;Student&lt;/h3&gt;
&lt;img class='featured-image' src='/images/blog/includes-joins-modellen.png' alt='Datamodel voor dit voorbeeld' /&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Student&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:enrollments&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:classrooms&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:through&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:enrollments&lt;/span&gt;
  
  &lt;span class='n'&gt;validates_presence_of&lt;/span&gt; &lt;span class='ss'&gt;:name&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='enrollment'&gt;Enrollment&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Enrollment&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:student&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:classroom&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='classroom'&gt;Classroom&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Classroom&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:classroom_teachers&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:teachers&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:through&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:classroom_teachers&lt;/span&gt;

  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:enrollments&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:students&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:through&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:enrollments&lt;/span&gt;

  &lt;span class='n'&gt;validates_presence_of&lt;/span&gt; &lt;span class='ss'&gt;:year&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='classroomteacher'&gt;ClassroomTeacher&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;ClassroomTeacher&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:classroom&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:teacher&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='teacher'&gt;Teacher&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Teacher&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:classroom_teachers&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:classrooms&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:through&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:classroom_teachers&lt;/span&gt;

  &lt;span class='n'&gt;validates_presence_of&lt;/span&gt; &lt;span class='ss'&gt;:name&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Om de applicatie wat context te geven hebben we hem gevuld door gebruik te maken van de gem &lt;a href='https://github.com/bluerail/randomizer'&gt;factory_randomizer&lt;/a&gt;. Deze gem stelt ons in staat een database te vullen met willekeurige data.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='mi'&gt;4000&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='no'&gt;Classroom&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:year&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Randomizer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;number&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:min&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;1980&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:max&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;2010&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='mi'&gt;250&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; 
  &lt;span class='n'&gt;t&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Teacher&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Randomizer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;full_name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;classrooms&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;Classroom&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;rand()&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='mi'&gt;100000&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;s&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Student&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Randomizer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;full_name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;classrooms&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;Classroom&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;rand()&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We hebben nu een applicatie met 100.000 studenten die elk in 5 van de 4000 willekeurige klassen ingeschreven zijn geweest. Elk van deze klassen is geleid door een tweetal leraren.&lt;/p&gt;

&lt;h2 id='testcase'&gt;Testcase&lt;/h2&gt;

&lt;p&gt;Om het verschil tussen een LEFT OUTER en INNER join duidelijk te maken zoeken we naar alle studenten die in 1989 of 1995 les hebben gehad van Monica Slater of Philip Rhodes. Aangezien Rails 3.1 slim genoeg is om pas de SQL uit te voeren op het moment dat de verzameling gebruikt wordt vragen we van elke student de naam op.&lt;/p&gt;

&lt;p&gt;In Ruby on Rails is dit geplaatst in een Rake taak:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;timer&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:fetch_students&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:environment&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;years&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1989&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1995&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='n'&gt;teachers&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Monica Slater&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Philip Rhodes&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

  &lt;span class='n'&gt;total_duration_includes&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;
  &lt;span class='n'&gt;total_duration_joins&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;
  &lt;span class='n'&gt;total_runs&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;100&lt;/span&gt;
  &lt;span class='n'&gt;ignore_runs&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;

  &lt;span class='n'&gt;total_runs&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;index&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='n'&gt;duration_includes&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Timer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;time&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='c1'&gt;# .includes = LEFT OUTER JOIN&lt;/span&gt;
      &lt;span class='no'&gt;Student&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;includes&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:enrollments&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:classroom&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:teachers&lt;/span&gt; &lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:classrooms&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:year&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;years&lt;/span&gt; &lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:teachers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;teachers&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;all&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
    
    &lt;span class='n'&gt;duration_joins&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Timer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;time&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='c1'&gt;# .joins = INNER JOIN&lt;/span&gt;
      &lt;span class='no'&gt;Student&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;joins&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:enrollments&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:classroom&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:teachers&lt;/span&gt; &lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:classrooms&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:year&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;years&lt;/span&gt; &lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:teachers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;teachers&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;
              &lt;span class='n'&gt;all&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
    
    &lt;span class='c1'&gt;# allow the database to &amp;#39;warm up&amp;#39;&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;index&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;ignore_runs&lt;/span&gt;
      &lt;span class='n'&gt;total_duration_includes&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='n'&gt;duration_includes&lt;/span&gt;
      &lt;span class='n'&gt;total_duration_joins&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='n'&gt;duration_joins&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  
  &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Average: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;total_runs&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;ignore_runs&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; runs&amp;quot;&lt;/span&gt;
  &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Includes: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;sprintf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;%.2f&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;total_duration_includes&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
  &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Joins: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;sprintf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;%.2f&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;total_duration_joins&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
  &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Remaining load: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;sprintf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;%.2f&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;100&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='n'&gt;total_duration_includes&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;total_duration_joins&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; %&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;De taak zal 100 maal de studenten op verschillende manieren ophalen. De eerste 10 pogingen worden buiten beschouwing gelaten, om de database server de kans te geven de gegevens in de cache op te slaan.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;rake fetch_students
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Average: 90 runs
Includes: 0.31
Joins: 0.17
Remaining load: 54.44 %&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id='40_winst'&gt;40% winst&lt;/h2&gt;

&lt;p&gt;In deze simpele test behalen we door enkel het woord &amp;#8216;includes&amp;#8217; te vervangen door &amp;#8216;joins&amp;#8217; meer dan 40% snelheidswinst. Dit verschil kan zelfs nog groter worden als:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;De database wordt actief gebruikt (waardoor tabellen / records regelmatig in geblokkeerd worden door andere transacties)&lt;/li&gt;

&lt;li&gt;De gebruikte gegevens meer dan alleen een id en een naam zijn&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='wanneer_toepassen'&gt;Wanneer toepassen?&lt;/h2&gt;

&lt;p&gt;Er is een groot verschil tussen het resultaat van een LEFT OUTER en INNER JOIN. Het belangrijkste verschil: De relatie hoeft bij een LEFT OUTER JOIN &lt;strong&gt;niet&lt;/strong&gt; te bestaan, waar deze bij een INNER JOIN verplicht is. Onderstaand voorbeeld laat zien dat bij een kleine tabel met boeken met daaraan de auteurs gekoppeld. Merk op dat er geen auteur is gekoppeld aan boek 3 &amp;#8216;The Magic Of Reality&amp;#8217;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='n'&gt;mysql&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='k'&gt;FROM&lt;/span&gt; &lt;span class='n'&gt;books&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;                  &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;author_id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;A&lt;/span&gt; &lt;span class='n'&gt;Dance&lt;/span&gt; &lt;span class='k'&gt;with&lt;/span&gt; &lt;span class='n'&gt;Dragons&lt;/span&gt;  &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;The&lt;/span&gt; &lt;span class='n'&gt;Affair&lt;/span&gt;            &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;The&lt;/span&gt; &lt;span class='n'&gt;Magic&lt;/span&gt; &lt;span class='k'&gt;Of&lt;/span&gt; &lt;span class='n'&gt;Reality&lt;/span&gt;  &lt;span class='o'&gt;|&lt;/span&gt;      &lt;span class='k'&gt;NULL&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+&lt;/span&gt;
&lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='k'&gt;rows&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='k'&gt;set&lt;/span&gt;

&lt;span class='n'&gt;mysql&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='k'&gt;FROM&lt;/span&gt; &lt;span class='n'&gt;authors&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;               &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;George&lt;/span&gt; &lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt; &lt;span class='n'&gt;Martin&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;Lee&lt;/span&gt; &lt;span class='n'&gt;Child&lt;/span&gt;          &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+--------------------+&lt;/span&gt;
&lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='k'&gt;rows&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='k'&gt;set&lt;/span&gt;

&lt;span class='n'&gt;mysql&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='k'&gt;FROM&lt;/span&gt; &lt;span class='n'&gt;books&lt;/span&gt; &lt;span class='k'&gt;INNER&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;authors&lt;/span&gt; &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;authors&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;books&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;author_id&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+----------------------+-----------+----+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;                 &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;author_id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;               &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+----------------------+-----------+----+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;A&lt;/span&gt; &lt;span class='n'&gt;Dance&lt;/span&gt; &lt;span class='k'&gt;with&lt;/span&gt; &lt;span class='n'&gt;Dragons&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;George&lt;/span&gt; &lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt; &lt;span class='n'&gt;Martin&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;The&lt;/span&gt; &lt;span class='n'&gt;Affair&lt;/span&gt;           &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;Lee&lt;/span&gt; &lt;span class='n'&gt;Child&lt;/span&gt;          &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+----------------------+-----------+----+--------------------+&lt;/span&gt;
&lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='k'&gt;rows&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='k'&gt;set&lt;/span&gt;

&lt;span class='n'&gt;mysql&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='k'&gt;FROM&lt;/span&gt; &lt;span class='n'&gt;books&lt;/span&gt; &lt;span class='k'&gt;LEFT&lt;/span&gt; &lt;span class='k'&gt;OUTER&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;authors&lt;/span&gt; &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;authors&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;books&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;author_id&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+------+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;                  &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;author_id&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;id&lt;/span&gt;   &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;name&lt;/span&gt;               &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+------+--------------------+&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;A&lt;/span&gt; &lt;span class='n'&gt;Dance&lt;/span&gt; &lt;span class='k'&gt;with&lt;/span&gt; &lt;span class='n'&gt;Dragons&lt;/span&gt;  &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;    &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;George&lt;/span&gt; &lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;R&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt; &lt;span class='n'&gt;Martin&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;The&lt;/span&gt; &lt;span class='n'&gt;Affair&lt;/span&gt;            &lt;span class='o'&gt;|&lt;/span&gt;         &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;    &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;Lee&lt;/span&gt; &lt;span class='n'&gt;Child&lt;/span&gt;          &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;|&lt;/span&gt;  &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;The&lt;/span&gt; &lt;span class='n'&gt;Magic&lt;/span&gt; &lt;span class='k'&gt;Of&lt;/span&gt; &lt;span class='n'&gt;Reality&lt;/span&gt;  &lt;span class='o'&gt;|&lt;/span&gt;      &lt;span class='k'&gt;NULL&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='k'&gt;NULL&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='k'&gt;NULL&lt;/span&gt;               &lt;span class='o'&gt;|&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='c1'&gt;----+-----------------------+-----------+------+--------------------+&lt;/span&gt;
&lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='k'&gt;rows&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='k'&gt;set&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Beide queries vragen om alle boeken met daaraan gekoppeld de auteurs. De query met de INNER JOIN stelt alleen de verplichting dat er een auteur is. Deze methode kan dus alleen gebruikt worden als met 100% zekerheid is te zeggen dat de relatie aanwezig moet zijn. Het is uiteraard wel mogelijk om LEFT OUTER en INNER JOINS te combineren.&lt;/p&gt;

&lt;p&gt;Een vuistregel om te onthouden:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gebruik een .joins als de condities bij het ophalen de relatie verplicht maken.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Bluerail sponsort Music BOXtel</title>
      <link>http://www.bluerail.nl/weblog/2011/11/07/Bluerail_sponsort_Music_BOXtel.html</link>
      <pubDate>Mon, 07 Nov 2011 00:00:00 CET</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/11/07/Bluerail_sponsort_Music_BOXtel</guid>
      <description>&lt;img class='featured-image' src='/images/blog/musicboxtel.jpg' alt='Music BOXtel' /&gt;
&lt;p&gt;Deze week is in het Brabantse Boxtel het project Music BOXtel van MiK Kunsteducatie van start gegaan. Dit is een uniek project wat deel uitmaakt van het landelijke project &lt;em&gt;Kinderen maken Muziek&lt;/em&gt;, geïnitieerd door Prinses Maxima op haar 40e verjaardag. In dit project leren 130 kinderen van twee lokale bassischolen een instrument te spelen. Het wordt aan het eind van dit schooljaar besloten met een landelijke einduitvoering.&lt;/p&gt;

&lt;p&gt;Bluerail is trots om een bijdrage te kunnen leveren aan dit mooie project dat mede mogelijk wordt gemaakt door het Oranje Fonds. Wij verzorgden voor Music BOXtel een op Refinery CMS gebaseerde website en de bijbehorende hosting.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Enkele foto&amp;#8217;s van de opening:&lt;/p&gt;
&lt;img src='/images/blog/MIK_Music_Boxtel_03.jpeg' /&gt;&lt;img src='/images/blog/MIK_Music_Boxtel_07.jpeg' /&gt;&lt;img src='/images/blog/MIK_Music_Boxtel_19.jpeg' /&gt;
&lt;p&gt;Een compleet verslag van de opening en meer informatie over dit project is te vinden op &lt;a href='http://www.musicboxtel.nl/'&gt;de webiste van Music BOXtel&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Hudson Continuous Integration testing met Rails 3 en RVM</title>
      <link>http://www.bluerail.nl/weblog/2011/10/21/Hudson-Continuous-Integration-testing-met-Rails-3-en-RVM.html</link>
      <pubDate>Fri, 21 Oct 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/10/21/Hudson-Continuous-Integration-testing-met-Rails-3-en-RVM</guid>
      <description>&lt;p&gt;In de Rails wereld zijn het gebruik van versiebeheer en het schrijven van tests of specs gemeengoed. Een Continuous Integration server is een nuttige toevoeging aan je bestaande ontwikkelproces. Deze server houdt het centrale repository in de gaten en zodra er een wijzing is voert de CI server de specs uit. Indien de specs falen wordt er een mail gestuurd aan de aangewezen beheerder en eventueel aan degene die de verantwoordelijke commit heeft gemaakt. In deze post geven we kort een up-to-date uitleg over het configureren van de Hudson CI server voor Ruby on Rails.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;a href='http://hudson-ci.org/'&gt;Hudson&lt;/a&gt; is een volwassen CI server die zijn oorsprong vindt in de Java wereld. Er zijn ook Ruby-specifieke CI servers, maar Hudson kwam op ons over als de meest volwassen oplossing. Bovendien heeft het standaard support voor Ruby, Rails en Git, dus het sluit perfect aan bij onze werkwijze. Bovendien is Hudson zeer eenvoudig te installeren. Op de Hudson wiki staan installatieinstructies voor verschillende besuringssystemen: &lt;a href='http://wiki.hudson-ci.org/display/HUDSON/Installing+Hudson'&gt;Installing Hudson&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Op onze CentOS server was de installatie zo gepiept met de officiële RPM:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;rpm -ivh http://hudson-ci.org/downloads/redhat/hudson-2.1.2-1.1.noarch.rpm
/sbin/service hudson start
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Hudson start dankzij zijn ingebouwde servlet container op poort 8080 en is na enkele minuten klaar voor gebruik. Elk project is in Hudson een losse &lt;em&gt;job&lt;/em&gt;. Laten we gelijk een job aanmaken voor ons &amp;#8216;Depot&amp;#8217; project:&lt;/p&gt;
&lt;img src='/images/blog/hudson1.png' alt='Hudson: New Job' width='100%' /&gt;
&lt;p&gt;Een Free-style project biedt volledige vrijheid in de configuratie van het build proces. Na het aamaken komen we direct in de configuratie terecht. De eerste stap, de configuratie van SCM, wijst zichtzelf:&lt;/p&gt;
&lt;img src='/images/blog/hudson2.png' alt='Hudson: SCM configuratie' width='100%' /&gt;
&lt;p&gt;Onder &amp;#8216;Build triggers&amp;#8217; stellen we in dat Hudson elke 5 minuten ons repository nazoekt op wijzigingen. De interval wordt opgegeven in crontab notatie. Vervolgens voegen we met de &amp;#8216;Add build step&amp;#8217; een &amp;#8216;Execute shell&amp;#8217; stap toe:&lt;/p&gt;
&lt;img src='/images/blog/hudson3.png' alt='Hudson: SCM polling en build step' width='100%' /&gt;
&lt;p&gt;Dit geeft ons een textvak waarin we ons build proces kunnen specificeren. Het onderstaande script is geschikt voor een Rails 3 applicatie op een server met een systeem-brede RVM installatie:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='c'&gt;#!/bin/bash -&lt;/span&gt;

&lt;span class='c'&gt;# Laad RVM&lt;/span&gt;
&lt;span class='nb'&gt;source&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;/usr/local/rvm/scripts/rvm&amp;quot;&lt;/span&gt;

&lt;span class='c'&gt;# Gebruik de juiste Ruby versie&lt;/span&gt;
rvm use 1.9.2

&lt;span class='c'&gt;# Installeer de gems uit de Gemfile&lt;/span&gt;
bundle install --deployment

&lt;span class='c'&gt;# Maak een database.yml aan&lt;/span&gt;
&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;development:&lt;/span&gt;
&lt;span class='s2'&gt;  adapter: mysql2&lt;/span&gt;
&lt;span class='s2'&gt;  encoding: utf8&lt;/span&gt;
&lt;span class='s2'&gt;  database: hudson_depot_dev&lt;/span&gt;
&lt;span class='s2'&gt;  username: hudson_depot_dev&lt;/span&gt;
&lt;span class='s2'&gt;  password: ...&lt;/span&gt;
&lt;span class='s2'&gt;  pool: 5&lt;/span&gt;
&lt;span class='s2'&gt;  timeout: 5000&lt;/span&gt;
&lt;span class='s2'&gt;test:&lt;/span&gt;
&lt;span class='s2'&gt;  adapter: mysql2&lt;/span&gt;
&lt;span class='s2'&gt;  encoding: utf8&lt;/span&gt;
&lt;span class='s2'&gt;  database: hudson_depot_test&lt;/span&gt;
&lt;span class='s2'&gt;  username: hudson_depot_test&lt;/span&gt;
&lt;span class='s2'&gt;  password: ...&lt;/span&gt;
&lt;span class='s2'&gt;  pool: 5&lt;/span&gt;
&lt;span class='s2'&gt;  timeout: 5000&amp;quot;&lt;/span&gt; &amp;gt; config/database.yml

&lt;span class='c'&gt;# Laad het database schema&lt;/span&gt;
bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake db:schema:load --trace

&lt;span class='c'&gt;# Voer de specs uit, met rcov&lt;/span&gt;
bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake spec:rcov --trace
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Er zullen ongetwijfeld aanpassingen nodig zijn voor je eigen applicatie, maar met dit voorbeeld zou je uit de voeten moeten kunnen. Voordat we op de &lt;em&gt;Build now&lt;/em&gt; knop kunnen duwen moeten we Hudson nog toegang geven tot het repository door een SSH key aan te maken en deze op de juiste plaats in de homedir van de hudson gebruiker te plaatsen. Mogelijk moet je bij de Hudson instellingen (_Manage Hudson_) nog even de juiste locatie van de git binary op je systeem aangeven. Daarna kun je met &lt;em&gt;Build now&lt;/em&gt; de eerste build uitvoeren. Via de console log kun je exact zien wat er gebeurt.&lt;/p&gt;

&lt;p&gt;Meer stappen zijn er niet nodig om je Rails applicatie automatisch te testen. Uiteraard is het niet de bedoeling dat er code gecommit wordt die tot falende specs leidt maar de praktijk leert dat het toch wel eens voorkomt. CI testing is een extra stok achter de deur die ervoor zorgt dat problemen snel opgemerkt en opgelost worden. Het hoofdscherm geeft een mooi overzicht van de status van de verschillende projecten:&lt;/p&gt;
&lt;img src='/images/blog/hudson5.png' alt='Hudson: SCM polling en build step' width='100%' /&gt;
&lt;p&gt;De weersverwachtingen geven de staat van het project aan. Een zonnetje betekent dat alle recente &amp;#8216;builds&amp;#8217; succesvol waren. Wolken, regen of onweer geven aan dat het minder goed gaat. De status van de laatste build zie je aan de gekleurde bol vooraan de regel. Blauw = gezonde build, Geel = build met problemen, Rood = mislukte build. Met de Console knop kun je de output van de laatste build bekijken en met de &amp;#8216;Schedule build&amp;#8217; knop kun je direct een nieuwe build starten.&lt;/p&gt;

&lt;p&gt;Het sturen van een e-mail bij problemen is natuurlijk leuk, maar dankzij de flexibiliteit van Hudson kunnen er ook direct maatregelen genomen worden, zoals we &lt;a href='http://www.papercut.com/blog/chris/2011/08/19/who-broke-the-build/'&gt;in deze blog post van Papercut zien&lt;/a&gt;:&lt;/p&gt;
&lt;iframe src='http://www.youtube.com/embed/1EGk2rvZe8A' allowfullscreen='allowfullscreen' frameborder='0' height='360' width='630'&gt;&lt;a href='http://www.youtube.com/watch?v=1EGk2rvZe8A'&gt;Bekijk het filmpje op Youtube&lt;/a&gt;&lt;/iframe&gt;</description>
    </item>
    
    <item>
      <title>Ruby 1.9.2 nu standaard bij virtual hosting</title>
      <link>http://www.bluerail.nl/weblog/2011/10/05/ruby_1_9_2_nu_standaard_bij_Virtual_Hosting.html</link>
      <pubDate>Wed, 05 Oct 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/10/05/ruby_1_9_2_nu_standaard_bij_Virtual_Hosting</guid>
      <description>&lt;p&gt;Sinds de lancering van Rails 3 wordt door de ontwikkelaars van Ruby on Rails geadviseerd om nieuwe applicaties te ontwikkelen met Ruby 1.9.2. Bluerail biedt sinds april van dit jaar ondersteuning voor Ruby 1.9.2 door middel van een losse Thin applicatieserver. Inmiddels kiest meer dan de helft van de nieuwe gebruikers voor deze oplossing. De tijd is daarom rijp voor een volwaardige implementatie.&lt;/p&gt;

&lt;p&gt;Vanaf nu worden onze servers standaard uitgerust met Ruby 1.9.2 in combinatie met Phusion Passenger zodat u deze met hetzelfde gemak kunt uitrollen als u van ons gewend bent. Bij het bestellen kunt u aangeven van welke Ruby versie u gebruik wenst te maken. Wij zijn van plan om beide versies geruime tijd naast elkaar aan te bieden. Huidige Ruby 1.8.7 gebruikers zijn dus niet verplicht om over te stappen.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Ruby on Rails object id's in Youtube-stijl, met Redis</title>
      <link>http://www.bluerail.nl/weblog/2011/09/16/ids-in-youtube-stijl.html</link>
      <pubDate>Fri, 16 Sep 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/09/16/ids-in-youtube-stijl</guid>
      <description>&lt;p&gt;Recentelijk wilden wij voor een applicatie de gewoonlijke opvolgende id&amp;#8217;s in URL&amp;#8217;s vervangen door iets onherkenbaars. We wilden de URL&amp;#8217;s echter wel kort houden, &lt;em&gt;/posts/101&lt;/em&gt; wordt bijvoorbeeld &lt;em&gt;/posts/D4qPPkZ4&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Om dit te bereiken hebben we een stuk code geschreven dat een korte hash berekent op basis van het ID en de modelnaam. Deze hash slaan we op in Redis (dat reeds gebruikt werd in deze applicatie) zodat we het eenvoudig kunnen toevoegen aan elk gewenst model zonder de database aan te hoeven passen.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Met 8 karakters uit 61 tekens is er ruim voldoende entropie om botsingen tussen de hashes te voorkomen. De hashes worden opgebouwd uit 2 CRC32 checksums. De code is als volgt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# HashParam generates an id hash for an ActiveRecord model and&lt;/span&gt;
&lt;span class='c1'&gt;# exposes that through the to_param method. The hash is stored&lt;/span&gt;
&lt;span class='c1'&gt;# in Redis so find_by_param can retrieve it.&lt;/span&gt;
&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;Bluerail&lt;/span&gt;
  &lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;HashParam&lt;/span&gt;
    &lt;span class='c1'&gt;# The characters that are used in constructing the hash&lt;/span&gt;
    &lt;span class='no'&gt;HASH_CHARS&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#39;&lt;/span&gt;

    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;included&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;base&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;base&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;class_eval&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='c1'&gt;# Store param in Redis upon creation of the model&lt;/span&gt;
        &lt;span class='n'&gt;after_create&lt;/span&gt; &lt;span class='ss'&gt;:update_redis_param&lt;/span&gt;

        &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;find_by_param!&lt;/span&gt; &lt;span class='n'&gt;param&lt;/span&gt;
          &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='vg'&gt;$redis&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;params:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_s&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;param&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/(\d+)/&lt;/span&gt;
            &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vg'&gt;$~&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;else&lt;/span&gt;
            &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;RecordNotFound&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Cannot find key &amp;#39;params:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_s&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;param&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;#39; in Redis&amp;quot;&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;find_by_param&lt;/span&gt; &lt;span class='n'&gt;param&lt;/span&gt;
          &lt;span class='k'&gt;begin&lt;/span&gt;
            &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find_by_param!&lt;/span&gt; &lt;span class='n'&gt;param&lt;/span&gt;
          &lt;span class='k'&gt;rescue&lt;/span&gt;
            &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;update_redis_param&lt;/span&gt;
      &lt;span class='c1'&gt;# Set the value in Redis only if it doesn&amp;#39;t exist yet&lt;/span&gt;
      &lt;span class='vg'&gt;$redis&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setnx&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;params:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_param&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_s&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;to_param&lt;/span&gt;
      &lt;span class='c1'&gt;# Generate two digests, the second one is for additional entropy&lt;/span&gt;
      &lt;span class='n'&gt;s1&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created_at&lt;/span&gt; &lt;span class='p'&gt;?&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='si'&gt;}#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created_at&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_i&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='si'&gt;}#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
      &lt;span class='n'&gt;d1&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Zlib&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;crc32&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;s1&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application-salt1&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;           &lt;span class='c1'&gt;# add your own salt here&lt;/span&gt;
      &lt;span class='n'&gt;d2&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Zlib&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;crc32&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;d1&lt;/span&gt;&lt;span class='si'&gt;}#{&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;application-salt2&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='c1'&gt;# add your own salt here&lt;/span&gt;

      &lt;span class='c1'&gt;# Our output buffer&lt;/span&gt;
      &lt;span class='n'&gt;out&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;&amp;#39;&lt;/span&gt;

      &lt;span class='c1'&gt;# Append the second digest as characters&lt;/span&gt;
      &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='n'&gt;d2&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;
        &lt;span class='n'&gt;d2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;char&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;d2&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;divmod&lt;/span&gt; &lt;span class='no'&gt;HASH_CHARS&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;length&lt;/span&gt;
        &lt;span class='n'&gt;out&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='no'&gt;HASH_CHARS&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;char&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='c1'&gt;# Append the first digest as characters&lt;/span&gt;
      &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='n'&gt;d1&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;
        &lt;span class='n'&gt;d1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;char&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;d1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;divmod&lt;/span&gt; &lt;span class='no'&gt;HASH_CHARS&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;length&lt;/span&gt;
        &lt;span class='n'&gt;out&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='no'&gt;HASH_CHARS&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;char&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='c1'&gt;# Output the last 8 characters of the output buffer&lt;/span&gt;
      &lt;span class='n'&gt;out&lt;/span&gt;&lt;span class='o'&gt;[-&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Deze code plaats je in &lt;em&gt;lib/bluerail/hash&lt;/em&gt;param.rb_. De code verwacht een Redis verbinding via &lt;em&gt;$redis&lt;/em&gt;. Voor een introductie van Redis en het opzetten van een verbinding kun je hebt beste &lt;a href='http://jimneath.org/2011/03/24/using-redis-with-ruby-on-rails.html'&gt;deze blog post van Jim Neath over Redis en Ruby on Rails&lt;/a&gt; eens doorlezen. De HashParam module activeren in een model kan eenvoudig met:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Post&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
    &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='no'&gt;Bluerail&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;HashParam&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Daarna is het wel zaak om eenmalig voor alle bestaande objecten een hash aan te maken:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='no'&gt;Post&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;all&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:update_redis_param&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;De to_param methode zorgt ervoor dat de URL helpers van Ruby on Rails automatisch de hash in plaats van het id gebruiken bij het opstellen van URL&amp;#8217;s. Dit betekent wel dat u in de controller records zult moeten zoeken aan de hand van de find_by_param methode. Indien u &lt;a href='https://github.com/ryanb/cancan'&gt;CanCan&lt;/a&gt; gebruikt kan dit heel eenvoudig automatisch op de volgende manier:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='n'&gt;load_and_authorize_resource&lt;/span&gt; &lt;span class='ss'&gt;:find_by&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:param&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Er zijn natuurlijk talloze variaties mogelijk op het opbouwen en opslaan van de hash.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Waarschuwing bij een Capistrano production deploy</title>
      <link>http://www.bluerail.nl/weblog/2011/08/26/Waarschuwing-bij-cap-production-deploy.html</link>
      <pubDate>Fri, 26 Aug 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/08/26/Waarschuwing-bij-cap-production-deploy</guid>
      <description>&lt;p&gt;Met de &lt;a href='https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension'&gt;multistage plugin voor Capistrano&lt;/a&gt; is het mogelijk om je applicatie uit te rollen naar verschillende omgevingen. Zo kun je bijvoorbeeld &lt;tt&gt;cap staging deploy&lt;/tt&gt; uitvoeren om een nieuwe versie op je staging-omgeving te plaatsen en -als alles naar behoren werkt- door middel van &lt;tt&gt;cap production deploy&lt;/tt&gt; dezelfde versie op de productieomgeving plaatsen.&lt;/p&gt;

&lt;p&gt;In sommige gevallen is de productieomgeving zo kritiek dat je zeker wilt weten een uitrol daadwerkelijk uitgevoerd dient te worden. Door het onderstaande stuk code op te nemen in je deploy.rb wordt de gebruiker om een bevestiging gevraagd voordat zijn uitrol naar de productieomgeving uitgevoerd wordt.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Neem het volgende stuk code op in je deploy.rb, &lt;em&gt;buiten&lt;/em&gt; de deploy namespace:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:ask_production_confirmation&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;say&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class='no'&gt;EOM&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='sh'&gt;    ===========================================================================&lt;/span&gt;
&lt;span class='sh'&gt;         WARNING: You&amp;#39;re about to perform actions on production server(s)&lt;/span&gt;
&lt;span class='sh'&gt;      Please confirm that all your intentions are kind and friendly and that      &lt;/span&gt;
&lt;span class='sh'&gt;       you&amp;#39;re confident that this deployment will not break the application.&lt;/span&gt;
&lt;span class='sh'&gt;    ===========================================================================&lt;/span&gt;

&lt;span class='no'&gt;  EOM&lt;/span&gt;

  &lt;span class='n'&gt;agree&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;agree&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;    Continue deployment? &amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;q&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='n'&gt;q&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;default&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;n&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='nb'&gt;exit&lt;/span&gt; &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;agree&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;production&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;ask_production_confirmation&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Deze bevestiging geeft de ontwikkelaar een extra moment om na te denken over zijn actie waardoor een onbedoelde productie-uitrol voorkomen kan worden.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Achtergrondtaken en versiebeheer: Whenever gem</title>
      <link>http://www.bluerail.nl/weblog/2011/08/19/Achtergrondtaken-en-versiebeheer-Whenever.html</link>
      <pubDate>Fri, 19 Aug 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/08/19/Achtergrondtaken-en-versiebeheer-Whenever</guid>
      <description>&lt;p&gt;Het gebruik van versiebeheer is zeer populair bij Ruby on Rails ontwikkelaars. Met name &lt;a href='http://git-scm.com/'&gt;git&lt;/a&gt; is een populair hulpmiddel om de historie en verschillen tussen de productie-, test- en ontwikkelomgevingen te beheren. Daarnaast is het niet wenselijk om gebruikers te laten wachten op de uitvoer van zware taken, waardoor deze in de achtergrond via een &lt;a href='http://nl.wikipedia.org/wiki/Cronjob'&gt;Cronjob&lt;/a&gt; uitgevoerd worden.&lt;/p&gt;

&lt;p&gt;Zou het dan niet handig zijn als de inhoud van de achtergrondtaken opgenomen kan worden in het versiebeheer? &lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id='waarom_dan_een_cronjob'&gt;Waarom dan een Cronjob?&lt;/h2&gt;

&lt;p&gt;Er zijn tal van oplossingen voor het op de achtergrond uitvoeren van taken. Helaas vereisen de meeste van deze oplossingen (zoals &lt;a href='http://rubygems.org/gems/resque'&gt;Resque&lt;/a&gt; of &lt;a href='http://rubygems.org/gems/delayed_job'&gt;Delayed job&lt;/a&gt;) een permanent draaiend achtergrondproces. Bij &lt;a href='/diensten/ruby-on-rails/virtual-hosting.html'&gt;Virtual Hosting pakketten&lt;/a&gt; is het alleen niet mogelijk achtergrondprocessen te draaien. Het gebruik van &lt;a href='http://nl.wikipedia.org/wiki/Cronjob'&gt;Cronjob&lt;/a&gt; is dan een goed alternatief.&lt;/p&gt;

&lt;h2 id='whenever'&gt;Whenever&lt;/h2&gt;

&lt;p&gt;Gelukkig is er een oplossing: de &lt;a href='https://github.com/javan/whenever'&gt;Whenever&lt;/a&gt; gem. Deze gem stelt ons in staat in een Ruby bestand aan te geven welke taken wanneer uitgevoerd moeten worden. Bij het plaatsen van de applicatie op de server kan met de aanroep van een commando ervoor gezorgd worden dat de achtergrondtaken ingesteld worden, op basis van de instellingen in het Ruby bestand.&lt;/p&gt;

&lt;h3 id='installatie'&gt;Installatie&lt;/h3&gt;

&lt;p&gt;De installatie van de Whenever gem is eenvoudig uit te voeren wanneer gebruik wordt gemaakt van &lt;a href='http://gembundler.com/'&gt;Bundler&lt;/a&gt; voor het beheren van de benodigde gems. Voeg hiervoor in Gemfile het volgende toe:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;whenever&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:require&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Het gedeelte &lt;tt&gt;:require =&gt; false&lt;/tt&gt; geeft aan dat bij het starten van de applicatie het niet nodig is dat de gem geladen wordt. De hulpmiddelen zijn tenslotte alleen nodig om op de server de achtergrondtaken in te stellen.&lt;/p&gt;

&lt;p&gt;Vervolgens dient nog een commando te worden gestart om een initieel &lt;tt&gt;config/schedule.rb&lt;/tt&gt; bestand aan te maken. Voer het onderstaande commando uit in de Rails.root-map (de map van het Ruby on Rails project):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;cd &lt;/span&gt;pad_naar_rails_applicatie
&lt;span class='nv'&gt;$ &lt;/span&gt;wheneverize .
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='gebruik'&gt;Gebruik&lt;/h3&gt;

&lt;p&gt;De achtergrondtaken die uitgevoerd dienen te worden staan beschreven in &lt;tt&gt;config/schedule.rb&lt;/tt&gt;. Een voorbeeld zegt in dit geval ook meer dan 1000 woorden:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;every&lt;/span&gt; &lt;span class='ss'&gt;:friday&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:at&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;5am&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;  
  &lt;span class='n'&gt;rake&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;apply_weekend_call_forwarding&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;every&lt;/span&gt; &lt;span class='ss'&gt;:month&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;rake&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;generate_monthly_statistics&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;every&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hours&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;rake&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;process_pending_deliveries&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Whenever biedt naast bovengenoemde nog tal van andere middelen om taken te beschrijven. Kijk voor meer voorbeelden op de website van de &lt;a href='https://github.com/javan/whenever'&gt;Whenever&lt;/a&gt; gem.&lt;/p&gt;

&lt;h3 id='publicatie'&gt;Publicatie&lt;/h3&gt;

&lt;p&gt;De publicatie middels &lt;a href='https://github.com/capistrano/capistrano'&gt;Capistrano&lt;/a&gt; en het gebruik van Whenever is eenvoudig te koppelen door aan &lt;tt&gt;config/deploy.rb&lt;/tt&gt; een enkele regel toe te voegen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;whenever/capistrano&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wanneer gebruik wordt gemaakt van de &lt;a href='https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension'&gt;multistage extensie voor Capistrano&lt;/a&gt; kan het wenselijk zijn om de naam van omgeving te gebruik in &lt;tt&gt;config/schedule.rb&lt;/tt&gt;. Voeg daarvoor in &lt;tt&gt;config/deploy.rb&lt;/tt&gt; het volgende toe:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='ss'&gt;:whenever_environment&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;defer&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;stage&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;tt&gt;config/schedule.rb&lt;/tt&gt; is het dan mogelijk om bepaalde taken alleen uit te voeren bij een bepaalde omgeving:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;environment&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;production&amp;#39;&lt;/span&gt;
  &lt;span class='n'&gt;every&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;day&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='c1'&gt;# Specifieke taken voor productieomgeving&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;De Whenever gem maakt het zeer eenvoudig om cronjobs te configureren voor Rails applicaties, zonder dat daarvoor handmatig wijzigingen op de server uit hoeft te voeren. Dankzij de goede integratie met Capistrano is het uitrollen van nieuwe taken kinderspel.&lt;/p&gt;

&lt;p&gt;&lt;a href='https://github.com/javan/whenever'&gt;Bekijk de Whenever gem op Github&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Het Bluerail weblog, aangedreven door Jekyll</title>
      <link>http://www.bluerail.nl/weblog/2011/08/15/Bluerail_weblog_over_Ruby_on_Rails.html</link>
      <pubDate>Mon, 15 Aug 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/08/15/Bluerail_weblog_over_Ruby_on_Rails</guid>
      <description>&lt;p&gt;Vanaf vandaag bevat de Bluerail website een blog. Op dit blog zullen we schrijven over zaken die niet alleen voor onze klanten, maar ook voor andere Ruby on Rails gebruikers interessant zijn. Denk daarbij aan nuttige gems, codeknipsels of links naar interessante sites. Om het weblog in de site te integreren hebben we de site van de oude PHP engine gemigreerd naar een nieuw platform op basis van &lt;a href='http://jekyllrb.com/'&gt;Jekyll&lt;/a&gt;. &lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id='wat_is_jekyll'&gt;Wat is Jekyll?&lt;/h2&gt;

&lt;p&gt;Jekyll noemt zichzelf een &lt;em&gt;&amp;#8220;simple, blog aware, static site generator&amp;#8221;&lt;/em&gt;. Het in Ruby geschreven Jekyll genereert een statische site op basis van een directory met templates, blogposts met Markdown of Textile opmaak en statische HTML pagina&amp;#8217;s. Het resultaat is een verzameling HTML bestanden die direct naar elke willekeurige webhost geupload kunnen worden. Aangezien wij zelf programmeurs zijn sluit dit perfect aan bij onze wensen. We kunnen nu een post schrijven in Vim of Textmate en met een rsync de gegenereerde pagina&amp;#8217;s rsyncen naar onze server.&lt;/p&gt;

&lt;h2 id='dynamische_functionaliteit'&gt;Dynamische functionaliteit&lt;/h2&gt;

&lt;p&gt;Uiteraard hebben de door Jekyll gegenereerde statische pagina&amp;#8217;s wel een beperking. Ze zijn namelijk statisch. Ineens was het duidelijk waarom door Javascript ingesloten tools als &lt;a href='http://disqus.com/'&gt;Disqus&lt;/a&gt; en &lt;a href='https://gist.github.com/'&gt;Gist&lt;/a&gt; zo in populariteit zijn gestegen. Het is immers niet mogelijk om in een statische pagina een reactie-systeem te maken. Gelukkig stelt Disqus ons in staat om met een paar regels Javascript een reactiesysteem aan te bieden waar bovendien veel bezoekers al reeds een login voor zullen hebben. Ideaal!&lt;/p&gt;

&lt;p&gt;Het laatste probleem wat we nog hadden was het contact- en bestelformulier. Hiervoor zijn ook verschillende on-line diensten beschikbaar, maar we hebben besloten om een kleine &lt;a href='http://sinatrarb.com/'&gt;Sinatra&lt;/a&gt; applicatie te maken die deze formulieren afhandelt. De Sinatra app staat helemaal los van de Jekyll pagina&amp;#8217;s. De formulieren worden in de pagina geplaatst door middel van een iframe. Dit is niet de netste oplossing, dus we zijn nog aan het kijken hoe we het een en ander kunen verbeteren.&lt;/p&gt;

&lt;h2 id='open_source'&gt;Open source&lt;/h2&gt;

&lt;p&gt;Tijdens het opzetten van de op Jekyll en Sinatra gebaseerde website hebben we veel gehad aan &lt;a href='https://github.com/mojombo/jekyll/wiki/Sites'&gt;de publiek gemaakte source code van andere websites&lt;/a&gt;. We zullen daarom op korte termijn ook de source code van onze eigen website openbaar maken. Overigens hebben we een fork van Jekyll gemaakt om de datums van blog posts in het Nederlands te kunnen weergeven. Er is inmiddels een pull request geplaatst om deze functie in Jekyll op te nemen.&lt;/p&gt;

&lt;p&gt;We zijn zeer tevreden met Jekyll. Wil je meer weten, kijk dan eens op de &lt;a href='https://github.com/mojombo/jekyll/wiki'&gt;Jekyll Wiki&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Nu nog meer schijfruimte bij het Business pakket</title>
      <link>http://www.bluerail.nl/weblog/2011/05/30/Schijfruimte_business_pakket_verdubbeld.html</link>
      <pubDate>Mon, 30 May 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/05/30/Schijfruimte_business_pakket_verdubbeld</guid>
      <description>&lt;p&gt;Het Business Virtual Hosting pakket bevat met ingang van vandaag 1 gigabyte schijfruimte, een verdubbeling van de oude hoeveelheid. Dit maakt het Business pakket het ideale pakket voor middelgrote applicaties en een aantrekkelijke upgrade vanaf het Hobby pakket. De wijziging is per direct voor alle huidige Business klanten doorgevoerd.&lt;/p&gt;

&lt;p&gt;Kijk op de &lt;a href='/diensten/ruby-on-rails/virtual-hosting.html'&gt;Virtual Hosting&lt;/a&gt; pagina voor meer informatie over onze hostingpakketten.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Ruby 1.9.2 ondersteuning bij Bluerail</title>
      <link>http://www.bluerail.nl/weblog/2011/04/06/ruby_1_9_2_ondersteuning.html</link>
      <pubDate>Wed, 06 Apr 2011 00:00:00 CEST</pubDate>
      <author>info@bluerail.nl (Bluerail)</author>
      <guid>http://www.bluerail.nl/weblog/2011/04/06/ruby_1_9_2_ondersteuning</guid>
      <description>&lt;p&gt;Deze maand hebben wij Ruby 1.9.2 opgenomen in ons standaardpakket, naast de bestaande Ruby 1.8.7 ondersteuning. Hiermee kan de nieuwste generatie applicaties optimaal presteren op ons platform. Voor nieuwe Rails 3 en Sinatra applicaties kunnen wij het gebruik van Ruby 1.9.2 van harte aanbevelen.&lt;/p&gt;

&lt;p&gt;Uw Ruby 1.9.2 applicatie wordt geserveerd door een eigen Thin server achter onze Apache proxy. Op het bestelformulier kunt u de voorkeur voor uw Ruby versie aangeven. In de nabije toekomst zal het hosten van Ruby 1.9.2 applicaties via het gebruikelijke Passenger platform ook mogelijk worden. Op Virtual Private Servers en dedicated servers is het gebruik van Ruby on Rails met Ruby 1.9.2 uiteraard ook mogelijk.&lt;/p&gt;

&lt;p&gt;Kijk voor meer informatie over onze Virtual Hosting diensten op de &lt;a href='/diensten/ruby-on-rails/virtual-hosting.html'&gt;Virtual Hosting&lt;/a&gt; pagina.&lt;/p&gt;</description>
    </item>
    

  </channel> 
</rss>

