<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
  <title>Pierlis</title>
  <subtitle>Pierlis Blog</subtitle>
  <link href="http://pierlis.com/" rel="alternate" type="text/html"/>
  <link href="http://pierlis.com/articles.atom" rel="self" type="application/atom+xml"/>
  <id>tag:pierlis.com,2007:/</id>
  <updated>2008-09-05T15:27:29+02:00</updated>
  <author>
    <name>Pierlis</name>
    <uri>http://pierlis.com/</uri>
    <email>list@pierlis.com</email>
  </author>
  <rights>Coyright 2008 Pierlis - Tous droits de reproduction r&#233;serv&#233;s.</rights>
  <entry>
    <title>Offre d&#8217;emploi &#8212; d&#233;veloppeur</title>
    <link href="http://pierlis.com/blog/2008/9/5/offre-d%E2%80%99emploi-%E2%80%94-developpeur" rel="alternate" type="text/xml"/>
    <id>tag:pierlis.com,2008-09-05:/offre-d%E2%80%99emploi-%E2%80%94-developpeur</id>
    <published>2008-09-05T15:21:00+02:00</published>
    <updated>2008-09-05T15:27:29+02:00</updated>
    <content xml:lang="fr" type="xhtml" xml:base="http://pierlis.com/">
      <div xmlns="http://www.w3.org/1999/xhtml">

<p>Pierlis est une agence parisienne spécialisée dans le développement de web applications, et d&#8217;applications de bureau pour MacOSX et iPhone;</p>


	<p>Nous recherchons un développeur avec 2 à 10 ans d&#8217;expérience pour rejoindre notre équipe de passionnés. Contrat en <span class="caps">CDI</span>. Rémunération selon expérience.</p>


	<ul>
	<li>Vous aimez l&#8217;autonomie, mais pas l&#8217;isolement ?</li>
		<li>Vous aimez la veille technologique et l&#8217;innovation ?</li>
		<li>Vous pensez que la carrière d&#8217;un développeur expérimenté ne passe par forcément par la case chef de projet ?</li>
	</ul>


	<p>N&#8217;hésitez pas à nous envoyer un CV. (list at pierlis.com)</p>


<strong>Vos compétences</strong>
	<ul>
	<li>Vous maîtrisez la conception orientée objet.</li>
		<li>Vous connaissez <span class="caps">SQL</span>.</li>
		<li>Vous avez utilisé en production au moins l’un des langages suivant : Ruby, Python, Objective-C,  C++, Java, C#, Haskell, Erlang.</li>
		<li>Vous avez l&#8217;habitude d&#8217;utiliser un framework.</li>
		<li>Vous avez une bonne culture générale informatique.</li>
		<li>Vous maîtrisez l&#8217;anglais.</li>
	</ul>


	<p>Idéalement, vous pratiquez Ruby, <span class="caps">ROR</span> (Ruby On Rails), éventuellement Javascript. A défaut, vous avez envie de vous y mettre.</p>


<strong>Vos responsabilités</strong>
	<ul>
	<li>Développement des projets en cours, en équipe ou de façon autonome selon les cas. </li>
		<li>Qualité du code produit</li>
		<li>Veille technologique</li>
		<li>(...)</li>
	</ul>


	<p>Nous sommes basé à Paris dans le 10e arrondissement.
Environnement de travail agréable et stimulant.
Clients / Projets &#8211; Prestigieux / Innovants.</p>
      </div>
    </content>
  </entry>
  <entry>
    <title>Having Rails &amp; Cocoa play together</title>
    <link href="http://pierlis.com/blog/2008/1/2/having-rails-cocoa-play-together" rel="alternate" type="text/xml"/>
    <id>tag:pierlis.com,2008-01-02:/mixing-rails-cocoa</id>
    <published>2008-01-02T13:39:00+01:00</published>
    <updated>2008-01-04T11:36:21+01:00</updated>
    <content xml:lang="fr" type="xhtml" xml:base="http://pierlis.com/">
      <div xmlns="http://www.w3.org/1999/xhtml">

<div class="media">
          <img alt="What our Captcha will look like" src="/media/pierliscaptcha.png?1199368351" />              <span class="legend">What our Captcha will look like</span>
            </div>


	<p>When it comes to image manipulation, most  Web Applications make use of ImageMagick, through a connector. (In the ruby world, RMagick).
ImageMagick is a decent graphic framework, though it can be quite a time-consuming and error-prone process to go beyond the classical crop/scale/reformat image manipulations. And it is a pain to install, too.
In today&#8217;s post we&#8217;ll build a captcha system. Not that we like captchas at all, but it is quite a good example to enlight our purpose: the use of the Cocoa <span class="caps">API</span> from a Ruby On Rails web application. Indeed, we&#8217;ll delegate all the graphic code to a brave soldier: <a href="http://developer.apple.com/graphicsimaging/quartz/quartzcomposer.html">Quartz Composer</a>. As you will see, it will drastically lower the lines of code required for our service, and, as usual, the less code, the less bugs.</p>


	<p><strong>Important</strong>: As you would have guessed, you will need a mac with Leopard and the <a href="http://developer.apple.com/tools/">developer tools</a> installed. <a href="http://flickr.com/photos/twylo/173895378/">No big deal however</a> for most Rails developers.</p>


	<h1>Setup a Rails application.</h1>


	<p>From your terminal, type <code>rails captcha</code> to create a new rails Application. We now want to give our Rails Application access to the Quartz Composer <span class="caps">API</span>. To do that, add the following at the end of your <code>config/environment.rb</code> file:</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="support support_class support_class_ruby">Mime</span><span class="punctuation punctuation_separator punctuation_separator_other punctuation_separator_other_ruby">::</span><span class="support support_class support_class_ruby">Type</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>register <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>image/jpeg<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>jpg</span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> Include your application configuration below
</span><span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> May Cocoa be with us
</span><span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>osx/cocoa<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">include</span> <span class="variable variable_other variable_other_constant variable_other_constant_ruby">OSX</span>
<span class="support support_class support_class_ruby">OSX</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>require_framework <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>Quartz<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span></code></pre>


	<p>Thanks to RubyCocoa bridge (new with Leopard), all Cocoa <span class="caps">API</span> is now available to you using the Ruby language. We&#8217;ve included the Quartz Framework, that will be necessary for the rendering of our composition.</p>


	<p>We have also registered the <code>image/jpeg</code> Mime-type, as we will use that in a <code>respond_to</code> block in our controller.</p>


	<h1>Create a Quartz Composition for your captcha.</h1>


<div class="media">
      <img alt="Our captcha Quartz Composition" src="/media/quartzcaptcha.png?1199368337" />              <span class="legend">Our captcha Quartz Composition</span>
        </div>

	<p>Open <strong>Quartz Composer</strong>, it should be in <code>/Developer/Applications/</code> if you installed the developer tools in the standard location. Create your captcha, for example using the &#8220;Image With String&#8221; generator.</p>


	<p>Publish an input by right-clicking on the &#8220;String&#8221; input of the &#8220;Image With String&#8221; generator. Name it &#8220;Captcha&#8221;. This step is important, because we will then be able to pass a hash of parameters to the Quartz composition from our Rails application. &#8220;Captcha&#8221; will be the key in the parameters hash.</p>


	<ul>
	<li>If you are new to Quartz Composer, <a href="http://developer.apple.com/graphicsimaging/quartz/quartzcomposer.html">read the doc</a>, play with it, it is an awesome &#8220;noodle-oriented&#8221; development tool. </li>
		<li>If you are lazy, just download our ready-to-run project from the top of the pink sidebar on the right of this page.</li>
	</ul>


	<p>Save the composition as <code>RAILS_ROOT/qtz/captcha.qtz</code>.</p>


	<p>Now, move on to the code.</p>


	<h1>Create a Composition model</h1>


	<p>Create a new file <code>composition.rb</code> in <code>RAILS_ROOT/app/models</code>, and let&#8217;s start with the Cocoa fun:</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="meta meta_class meta_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby">Composition</span></span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">  <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> creates a composition from a .qtz file whose path is qtz_path
</span>  <span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"><span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">initialize</span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby">(</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby">qtz_path<span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> width <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="constant constant_numeric constant_numeric_ruby">350</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> height <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="constant constant_numeric constant_numeric_ruby">100</span></span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby">)</span></span>
    <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>qtz</span> <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="support support_class support_class_ruby">QCComposition</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>compositionWithFile<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span>qtz_path<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span>
    <span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">raise</span> <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>Can't find a valid composition at “<span class="source source_ruby source_ruby_embedded source_ruby_embedded_source"><span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">#{</span>qtz_path<span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">}</span></span>”.<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span> <span class="keyword keyword_control keyword_control_ruby">if</span> <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>qtz</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>nil?
    <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>renderer</span> <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="support support_class support_class_ruby">QCRenderer</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>alloc<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>initOffScreenWithSize_colorSpace_composition<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span>
                        <span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span>width<span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> height<span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span>
                        <span class="variable variable_other variable_other_constant variable_other_constant_ruby">CGColorSpaceCreateWithName</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="variable variable_other variable_other_constant variable_other_constant_ruby">KCGColorSpaceGenericRGB</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span>
                        <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>qtz</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p><code>QCComposition</code> and <code>QCRenderer</code> are two classes defined in the Quartz Framework. If you wan&#8217;t to know more, open Xcode, and open the Documentation window from the Help menu.</p>


	<p>Have a look at the <code>initOffScreenWithSize_colorSpace_composition</code> method name: pretty long, huh ? Objective-C, the native language of Cocoa, uses named parameters in methods. As ruby does not support that, a convention has been established by the language bridge: replace all colons (<code>:</code>) for underscores (<code>_</code>) in the Objectice-C method signature, to get the Ruby method name. And pass the parameters in the order of the signature. (You can omit the last _). Refer to the <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/RubyPythonCocoa/Articles/RubyPythonMacOSX.html">Apple Documentation</a> to learn more.</p>


	<p>Now move on to rendering :</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> renders the composition at time, with parameters, and return the jpeg data.
</span><span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"><span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">render</span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby">(</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby">time <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="constant constant_numeric constant_numeric_ruby">0</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> parameters <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{}</span></span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby">)</span></span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">  <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> Only pass in parameters expected by the Qtz composition
</span>  input_keys <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>qtz</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>inputKeys
  parameters<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>each <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do </span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span><span class="variable variable_other variable_other_block variable_other_block_ruby">key</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">,</span> <span class="variable variable_other variable_other_block variable_other_block_ruby">value</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span>
    <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>renderer</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>setValue_forInputKey<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span>value<span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> key<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span> <span class="keyword keyword_control keyword_control_ruby">if</span> input_keys<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>include? key
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">  <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> And render the composition
</span>  rendering_ok <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>renderer</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>renderAtTime_arguments<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span>time<span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_language constant_language_ruby">nil</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span>
  <span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">raise</span> <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>Unable to render Quartz Composition<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span> <span class="keyword keyword_control keyword_control_ruby">unless</span> rendering_ok <span class="keyword keyword_operator keyword_operator_comparison keyword_operator_comparison_ruby">==</span> <span class="constant constant_numeric constant_numeric_ruby">1</span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">  <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> now convert the result to jpeg
</span>  bitmapRep <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"><span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby">@</span>renderer</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>createSnapshotImageOfType<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>NSBitmapImageRep<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span>
  jpegimagedata <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> bitmapRep<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>representationUsingType_properties<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="variable variable_other variable_other_constant variable_other_constant_ruby">NSJPEGFileType</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> 
    <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="variable variable_other variable_other_constant variable_other_constant_ruby">NSImageCompressionFactor</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> <span class="constant constant_numeric constant_numeric_ruby">0.8</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span>
    <span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>rubyString 
<span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p>The language bridge makes a very good work in letting you swap any Objective-C basic structure by its ruby counterpart (and vice-versa). In the above code, we first filter the parameters to make sure the composition accepts them. So we first read the <code>inputKeys</code> dictionary (Objective-C word for Hash) from the composition, and then we fill the hash provided that <code>inputKeys</code> includes the keys from the <code>parameters</code> hash. <code>include?</code> is defined in Ruby&#8217;s <code>Hash</code>, but it is ok to call it on an <code>NSDictionary</code>. Thanks, RubyCocoa !</p>


	<h1>Wrap it up !</h1>


	<p>Now, we need a resource to wrap-up our stuff. It will generate a jpeg image on the fly with the captcha. Create <code>captchas_controller.rb</code> under <code>RAILS_ROOT/app/controllers/</code>, and write the following:</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="meta meta_rails meta_rails_controller"><span class="meta meta_class meta_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby">CaptchasController<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> <span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby">&lt;</span> ApplicationController</span></span></span>
  <span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"><span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">show</span></span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">    <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> Read the captcha composition
</span>    captcha <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="support support_class support_class_ruby">Composition</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="support support_class support_class_ruby">File</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>join<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="variable variable_other variable_other_constant variable_other_constant_ruby">RAILS_ROOT</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>qtz<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>captcha.qtz<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">))</span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">    <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> encode our string (rot13 used here to keep our sample concise:. 
</span><span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">    <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> think to something robust for any public use - left as an exercise)
</span>    captcha_string <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> params<span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>id</span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>tr <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>A-Za-z<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>N-ZA-Mn-za-m<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span>
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">    <span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby">#</span> render the captcha
</span>    respond_to <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do </span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span><span class="variable variable_other variable_other_block variable_other_block_ruby">format</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span>
      format<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>text <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="support support_function support_function_actionpack support_function_actionpack_rails">render</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>text</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> captcha_string <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span>
      format<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>jpg  <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span>send_data captcha<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span><span class="support support_function support_function_actionpack support_function_actionpack_rails">render</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="constant constant_numeric constant_numeric_ruby">0</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>Captcha<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> captcha_string<span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>disposition</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>inline<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span> <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span>
    <span class="keyword keyword_control keyword_control_ruby">end</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span>
</span><span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p>As you can see in the source code, you would need some kind of obfuscation to prevent automatic &#8220;guessing&#8221; of the captcha, but that is out of scope of this tutorial. (We use rot13 here, do not do that for production code !)</p>


	<p>Add a <code>map.resources :captchas</code> in <code>config/routes.rb</code>, and edit <code>RAILS_ROOT/public/index.html</code> to make a form with a captcha. For the captcha image, just write: <code>&lt;img src="http://0.0.0.0:3000/captchas/sometext.jpg" alt="captcha" /&gt;</code> and replace <code>sometext</code> with something randomly generated.</p>


	<p>You&#8217;re done !</p>


	<h1>Final thoughts</h1>


	<ul>
	<li>All graphic code has been delegated to Quartz Composer. Once you get used to it, you&#8217;ll appreciate the power of this fantastic tool. Moreover, you can train your favorite graphic designer to use it. It will be easier to generate a new look for each client, without changing your project, <strong>as it does not include any single line related to the captcha appearance</strong>. And it will look a lot nicer !</li>
		<li>Quartz Composer relies on OpenGL and the graphic card for most of its work, so it is very efficient. Incredibly more than ImageMagick.</li>
		<li>OK, perhaps you don&#8217;t host your web apps on a mac. But what about a spare mac that would host several services, like this one, or some other that could be written in just a few lines of Cocoa-Ruby ? Cocoa is a very rich framework, have a look at it ! Examples that come to my mind are: <span class="caps">URL</span>-to-PDF service (a dozen of lines), Image filters (QC again)... Tell us what you&#8217;ve done with it ! and&#8230; <b>Happy new Year</b> !

	<p>Download the complete sample code: <a href="/media/captcha.zip">captcha.zip</a></p></li>
	</ul>
      </div>
    </content>
  </entry>
  <entry>
    <title>Release of the SearchAPI plugin</title>
    <link href="http://pierlis.com/blog/2007/12/10/release-of-the-searchapi-plugin" rel="alternate" type="text/xml"/>
    <id>tag:www.pierlis.com,2007-12-04:/release-of-the-searchapi-plugin</id>
    <published>2007-12-10T00:00:00+01:00</published>
    <updated>2007-12-11T09:36:20+01:00</updated>
    <content xml:lang="fr" type="xhtml" xml:base="http://pierlis.com/">
      <div xmlns="http://www.w3.org/1999/xhtml">

<h1>ActiveRecord and high-level model concepts, round 1</h1>


	<p>When we&#8217;re accessing our models, we&#8217;re always going through several layers of abstraction. The <a href="http://conferences.oreillynet.com/railseurope/">latest European RailsConf</a> provided us with <a href="http://www.railsconfeurope.com/cs/railseurope2007/view/e_sess/14961">two</a> <a href="http://conferences.oreillynet.com/cs/railseurope2007/view/e_sess/14259">sessions</a>, by speakers from <a href="http://www.sun.com">prestigious</a> <a href="http://www.ibm.com">companies</a>, related to that subject. In brief: ActiveRecord is short of one abstraction layer.</p>


	<p>Imagine you are developping a <span class="caps">CMS</span>, a web site displaying articles. You would have to take care about the content you put online: some articles are not fully written yet, others are waiting for the publishing director&#8217;s agreement, etc.</p>


	<p>ActiveRecord does not provide you with a standardized way to cope with this &#8220;online article&#8221; concept. Instead, it helps<sup><a href="#fn1">1</a></sup> you to stick at the column level, to think about &#8220;those articles whose publication date has been set in some date in the past, and flagged as reviewed by the publishing director, etc. etc.&#8221;.</p>


	<p>That&#8217;s a real pity, since &#8220;online article&#8221; is a very good concept: it is stable and it is able to hide its actual implementation. Online articles are the only articles that your website will ever display, this is their very <em>essence</em>. And should suddenly Rupert Murdoch&#8217;s agreement be required, only the implementation of &#8220;online article&#8221; would change.</p>


	<p><a href="http://paulmwatson.com/journal/2006/09/15/railsconf-europe-2006-search-with-rails/">Our own RailsConf Europe 2006 talk</a> focused on an early Pierlis technology named &#8220;ModelSearch&#8221;. Its purpose was, already, to let you declare Search APIs.</p>


	<p>ModelSearch was keen, but Rails has evolved since its birth, we realized that our baby library would not fit well into the directions taken by our favorite framework. It was reluctant to integrate transparently into ActiveRecord, it did not play well with associations, scopes, and definitely wasn&#8217;t fancy. It was time to step back a little.</p>


	<h1>Round 2: SearchAPI</h1>


	<p>We&#8217;re now proud to release the <a href="http://rubyforge.org/projects/searchapi/">SearchAPI plugin</a>. It&#8217;s available on <a href="http://rubyforge.org/projects/searchapi/">RubyForge</a>, placed under the <a href="http://en.wikipedia.org/wiki/Licence_MIT"><span class="caps">MIT</span> license</a>, fully <a href="http://www.pierlis.com/doc/searchapi/">documented</a>, tested<sup><a href="#fn2">2</a></sup>, and installed via the classic command:</p>


	<pre><code>$ script/plugin install svn://rubyforge.org/var/svn/searchapi</code></pre>


	<h2>SearchAPI extends the expressivity of ActiveRecord&#8217;s conditions hashes.</h2>


	<p>Since Rails 1.2, ActiveRecord&#8217;s <code>find</code> method accepts <a href="http://api.rubyonrails.com/classes/ActiveRecord/Base.html">condition hashes</a>, in which all keys are columns.</p>


	<pre><code><span class="source source_ruby"><span class="variable variable_other variable_other_constant variable_other_constant_ruby">Article</span>.find(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>,
  <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block">
</span>    <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:author_id</span> =&gt; <span class="constant constant_numeric constant_numeric_ruby">1</span>,
    <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:validated_by_publishing_director</span> =&gt; <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span> })
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># =&gt; all articles whose author_id is 1, and validated_by_publishing_director is true</span></span></code></pre>


	<p>SearchAPI let you extend conditions hashes powers, by defining <strong>your own keys</strong>.</p>


	<pre><code><span class="source source_ruby"><span class="variable variable_other variable_other_constant variable_other_constant_ruby">Article</span>.find(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:online</span> =&gt; <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span> })
<span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># =&gt; all online articles, the SearchAPI way</span>
</span></code></pre>


	<p>The cool thing here is that there is no such &#8220;online&#8221; column.</p>


	<h2>SearchAPI integrates <em>really</em> nicely with ActiveRecord</h2>


	<p>SearchAPI allows you to mix conditions:</p>


	<pre><code><span class="source source_ruby"><span class="variable variable_other variable_other_constant variable_other_constant_ruby">Article</span>.find(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>,
  <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block">
</span>    <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:online</span> =&gt; <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span>,
    <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tagged_as</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'rails'</span>,
    <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:author_id</span> =&gt; <span class="constant constant_numeric constant_numeric_ruby">1</span> })</span></code></pre>


	<p>Or to use your Search <span class="caps">API</span> with associations (or to mix conditions and associations, etc.):</p>


	<pre><code><span class="source source_ruby"><span class="variable variable_other variable_other_constant variable_other_constant_ruby">Author</span>.find(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:first</span>).articles.find(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:online</span> =&gt; <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span> })</span></code></pre>


	<h2>Declaring a Search <span class="caps">API</span>, an example</h2>


	<p>As usual in computer science, there&#8217;s no magic here, but a few lines of code instead. SearchAPI allows you to independently declare the actual ActiveRecord conditions for each one of your own, high-level, search keys.</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="meta meta_rails meta_rails_model"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">Article<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; ActiveRecord::Base</span></span></span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">belongs_to</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:author</span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">has_many</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:taggings</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:dependent</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:delete_all</span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">has_many</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tags</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:through</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:taggings</span><br />
  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Allows declaration of high-level Search API</span>
  has_search_api<br />
  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Define the online search key</span>
  search <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:online</span> <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do </span>|search|
    <span class="keyword keyword_control keyword_control_ruby">if</span> search.online
      {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"published_on &lt;= ? AND validated_by_publishing_director = ?"</span>, <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Time</span>.now, <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span>]}
    <span class="keyword keyword_control keyword_control_ruby">else</span>
      {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"published_on &gt; ? OR validated_by_publishing_director = ?"</span>, <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Time</span>.now, <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">false</span>]}
    <span class="keyword keyword_control keyword_control_ruby">end</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span><br />
  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Define the tagged_as search key</span>
  search <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tagged_as</span> <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do </span>|search|
    {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:include</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tags</span>,
      <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"tags.name = ?"</span>, search.tagged_as]}
  <span class="keyword keyword_control keyword_control_ruby">end</span>
</span><span class="keyword keyword_control keyword_control_ruby">end</span></span></code></pre>


	<p>Let&#8217;s dig a little into the code above.</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="meta meta_rails meta_rails_model"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">Article<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; ActiveRecord::Base</span></span></span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">belongs_to</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:author</span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">has_many</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:taggings</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:dependent</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:delete_all</span>
  <span class="support support_function support_function_activerecord support_function_activerecord_rails">has_many</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tags</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:through</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:taggings</span></span></span></code></pre>


	<p>This is the setup of our Article class. It&#8217;s designed to let me demonstrate SearchAPI features, especially its integration with associations.</p>


	<pre><code><span class="source source_ruby source_ruby_rails">  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Allows declaration of high-level Search API</span>
  has_search_api</span></code></pre>


	<p>As the plugin does not automatically decorate all ActiveRecord classes, the line above is required to declare the article Search <span class="caps">API</span>.</p>


	<pre><code><span class="source source_ruby source_ruby_rails">  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Define the online condition</span>
  search <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:online</span> <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do </span>|search|
    <span class="keyword keyword_control keyword_control_ruby">if</span> search.online
      {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"published_on &lt;= ? AND validated_by_publishing_director = ?"</span>, <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Time</span>.now, <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">true</span>]}
    <span class="keyword keyword_control keyword_control_ruby">else</span>
      {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"published_on &gt; ? OR validated_by_publishing_director = ?"</span>, <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Time</span>.now, <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">false</span>]}
    <span class="keyword keyword_control keyword_control_ruby">end</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span></span></code></pre>


	<p>This is our first piece of meat: the declaration of the <code>:online</code> parameter of the article Search <span class="caps">API</span>.</p>


	<p>The <code>search</code> method is added to your class by <code>has_search_api</code>. Its first parameter is the name of the search key that will be implemented in the block.</p>


	<p>The block has to return a standard<sup><a href="#fn3">3</a></sup> ActiveRecord find hash, with its usual <code>:conditions</code>, <code>:include</code>, <code>:join</code>, <code>:order</code>, etc. parameters. This find hash will be merged with others by SearchAPI, and given to ActiveRecord that will process it as it has always done.</p>


	<p><em>Somehow, SearchAPI is a merger of find hashes, nothing more. ActiveRecord is, and remains, the only Rails&#8217; <span class="caps">ORM</span>.</em></p>


	<p>Now let&#8217;s have a look at the <code>:tagged_as</code> search key:</p>


	<pre><code><span class="source source_ruby source_ruby_rails"><span class="meta meta_rails meta_rails_model">  <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># Define the tagged_as condition</span>
  search <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tagged_as</span> <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do </span>|search|
    {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:include</span> =&gt; <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:tags</span>,
      <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_double string_quoted_double_ruby">"tags.name = ?"</span>, search.tagged_as]}
  <span class="keyword keyword_control keyword_control_ruby">end</span>
</span></span></code></pre>


	<p><code>:tagged_as</code> demonstrates how to get the support of associations to define search keys. Using a <code>:join</code> parameter would have been just as correct.</p>


	<p>In case you would like to play with above code, you may download <a href="/doc/searchapi_example_application.tgz">a sample application</a> that defines the above models. The console will be your playground, though.</p>


	<h1>A word about the plugin design</h1>


	<p>Despite the fact that in the former examples, SearchAPI seems deeply integrated to ActiveRecord, it is not. SearchAPI is definitely one layer above ActiveRecord.</p>


	<p>SearchAPI is, actually, built on three components: one that defines <a href="http://www.pierlis.com/doc/searchapi/classes/SearchApi/Search/Base.html">what&#8217;s a Search <span class="caps">API</span></a>, another that makes such an <span class="caps">API</span> able to talk to ActiveRecord through a <a href="http://www.pierlis.com/doc/searchapi/classes/SearchApi/Bridge/ActiveRecord.html">dedicated bridge</a>, and the last one, which is hardly a component, does nothing more that modifying ActiveRecord so that SearchAPI looks deeply integrated into it &#8211; but <a href="http://www.pierlis.com/doc/searchapi/classes/SearchApi/Integration/ActiveRecord/Base.html">that&#8217;s just an illusion</a>.</p>


	<p><a href="http://www.pierlis.com/doc/searchapi">Digging around the documentation</a> is not sufficient, but should help you understanding SearchAPI.</p>


	<h1>Why a public release?</h1>


	<p><del>Fame!</del></p>


	<p>There are <a href="http://www.tbray.org/ongoing/When/200x/2003/11/16/SearchAPIs">many</a> <a href="http://www.imc.org/atom-syntax/mail-archive/msg18997.html">discussions</a> about the nature of a Restful search.</p>


	<p>In the Rails community, there seem to be a <a href="http://woss.name/2007/07/22/convention-for-restful-search-in-rails/">need for a convention</a> upon that subject. This may be caused by the focus on <a href="http://en.wikipedia.org/wiki/Convention_over_Configuration">convention over configuration</a>, and the excitement around the edgy ActiveResource.</p>


	<p>Honestly, I&#8217;m not sure such a convention will eventually emerge.</p>


	<p>As a matter of fact, since an ActiveResource server is unlikely to expose directly its database, it has to define, in a way or another, a Search <span class="caps">API</span> for its searchable resources. Some people say:</p>


	<blockquote>
		<p>A Restful design pre-defines the useful collections URLs the clients will subscribe to.</p>
	</blockquote>


	<p>This is surely a great design guideline, and no one would complaining if required to download <code>http://server.com/people/ceo.xml</code>, or <code>http://server.com/titles/ceo/people.xml</code>, in order to fetch a list of all CEOs.</p>


	<p>Nevertheless, I think that this Restful design may, sometimes, be hard to maintain. Especially if your server should allow its clients to dig into data using several dimensions. Is there any possible convention upon the <span class="caps">URL</span> of all 34-years-old female CEOs ?</p>


	<p>Maybe <code>http://server.com/people.xml?title=CEO&#38;sex=female&#38;age=34</code> is not the worse candidate, even if it does not look Restful.</p>


	<p>It is, at least, <strong>rest-savvy</strong>. The SearchAPI plugin, through its ability to define high-level search keys, is able to directly process the parameters of this <span class="caps">URL</span>. That&#8217;s why it is our contribution to the &#8220;restification&#8221; of Rails.</p>


	<p>Thank you for reading.</p>


	<p><br /><br /></p>


	<h3>Footnotes</h3>


	<p id="fn1"><sup>1</sup> The bravest of you will define some class methods or constants that will encapsulate that concept. By doing this, they will still have to fight against ActiveRecord when high level features like associations get involved.</p>


	<p id="fn2"><sup>2</sup> Testing the plugin requires a host application, in order to setup the test database with test data. SearchAPI has been tested on Rails 1.2.5, 1.2.6 and 2.0.1.</p>


	<p id="fn3"><sup>3</sup> Well, not quite standard. We&#8217;ve added two features, the first because it&#8217;s handy, the last because we needed it.</p>


	<p>First, the <code>["sanitize ?", 'this']</code> syntax is allowed for all parameters, not only <code>:conditions</code>.</p>


	<p>We also introduced the <code>:having</code> parameter. The usual practice is to put <span class="caps">SQL</span> <code>HAVING</code> conditions in the <code>:group</code> parameter. But since SearchAPI allows you to define several search keys, it&#8217;s possible that several groupings, declared independently, are activated at the same time, and will eventually be merged. This merging wouldn&#8217;t be possible without splitting the <code>GROUP BY</code> clause from the <code>HAVING</code> conditions. I hope I&#8217;ll be able to publish an example test case that will proove the need for that <code>:having</code> key.</p>
      </div>
    </content>
  </entry>
  <entry>
    <title>Super Rescue</title>
    <link href="http://pierlis.com/blog/2007/11/28/super-rescue" rel="alternate" type="text/xml"/>
    <id>tag:www.pierlis.com,2007-11-28:/super_rescue</id>
    <published>2007-11-28T23:29:00+01:00</published>
    <updated>2007-12-03T20:54:15+01:00</updated>
    <content xml:lang="fr" type="xhtml" xml:base="http://pierlis.com/">
      <div xmlns="http://www.w3.org/1999/xhtml">

<div class="media">
          <img alt="" src="/media/super-rescue.jpg?1196289137" />            </div>


	<p><strong>&#8220;Super rescue&#8221; is not a superhero, it&#8217;s a trick for Ruby on Rails developers that, indeed, may rescue you when you&#8217;re in trouble.</strong></p>


	<p>It involves models&#8217; methods defined like:</p>


	<pre><code><span class="source source_ruby"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">BlogPost<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; AR::Base</span></span></span>
<span class="declaration declaration_function declaration_function_method declaration_function_method_without-arguments declaration_function_method_without-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">language</span></span>
    <span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby">super</span> <span class="keyword keyword_control keyword_control_ruby">rescue</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'en'</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p>Yes, that&#8217;s actual Ruby.</p>


	<p>Generally speaking, it means &#8220;attempt to get language in the way of my super class, and if it fails, let&#8217;s assume it&#8217;s English&#8221;.</p>


	<p>In practice, Super Rescue is handy when Rails&#8217; <code>rake db:migrate</code> won&#8217;t run throughout. The main theme is:</p>


	<h1>Compatibility with former versions of the database schema</h1>


	<p>There&#8217;s a quite common situation in a Ruby on Rails application that undergoes many iterations : the application code is in sync with the last version of your migrations, not with former ones.</p>


	<p>The <span class="caps">OMG</span> scenario is the following:</p>


	<ul>
	<li>In a recent migration, you added a brand new model, or a column to another, and you source code reflected this changes by updating a validation, or adding a callback like <code>after_save</code> that does some important stuff.</li>
		<li>In a former migration, you actually fill in the database with initial data (like an initial admin user, or an initial greeting post, whatever).</li>
		<li>You deploy a brand new copy of your application, and trustfully <code>rake db:migrate</code> to setup your brand new empty database.</li>
	</ul>


	<p>You see the pattern: as migrations run through from the beginning, they&#8217;ll eventually trigger parts of your present code that references <strong>columns or models that do not exist yet</strong>.</p>


	<p>An exception abruptly wakes you up: you encounter the incompatibility-with-former-versions-of-the-database-schema syndrome.</p>


	<h1>Super rescue to the rescue!</h1>


	<p>You think your migration complains because a <strong>column</strong> does not exist (yet)?</p>


	<p>That&#8217;s not <em>quite</em> the case.</p>


	<p>Database columns <em>do</em> help Rails building getter and setter methods in your ActiveRecord subclasses. These methods are the indirect way column values are usually accessed.</p>


	<p>So, what really do not exist are <strong>ruby methods</strong>.</p>


	<p>So, let&#8217;s fool Rails. The <code>language</code> column does not exist yet in the <code>blog_posts</code> table, er, the <code>language</code> method does not exist yet in the <code>BlogPost</code> class? Take that, Rails!</p>


	<pre><code><span class="source source_ruby"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">BlogPost<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; AR::Base</span></span></span>
<span class="declaration declaration_function declaration_function_method declaration_function_method_without-arguments declaration_function_method_without-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">language</span></span>
    <span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby">super</span> <span class="keyword keyword_control keyword_control_ruby">rescue</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'en'</span>
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p>Precisely speaking, it means: &#8220;let ActiveRecord handle the database access as usual, but should the column not exit, assume a default value&#8221;.</p>


	<p>And now your</p>


	<pre><code><span class="source source_ruby"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">BlogPost<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; AR::Base</span></span></span>
  validates_presence_of <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:language</span>
<span class="keyword keyword_control keyword_control_ruby">end</span></span></code></pre>


	<p>will succeed, even if the column is still to be created by a future migration.</p>


	<h1>Super rescue getter/setter</h1>


	<p>The getter/setter pair can be implemented as:</p>


	<pre><code><span class="source source_ruby"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">BlogPost<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; AR::Base</span></span></span>
<span class="declaration declaration_function declaration_function_method declaration_function_method_without-arguments declaration_function_method_without-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">language</span></span>
    <span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby">super</span> <span class="keyword keyword_control keyword_control_ruby">rescue</span> (<span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@language</span> ||= <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'en'</span>)
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="declaration declaration_function declaration_function_method declaration_function_method_with-arguments declaration_function_method_with-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">language=</span>(<span class="variable variable_parameter">value</span>)</span>
    <span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby">super</span> <span class="keyword keyword_control keyword_control_ruby">rescue</span> (<span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@language</span> = value)
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="keyword keyword_control keyword_control_ruby">end</span>
</span></code></pre>


	<p>And now your</p>


	<pre><code><span class="source source_ruby"><span class="declaration declaration_class declaration_class_ruby"><span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby">Blog<span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"> &lt; AR::Base</span></span></span>
  has_many <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:blog_posts</span>
<span class="declaration declaration_function declaration_function_method declaration_function_method_without-arguments declaration_function_method_without-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">after_create</span></span>
    blog_posts.create(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:content</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'This is your first post'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:language</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'en'</span>)
  <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="keyword keyword_control keyword_control_ruby">end</span></span></code></pre>


	<p>will work like a charm, despite the lack of the <code>language</code> column.</p>


	<p>That&#8217;s the trick. Simple, effective, and harmless.</p>


	<p>It&#8217;s not a panacea. It&#8217;s unable to handle complex situations. The incompatibility-with-former-versions-of-the-database-schema syndrome lost a battle, not the war.</p>


	<p>Thank you for reading.</p>
      </div>
    </content>
  </entry>
</feed>
