<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>LoGeek's software blog - Home</title>
  <id>tag:blog.logeek.fr,2011:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
  <link href="http://blog.logeek.fr/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://blog.logeek.fr/" rel="alternate" type="text/html"/>
  <updated>2011-03-04T22:28:56Z</updated>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2011-03-04:83</id>
    <published>2011-03-04T22:27:00Z</published>
    <updated>2011-03-04T22:28:56Z</updated>
    <category term="books"/>
    <category term="hackernews"/>
    <category term="stackoverflow"/>
    <link href="http://blog.logeek.fr/2011/3/4/hackerbooks-books-stackoverflow-hackernews" rel="alternate" type="text/html"/>
    <title>HackerBooks.com (books from StackOverflow and HackerNews)</title>
<content type="html">
            &lt;p&gt;I&#8217;ve teamed with my wife to ship &lt;a href=&quot;http://www.hackerbooks.com&quot;&gt;HackerBooks.com&lt;/a&gt;, a search engine for books quoted on StackOverflow and HackerNews.&lt;/p&gt;


	&lt;p&gt;The app is currently fairly simple and more is planned &#8211; yet I hope you will like the site just as much as the HackerNews crowd!&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://www.hackerbooks.com&quot;&gt;
  &lt;img src=&quot;http://blog.logeek.fr/assets/2011/3/4/hackerbooks.png&quot; alt=&quot;HackerBooks.com&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;


	&lt;h3&gt;A few usage tips&lt;/h3&gt;


	&lt;p&gt;There&#8217;s an advanced search built-in which is not really advertised yet but you may want to use:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;search for &lt;em&gt;has:kindle&lt;/em&gt; to find all &lt;a href=&quot;http://www.hackerbooks.com/books?keywords=has:kindle&quot;&gt;kindle ebooks&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;search for &lt;em&gt;site:stackoverflow&lt;/em&gt; to find all &lt;a href=&quot;http://www.hackerbooks.com/books?keywords=site:stackoverflow&quot;&gt;stackoverflow books&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;or &lt;em&gt;site:hackernews&lt;/em&gt; to restrict to &lt;a href=&quot;http://www.hackerbooks.com/books?keywords=site:hackernews&quot;&gt;hackernews books&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Technical notes&lt;/h3&gt;


	&lt;p&gt;It deserves more blogging, definitely, but I&#8217;ll give at least some details today.&lt;/p&gt;


	&lt;p&gt;The app is largely an &lt;span class=&quot;caps&quot;&gt;ETL&lt;/span&gt; which munges multiple data sources (including the StackExchange data dump, a HackerNews data dump and a HackerNews crawler I wrote, as well as calls to the Amazon &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;) to extract, conform and load books and quotes from these sites users into a MongoDB back-end.&lt;/p&gt;


	&lt;p&gt;On top of that, Sunspot and Solr are used to index the books and allow a full-text search with a flexible weighting (see below):&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
    Sunspot.search(Book) do
      unless query[:keywords].blank?
        keywords query[:keywords], :fields =&amp;gt; [:title, :description] do
          boost_fields :title =&amp;gt; 1.5, :description =&amp;gt; 0.4
          boost(function { product(:karma, 3) }) 
        end
      else
        order_by :karma, :desc
      end

      with(:quoted_on,query[:quoted_on]) unless query[:quoted_on].blank?
      with(:kindle_edition,true) if query[:kindle]

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

	&lt;p&gt;Then we have a RubyOnRails 3 app which runs on top of &lt;span class=&quot;caps&quot;&gt;RVM&lt;/span&gt;/Ruby 1.9.2.&lt;/p&gt;


	&lt;p&gt;To make a snappy app, we&#8217;ve used Nginx and Passenger, then Redis for the caching, coupled with a &#8220;warmup&#8221; script which fills the cache.&lt;/p&gt;


	&lt;h3&gt;Keeping things &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt; light&lt;/h3&gt;


	&lt;p&gt;Initially the crawler was relying on &lt;a href=&quot;https://github.com/defunkt/resque&quot;&gt;resque&lt;/a&gt; (my favourite background processing tool) and &lt;a href=&quot;https://github.com/bvandenbos/resque-scheduler&quot;&gt;resque-scheduler&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;I really like both tools for my work but here we were severely restricted in &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt;. Resque spawns a child process and resque-scheduler needs a dedicated process too, so it wasn&#8217;t a good fit here.&lt;/p&gt;


	&lt;p&gt;So another welcome addition to my toolset in this specific case is &lt;a href=&quot;http://rufus.rubyforge.org/rufus-scheduler/&quot;&gt;rufus-scheduler&lt;/a&gt; by &lt;a href=&quot;http://jmettraux.wordpress.com/&quot;&gt;John Mettraux&lt;/a&gt;, which allowed me to quickly (2 hours) convert the existing crawler to a more lightweight, single-process one.&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
  require 'rubygems'
  require 'bundler'

  # restrict required gems with a bundler group to save RAM
  ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', File.dirname(__FILE__))
  Bundler.require(:crawler)

  require 'rufus/scheduler'
  scheduler = Rufus::Scheduler.start_new

  # ...

  scheduler.cron '*/15 * * * *' do
    puts &quot;Scheduling FetchHackerNewsNewestJob&quot; 
    FetchHackerNewsNewestJob.perform
  end

  scheduler.cron '*/2 * * * *' do
    puts &quot;Scheduling ExtractHackerNewsPageJob&quot; 
    ExtractHackerNewsPageJob.perform
  end

  def scheduler.handle_exception(job, exception)
    puts &quot;job #{job.job_id} caught exception #{exception}&quot; 
    puts &quot;notifying hoptoad&quot; 
    HoptoadNotifier.notify(:error_class =&amp;gt; exception.class,
      :error_message =&amp;gt; exception.to_s)
  end

  scheduler.join

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

	&lt;h3&gt;Automating the sysadmin&lt;/h3&gt;


	&lt;p&gt;For this specific project, the (currently single) server is managed from start to finish with &lt;a href=&quot;http://wiki.opscode.com/display/chef/Chef+Solo&quot;&gt;chef-solo&lt;/a&gt; &#8211; and I must thank &lt;a href=&quot;http://vagrantup.com/&quot;&gt;vagrant&lt;/a&gt; too which helped me create/tweak the recipes at home.&lt;/p&gt;


	&lt;p&gt;Here&#8217;s what I have automated on this project:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;initial rvm bootstrapping&lt;/li&gt;
		&lt;li&gt;ssh configuration (port change, no root/password allowed etc)&lt;/li&gt;
		&lt;li&gt;firewall with csf&lt;/li&gt;
		&lt;li&gt;cloudkick agent deployment&lt;/li&gt;
		&lt;li&gt;god setup&lt;/li&gt;
		&lt;li&gt;mongodb compilation, configuration and launching&lt;/li&gt;
		&lt;li&gt;same for redis&lt;/li&gt;
		&lt;li&gt;same for jetty, solr and sunspot&lt;/li&gt;
		&lt;li&gt;nginx with passenger module&lt;/li&gt;
		&lt;li&gt;2 apps deployment (the front-end and the crawler)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Of course, all the god watches as well as all the config files (jetty configuration, mongodb etc, nginx config) are managed via erb and chef-solo.&lt;/p&gt;


	&lt;p&gt;This is the first time I&#8217;m not using Capistrano anymore for app deployment.&lt;/p&gt;


	&lt;p&gt;I cannot stress enough the usefulness of using both vagrant and chef-solo, especially for single-server deployment; I&#8217;m thinking about writing an e-book on that topic, as the learning curve has been fairly steep.&lt;/p&gt;


	&lt;p&gt;Having used both gives me for instance the ability to go from a new host (eg: Linode) to a fully configured stack and app with the necessary data restored from backup, in less than 15mn.&lt;/p&gt;


	&lt;p&gt;Voilà &#8211; stay tuned for more description of the underlying architecture of &lt;a href=&quot;http://www.hackerbooks.com&quot;&gt;HackerBooks.com&lt;/a&gt;!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2010-08-02:82</id>
    <published>2010-08-02T20:52:00Z</published>
    <updated>2010-08-02T21:11:08Z</updated>
    <category term="jruby"/>
    <category term="resque"/>
    <category term="windows"/>
    <link href="http://blog.logeek.fr/2010/8/2/on-jruby-resque-and-windows" rel="alternate" type="text/html"/>
    <title>On JRuby, Resque and Windows</title>
<content type="html">
            &lt;p&gt;&lt;a href=&quot;http://letsfreckle.com/?ref=244u8i3&quot;&gt;&lt;img src=&quot;http://affiliates.letsfreckle.com/af/freckle-125x125-1.png&quot; height=&quot;125&quot; width=&quot;125&quot;&gt;&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;During the past few months I&#8217;ve been working (by bursts) on a system that generates &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt; documents in interaction with a Windows-based &lt;span class=&quot;caps&quot;&gt;CRM&lt;/span&gt;. I thought it would be interesting to share some bits from a technical point of view.&lt;/p&gt;


	&lt;h2&gt;Constraints&lt;/h2&gt;


	&lt;ul&gt;
	&lt;li&gt;the in-house &lt;span class=&quot;caps&quot;&gt;CRM&lt;/span&gt; system is Windows-based&lt;/li&gt;
		&lt;li&gt;the &lt;span class=&quot;caps&quot;&gt;CRM&lt;/span&gt; comes with a 2000ish Java &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; allowing &lt;span class=&quot;caps&quot;&gt;CRUD&lt;/span&gt; operations on the data it hosts&lt;/li&gt;
		&lt;li&gt;some &lt;span class=&quot;caps&quot;&gt;XML&lt;/span&gt; data comes from a third-party system via &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h2&gt;The big picture&lt;/h2&gt;


After a bit of thinking, I came up with an architecture that mixes Ubuntu and Windows:
	&lt;ul&gt;
	&lt;li&gt;the front-end uses a very simple Apache + Passenger + Rails app that pushes incoming messages into Resque (also hosted on Ubuntu)&lt;/li&gt;
		&lt;li&gt;a JRuby/Windows &lt;a href=&quot;http://github.com/defunkt/resque&quot;&gt;Resque&lt;/a&gt; (~v1.5) worker grabs the messages, talks to the in-house &lt;span class=&quot;caps&quot;&gt;CRM&lt;/span&gt; using the Java &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;, and generates the &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt; using Prawn&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;This way I can benefit from the reliable, well-tested app hosting for the front-end using Passenger, while keeping Windows for the worker part that really requires Windows. Another nice point is that Resque allows to increase the number of workers if needed to scale as needed.&lt;/p&gt;


	&lt;p&gt;&lt;img src=&quot;http://blog.logeek.fr/assets/2010/8/2/resque_1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;


	&lt;h2&gt;Starting and keeping the Windows/JRuby worker up&lt;/h2&gt;


To keep the JRuby worker alive, I choosed to rely on &lt;a href=&quot;http://www.firedaemon.com/firedaemon-pro.php&quot;&gt;FireDaemon Pro&lt;/a&gt; and came up with a configuration that seems to work fairly well:
	&lt;ul&gt;
	&lt;li&gt;use C:\Program Files\jruby-1.4.0\bin\rake.bat as the executable&lt;/li&gt;
		&lt;li&gt;pass parameters &#8220;resque:work&#8221; &lt;/li&gt;
		&lt;li&gt;uncheck &#8220;enable event logging&#8221; in advanced (otherwise FireDaemon will consider a process that doesn&#8217;t log in the event log is dead)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;We&#8217;re also using Nagios on top of that to monitor what&#8217;s going on.&lt;/p&gt;


	&lt;h2&gt;Resque tweaks (for Windows)&lt;/h2&gt;


	&lt;p&gt;The version 1.5 of Resque relied on &#8216;ps&#8217; &#8211; I made a patch to rely on the built-in wmic.exe instead:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;wmic PROCESS get Processid,CommandLine /format:csv&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Full patch available &lt;a href=&quot;http://github.com/thbar/resque/commit/60f46077928e209989677a62bcf0857a52bafef5&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;


	&lt;h2&gt;Redis tweaks&lt;/h2&gt;


	&lt;ul&gt;
	&lt;li&gt;I switched Redis to &lt;a href=&quot;http://code.google.com/p/redis/wiki/AppendOnlyFileHowto&quot;&gt;append only file&lt;/a&gt; to ensure a reboot with pending messages won&#8217;t lose the messages (the performance is still largely good enough for our use case here)&lt;/li&gt;
		&lt;li&gt;I encoded the incoming &lt;span class=&quot;caps&quot;&gt;XML&lt;/span&gt; data (latin1) to base64 to avoid any deserialization issue with latin1 specific characters &#8211; the data is then iconv converted by the worker&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h2&gt;Conclusion&lt;/h2&gt;


	&lt;p&gt;Mixing stacks with different strengths can be useful to tackle a problem: here hosting the front-end on Windows would have been harder to get right in my opinion, while running the worker on Ubuntu was not possible in this case.&lt;/p&gt;


	&lt;p&gt;JRuby is great to build nice-to-use, well-tested APIs on top of factorish legacy Java APIs.&lt;/p&gt;


	&lt;p&gt;Prawn works equally well on JRuby and on &lt;span class=&quot;caps&quot;&gt;MRI&lt;/span&gt;.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2010-06-29:80</id>
    <published>2010-06-29T14:33:00Z</published>
    <updated>2010-06-29T14:34:12Z</updated>
    <category term="heroku"/>
    <category term="mongodb"/>
    <category term="mongohq"/>
    <category term="sinatra"/>
    <link href="http://blog.logeek.fr/2010/6/29/sinatra-heroku-mongodb-mongohq-notes" rel="alternate" type="text/html"/>
    <title>Notes from Sinatra, Heroku and MongoHQ deployment</title>
<content type="html">
            &lt;p&gt;For previous projects I never used Heroku, sometimes because I needed tools not available on the platform, other times because I had other options that were more interesting, price-wise.&lt;/p&gt;


	&lt;p&gt;My &lt;a href=&quot;http://www.toutpourmonipad.com&quot;&gt;last side-project&lt;/a&gt; sounded like a perfect fit to trial Heroku though. My conclusion as you may have guessed is that &lt;a href=&quot;http://www.heroku.com&quot;&gt;Heroku&lt;/a&gt; is really addictive and sweet to use, I encourage you to give it a try.&lt;/p&gt;


	&lt;p&gt;Disclaimer: this is &lt;strong&gt;not&lt;/strong&gt; a sponsored post for Heroku or MongoHQ &#8211; I&#8217;m just really happy with my current experience.&lt;/p&gt;


	&lt;p&gt;The site itself is &lt;a href=&quot;http://www.toutpourmonipad.com&quot;&gt;ToutPourMoniPad.com &#8211; housses et &amp;eacute;tuis pour iPad&lt;/a&gt; &#8211; a place that aggregates the cases, sleeves and protections we have found for the iPad, from vendors that ship to France without international shipping costs. Currently 100+ references and counting.&lt;/p&gt;


	&lt;p&gt;This post is not intended to be a nice layed-out article, rather a repository of links and code snippets I&#8217;ve been using and found practical.&lt;/p&gt;


	&lt;h3&gt;Overall impression&lt;/h3&gt;


	&lt;p&gt;The deployment was absolutely quick (less than 30mn apart from &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt; config) and flawless. The site is quite snappy as well, and the uptime has been wonderful.&lt;/p&gt;


	&lt;h3&gt;Technical overview&lt;/h3&gt;


	&lt;ul&gt;
	&lt;li&gt;I used the new &lt;a href=&quot;http://docs.heroku.com/stack&quot;&gt;Bamboo stack&lt;/a&gt; which allows Ruby 1.9.1&lt;/li&gt;
		&lt;li&gt;the app itself is based on &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; with &lt;span class=&quot;caps&quot;&gt;HAML&lt;/span&gt; and &lt;span class=&quot;caps&quot;&gt;SASS&lt;/span&gt;&lt;/li&gt;
		&lt;li&gt;I struggled a bit to get the &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt; to work but it went ok (see &lt;a href=&quot;http://docs.heroku.com/custom-domains#dns-setup&quot;&gt;docs here&lt;/a&gt;) &#8211; maybe I should have tried using Zerigo as suggested&lt;/li&gt;
		&lt;li&gt;I used Bundler for the gems, as &lt;a href=&quot;http://docs.heroku.com/bundler&quot;&gt;advised&lt;/a&gt; &lt;/li&gt;
		&lt;li&gt;the database started with Sqlite then we moved to &lt;a href=&quot;http://www.mongodb.org&quot;&gt;MongoDB&lt;/a&gt; 1.4, hosted on &lt;a href=&quot;https://mongohq.com&quot;&gt;MongoHQ&lt;/a&gt; (free plan)&lt;/li&gt;
		&lt;li&gt;I could not get exceptional to work (but that&#8217;s probably linked to how my app is configured), so I used custom email&lt;/li&gt;
		&lt;li&gt;the data aggregation system is built on TinyTL, &lt;a href=&quot;http://github.com/aeden/activewarehouse-etl&quot;&gt;ActiveWarehouse-ETL&lt;/a&gt; unreleased tiny brother, just like my &lt;a href=&quot;http://www.learnivore.com/&quot;&gt;ruby/rails screencasts aggregator&lt;/a&gt; previously&lt;/li&gt;
		&lt;li&gt;as I used a lot of data (html, xml, csv) caching locally, I used &lt;a href=&quot;http://github.com/mloughran/api_cache&quot;&gt;api-cache&lt;/a&gt; which works wonders&lt;/li&gt;
		&lt;li&gt;image resizing is done on my dev machine using imagemagick via command line, then pushed via git&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Exceptions notifications&lt;/h3&gt;


	&lt;p&gt;For some reason, I could not get Exceptional to work with my Sinatra setup. Here&#8217;s what I used (see below for email setup):&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;  error do
    e = request.env['sinatra.error']
    msg = ... 
    send_email('Exception!',msg)
    haml :'500'
  end
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Graphical tips&lt;/h3&gt;


	&lt;p&gt;I used &lt;a href=&quot;http://code.google.com/webfonts&quot;&gt;Google web fonts&lt;/a&gt;, &lt;a href=&quot;http://jquery.malsup.com/corner/&quot;&gt;jQuery corners&lt;/a&gt;, &lt;a href=&quot;http://compass-style.org/&quot;&gt;Compass&lt;/a&gt; and  &lt;a href=&quot;http://sass-lang.com/&quot;&gt;&lt;span class=&quot;caps&quot;&gt;SASS&lt;/span&gt;&lt;/a&gt; to finetune the blueprintcss-based layout.&lt;/p&gt;


	&lt;h3&gt;Notes on MongoDB&lt;/h3&gt;


	&lt;p&gt;This site relies heavily on a &lt;a href=&quot;http://amzn.to/90ReUU&quot; title=&quot;Extract-Transform-Load&quot;&gt;&lt;span class=&quot;caps&quot;&gt;ETL&lt;/span&gt;&lt;/a&gt; process in the background.&lt;/p&gt;


	&lt;p&gt;I&#8217;ve been playing a lot with &lt;span class=&quot;caps&quot;&gt;ETL&lt;/span&gt; and MongoDB recently and I find that this database is very convenient to munge data coming from different sources and in different formats (html, xml, csv).&lt;/p&gt;


	&lt;p&gt;Being able to add any number and variety of columns on the fly as you integrate the data sources is a big win: MongoDB is very interesting for that matter.&lt;/p&gt;


	&lt;p&gt;MongoHQ offers a free plan to get started so I took the plunge. The internal aws connection between Heroku and MongoHQ seems to work fast enough.&lt;/p&gt;


	&lt;h3&gt;Data caching&lt;/h3&gt;


	&lt;p&gt;&lt;a href=&quot;http://mloughran.github.com/api_cache/&quot;&gt;Api-cache&lt;/a&gt; is worth a look to cache just about anything. Quick snippet:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;require 'moneta/basic_file'

preprocess {
  # setup the cache store, here using file storage
  folder = File.dirname(__FILE__) + '/cache'
  FileUtils.mkdir(folder) unless File.exists?(folder)
  APICache.store = Moneta::BasicFile.new(:path =&amp;gt; folder)
}

def some_cached_operation(xxx...)
  APICache.get(the_key, :cache =&amp;gt; 60*60*24*10, :valid =&amp;gt; 60*60*24*4, :period =&amp;gt; 2) do
    process_some_heavy_download_with_polling_limits
  end
end

each_row { |row|
  row[:data] = some_cached_operation(xxx...)  
}&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Heroku tips&lt;/h3&gt;


	&lt;h4&gt;Migrating stack to &lt;span class=&quot;caps&quot;&gt;MRI 1&lt;/span&gt;.9.1&lt;/h4&gt;


	&lt;p&gt;I started with the wrong stack but this can be &lt;a href=&quot;http://docs.heroku.com/stack#migrating-between-stacks&quot;&gt;easily changed&lt;/a&gt;:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ heroku stack:migrate bamboo-mri-1.9.1
&lt;/code&gt;&lt;/pre&gt;

	&lt;h4&gt;Database setup&lt;/h4&gt;


	&lt;p&gt;By default the database uses a locally running MongoDB instance. I update the production database from the developer workstation (no heroku worker so far, although that may come), by using env vars for setting up the connection:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;if ENV['MONGOHQ_HOST']
  puts &quot;Running on MongoHQ&quot; 
  MongoMapper.connection = Mongo::Connection.new(ENV['MONGOHQ_HOST'], ENV['MONGOHQ_PORT'])
  MongoMapper.database = ENV['MONGOHQ_DATABASE']
  MongoMapper.database.authenticate(ENV['MONGOHQ_USER'],ENV['MONGOHQ_PASSWORD'])
else
  puts &quot;Using local database&quot; 
  MongoMapper.database = 'tout-pour-mon-ipad'
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;On traditional hosting you would put a config.yml path in the Capistrano shared folder, here you use &lt;a href=&quot;http://docs.heroku.com/config-vars&quot;&gt;vars that are managed by Heroku itself&lt;/a&gt;, and kept between deployments.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ heroku config:add MONGO_HOST=flame.mongohq.com ....&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;These variables are then set in the application &lt;span class=&quot;caps&quot;&gt;ENV&lt;/span&gt;.&lt;/p&gt;


	&lt;h4&gt;Email setup&lt;/h4&gt;


	&lt;p&gt;I use smtp for the contact form and simple exception notifications. As suggested by Heroku docs, I used &lt;a href=&quot;http://github.com/benprew/pony&quot;&gt;Pony&lt;/a&gt; and the &lt;a href=&quot;http://docs.heroku.com/sendgrid&quot;&gt;free sendgrid add-on&lt;/a&gt;.&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;require 'pony'
TPMI_SMTP_OPTIONS = {
    :address        =&amp;gt; &quot;smtp.sendgrid.net&quot;,
    :port           =&amp;gt; &quot;25&quot;,
    :authentication =&amp;gt; :plain,
    :user_name      =&amp;gt; ENV['SENDGRID_USERNAME'],
    :password       =&amp;gt; ENV['SENDGRID_PASSWORD'],
    :domain         =&amp;gt; ENV['SENDGRID_DOMAIN'],
}

def send_email(subject,html_body)
  Pony.mail(:to =&amp;gt; '...', :from =&amp;gt; '...',
    :subject =&amp;gt; subject,
    :html_body =&amp;gt; html_body,
    :via =&amp;gt; :smtp, :via_options =&amp;gt; TPMI_SMTP_OPTIONS)
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Conclusion&lt;/h3&gt;


	&lt;p&gt;I&#8217;m very happy with the setup and overall experience so far, and it has been very nice to have no hosting cost at all to trial the idea. I will definitely switch to a paid plan at some point, and will try to experiment with larger sites.&lt;/p&gt;


	&lt;p&gt;I intend to work more on the site and will blog about the technical aspects here, so stay tuned.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2010-02-14:78</id>
    <published>2010-02-14T10:46:00Z</published>
    <updated>2010-02-14T10:46:55Z</updated>
    <category term="duby"/>
    <category term="fssm"/>
    <category term="growl"/>
    <link href="http://blog.logeek.fr/2010/2/14/monitoring-file-changes-growl-notifications" rel="alternate" type="text/html"/>
    <title>Monitoring File Changes and Getting Notified via Growl</title>
<content type="html">
            &lt;p&gt;I&#8217;ve been experimenting with Duby for faster computations in &lt;a href=&quot;http://github.com/thbar/opaz-plugdk&quot;&gt;Opaz-PlugDK&lt;/a&gt;. I wanted to stay in the editor, triggering compilations with opt+s, and to be notified through Growl when something happens (in my case, compiling duby).&lt;/p&gt;


	&lt;p&gt;Here&#8217;s how to do it, using &lt;a href=&quot;http://github.com/ttilley/fssm&quot;&gt;&lt;span class=&quot;caps&quot;&gt;FSSM&lt;/span&gt;&lt;/a&gt; and &lt;a href=&quot;http://segment7.net/projects/ruby/growl/&quot;&gt;Ruby-Growl&lt;/a&gt;.&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
# all cross-platforms gems in theory
require 'fssm'
require 'ruby-growl'

growl = Growl.new &quot;localhost&quot;, &quot;ruby-growl&quot;, [&quot;Opaz-PlugDK&quot;]

FSSM.monitor('plugins', '**/*.duby') do
  update do |base, relative|
    in_folder(base) do
      growl.notify &quot;Opaz-PlugDK&quot;, relative, &quot;Compiling...&quot; 
      unless system(&quot;dubyc -java #{relative}&quot;)
        growl.notify &quot;Opaz-PlugDK&quot;, relative, &quot;Duby compile failed&quot; 
      else
        growl.notify &quot;Opaz-PlugDK&quot;, relative, &quot;OK!&quot; 
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;I really love Ruby for those things :)&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-11-27:75</id>
    <published>2009-11-27T07:55:00Z</published>
    <updated>2009-11-27T08:03:41Z</updated>
    <category term="calendar"/>
    <category term="ruby"/>
    <category term="tracking"/>
    <link href="http://blog.logeek.fr/2009/11/27/how-to-use-google-calendar-and-rufus-google-for-time-tracking" rel="alternate" type="text/html"/>
    <title>How to use Google Calendar and Rufus-Google for Basic Time Tracking</title>
<content type="html">
            &lt;p&gt;Today I&#8217;ll share what I use for some of my &lt;span class=&quot;caps&quot;&gt;DIY&lt;/span&gt; time-tracking when I do freelancing (sometimes I use other tools, depending on the customer).&lt;/p&gt;


Conventions I currently use:
	&lt;ul&gt;
	&lt;li&gt;one &lt;a href=&quot;http://www.google.com/calendar&quot;&gt;google calendar&lt;/a&gt; per customer (I name these &#8220;LoGeek CustomerName&#8221; to avoid conflicts with other calendars I would have shared with my customer &#8211; LoGeek is my company name)&lt;/li&gt;
		&lt;li&gt;event title stores the project name&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Code&lt;/h3&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
require 'rufus/gcal'

customer = &quot;LoGeek Acme Company&quot; 

Rufus::Google::Calendar.get_calendars( :account =&amp;gt; user_name, :password =&amp;gt; password)

calendar = get_calendars[customer]

# don't keep the default (low) :max_results or you'll probably miss events
events = calendar.events( &quot;max_results&quot; =&amp;gt; 1000, 
  &quot;start_min&quot; =&amp;gt; '2009-09-09T00:00:00',
  &quot;start_max&quot; =&amp;gt; '2009-11-25T11:00:00')

events.each do |e|
  duration = (e.end_time - e.start_time)/3600.0
  raise &quot;corrupt event&quot; if duration &amp;gt; 8

  print e.title # project name
  print &quot; - &quot; 
  print e.start_time.strftime(...)
  print e.end_time.strftime(...)
  print &quot; - &quot; 
  print duration
  print &quot; hours&quot; 
  puts

  total += duration
end

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

	&lt;p&gt;Once you have this data, you can either export it as &lt;span class=&quot;caps&quot;&gt;CSV&lt;/span&gt;, or reinject it into &lt;a href=&quot;http://prawn.majesticseacreature.com/&quot;&gt;Prawn&lt;/a&gt; or &lt;a href=&quot;http://stonecode.svnrepository.com/documatic/trac.cgi/wiki&quot;&gt;Documatic&lt;/a&gt; to create an invoice.&lt;/p&gt;


	&lt;h3&gt;Resources&lt;/h3&gt;


Known dependencies:
	&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://github.com/jmettraux/rufus-google&quot;&gt;rufus-google&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://github.com/jmettraux/rufus-verbs&quot;&gt;rufus-verbs&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://wiki.github.com/bct/atom-tools&quot;&gt;atom-tools&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


Documentation:
	&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://code.google.com/apis/calendar/developers_guide_protocol.html&quot;&gt;Google Calendar &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; developer guide&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Thanks!&lt;/h3&gt;


	&lt;p&gt;Thanks to &lt;a href=&quot;http://jmettraux.wordpress.com/&quot;&gt;John Mettraux&lt;/a&gt; for creating this useful libraries and many others!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-11-17:74</id>
    <published>2009-11-17T22:07:00Z</published>
    <updated>2009-11-17T22:09:40Z</updated>
    <category term="fun"/>
    <category term="jruby"/>
    <category term="vst"/>
    <link href="http://blog.logeek.fr/2009/11/17/how-to-prototype-vst-audio-plugins-with-jruby-and-java" rel="alternate" type="text/html"/>
    <title>Using JRuby to prototype VST plugins</title>
<content type="html">
            &lt;p&gt;In this article I&#8217;ll present some bits of &lt;a href=&quot;http://github.com/thbar/opaz-plugdk&quot;&gt;the work&lt;/a&gt; we&#8217;ve been doing daniel309 (author of &lt;a href=&quot;http://jvstwrapper.sourceforge.net/&quot;&gt;jVSTwRapper&lt;/a&gt;) and I, around the topic: &lt;strong&gt;is it possible to use Ruby to make it easier to prototype &lt;a href=&quot;http://en.wikipedia.org/wiki/Virtual_Studio_Technology&quot;&gt;&lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; plugins&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;It looks like yes &#8211; here&#8217;s the most simple example:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class RubyGain &amp;lt; OpazPlug
  plugin &quot;RubyGain&quot;, &quot;Opaz&quot;, &quot;LoGeek&quot; 
  can_do &quot;1in1out&quot;, &quot;plugAsChannelInsert&quot;, &quot;plugAsSend&quot; 
  unique_id &quot;RGaN&quot; 

  param :gain, &quot;Gain&quot;, 1.0, &quot;dB&quot; 

  def process(inputs, outputs, sampleFrames)
    inBuffer, outBuffer = inputs[0], outputs[0]
    for i in (0..sampleFrames-1)
      outBuffer[i] = inBuffer[i] * gain
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

We&#8217;ve been working iteratively using Git, and a few things came out:
	&lt;ul&gt;
	&lt;li&gt;a &lt;span class=&quot;caps&quot;&gt;DSL&lt;/span&gt; that allows to use declarative syntax for parameters handling (which usually involves a lot of boilerplate code)&lt;/li&gt;
		&lt;li&gt;the possibility to mix and match Java (for performance) and JRuby (for conciseness and declarativeness) as needed&lt;/li&gt;
		&lt;li&gt;a set of rake tasks to make it easier to bundle the plugins for the 3 platforms (Windows, Mac &lt;span class=&quot;caps&quot;&gt;OS X&lt;/span&gt;, Linux)&lt;/li&gt;
		&lt;li&gt;some simple but working, portable plugins&lt;/li&gt;
		&lt;li&gt;other promising things like &lt;span class=&quot;caps&quot;&gt;UI DSL&lt;/span&gt;, IRB console for live code hacking (!)&lt;/li&gt;
		&lt;li&gt;a lot of fun :)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;I&#8217;ll keep how things work internally for a later post (a lot of glueing), here I&#8217;ll present the basics and examples of plugins, but now I&#8217;m totally sold on the fact that JRuby is a great way to leverage the existing &lt;span class=&quot;caps&quot;&gt;JVM&lt;/span&gt; infrastructure&#8482;.&lt;/p&gt;


	&lt;p&gt;The overall performance is very slow compared to the Java or C++ equivalent, but it provides a very agile way to try out ideas before optimizing and picking either Java or C++ once the idea is mostly there.&lt;/p&gt;


	&lt;h3&gt;Some definitions to get started&lt;/h3&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Virtual_Studio_Technology&quot;&gt;&lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt;&lt;/a&gt; &#8211; one of the most common norms to create audio plugins, supported by hosts like &lt;a href=&quot;http://www.ableton.com&quot;&gt;Ableton Live&lt;/a&gt;, &lt;a href=&quot;http://www.cockos.com/reaper/&quot;&gt;Reaper&lt;/a&gt; and many more&lt;/li&gt;
		&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;VST SDK&lt;/span&gt; &#8211; a C++ &lt;span class=&quot;caps&quot;&gt;SDK&lt;/span&gt; provided by Steinberg to develop &lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; plugins&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://jvstwrapper.sourceforge.net&quot;&gt;jVSTwRapper&lt;/a&gt; &#8211; a Java &lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; framework, sitting on top of the &lt;span class=&quot;caps&quot;&gt;VST SDK&lt;/span&gt; and allowing to create plugins with Java in a portable fashion&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://github.com/thbar/opaz-plugdk&quot;&gt;Opaz-PlugDK&lt;/a&gt; &#8211; a JRuby &lt;span class=&quot;caps&quot;&gt;DSL&lt;/span&gt; + set of rake tasks, sitting on top of jVSTwRapper, allowing to create portable &lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; plugins with JRuby (and Java when better performance is needed)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;How to use the simple gain&lt;/h3&gt;


	&lt;p&gt;If you git clone the &lt;a href=&quot;http://github.com/thbar/opaz-plugdk&quot;&gt;opaz-plugdk repo&lt;/a&gt;, you&#8217;ll see that all plugins are under a plugins folder.&lt;/p&gt;


	&lt;p&gt;The following task (you&#8217;ll need javac in the path) will build the 3 versions of the plugins:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ rake package plugin=RubyGain
[snip]
$ ls plugins/RubyGain/build/
linux   osx   win
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;These three folders contain a RubyGain.vst folder ready to be added to the &lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; folder configured in your host.&lt;/p&gt;


	&lt;h3&gt;Improving the performance: HybridGain&lt;/h3&gt;


	&lt;p&gt;Our rake tasks automatically compile the .java files when rake package is called. Here&#8217;s a notably faster version of the previous gain &#8211; it mixes JRuby code (for the declaration and glue code) with Java (for &lt;span class=&quot;caps&quot;&gt;CPU&lt;/span&gt; intensive code).&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;HybridGain.rb&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;include_class Java::HybridGainTools

class HybridGain &amp;lt; OpazPlug
  plugin &quot;HybridGain&quot;, &quot;Opaz&quot;, &quot;LoGeek&quot; 
  can_do &quot;1in1out&quot;, &quot;plugAsChannelInsert&quot;, &quot;plugAsSend&quot; 
  unique_id 9876549

  param :gain, &quot;Gain&quot;, 1.0

  def process(inputs, outputs, sampleFrames)
    HybridGainTools.applyGain(inputs[0], outputs[0], sampleFrames, gain)
  end
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;HybridGain.java&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;// factor out computation-intensive stuff to java
public class HybridGainTools {
    public static void applyGain(float[] input, float[] output, int sampleFrames, float gain) {
        for (int i=0; i &amp;lt; sampleFrames; i++) {
            output[i] = gain * input[i];
        }
     }
}&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Thanks to JRuby, it&#8217;s quite easy to delegate the &lt;span class=&quot;caps&quot;&gt;CPU&lt;/span&gt; intensive stuff to Java &#8211; we can use the java classes straight from the ruby code.&lt;/p&gt;


	&lt;h3&gt;A more useful example: HybridFilta&lt;/h3&gt;


	&lt;p&gt;The hybrid filter is a LP/HP cut-off resonance filter (quite used in &#8220;filtered house&#8221; for instance). Although longer, the code is still an order of magnitude shorter than a full Java or full C++ equivalent plugin.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;HybridFilta.rb&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;include_class Java::FilterTools

class HybridFilta &amp;lt; OpazPlug
  plugin &quot;HybridFilta&quot;, &quot;Opaz&quot;, &quot;LoGeek&quot; 
  can_do &quot;1in1out&quot;, &quot;plugAsChannelInsert&quot;, &quot;plugAsSend&quot; 
  unique_id &quot;hflt&quot; 

  param :cut_off,   &quot;Cut Off&quot;,         1.0
  param :resonance, &quot;Resonance&quot;,       0.1
  param :mode,      &quot;Mode (LP or HP)&quot;,   0

  def filter
    @filter ||= FilterTools.new
  end

  def use_low_pass?
    mode &amp;lt; 0.5
  end

  def process(inputs, outputs, sampleFrames)
    filter.recomputeParameters(cut_off, resonance, use_low_pass?, sample_rate)
    filter.apply(inputs, outputs, sampleFrames)
  end
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;HybridFilta.java&lt;/strong&gt;&lt;/p&gt;


&lt;code&gt;&lt;pre&gt;public class FilterTools {
    float f,r,c,a1,a2,a3,b1,b2;

    public void recomputeParameters(float cutoff, float resonance, boolean lowPassMode, float sampleRate) {
        r = (1-resonance) * 10f; // r  = rez amount, from sqrt(2) to ~ 0.1
        if (r&amp;lt;0.1f) 
            r=0.1f;
        f = cutoff * sampleRate/4; // replace 44100 by context.samplerate later (or /4 ?)
        if (f&amp;lt;40f) 
            f=40f;

        if (lowPassMode) // lowpass mode
        {
            c = (float)(1.0 / Math.tan(Math.PI * f / sampleRate));

            a1 = 1.0f / ( 1.0f + r * c + c * c);
            a2 = 2* a1;
            a3 = a1;
            b1 = 2.0f * ( 1.0f - c*c) * a1;
            b2 = ( 1.0f - r * c + c * c) * a1;
        }
        else // hipass mode
        {
            c = (float)Math.tan(Math.PI * f / sampleRate);

            a1 = 1.0f / ( 1.0f + r * c + c * c);
            a2 = -2*a1;
            a3 = a1;
            b1 = 2.0f * ( c*c - 1.0f) * a1;
            b2 = ( 1.0f - r * c + c * c) * a1;
        }        
    }

    // history elements ih(x)_(y) : input sample n-y on voice x, oh(x)_y : output sample n-y on voice x
    float ih1_1=0,ih1_2=0,oh1_1=0,oh1_2=0;

    float computeFilter(float inp0,float inp1,float inp2,float outp1,float outp2)
    {
        return a1 * inp0 + a2 * inp1 + a3 * inp2 - b1*outp1 - b2*outp2;
    }

    public void apply(float[][] inputs, float[][] outputs, int sampleFrames) {
        // one channel only for the moment
        float[] input0 = inputs[0];
        float[] output0 = outputs[0];

         // TODO: move the whole following code to some more functional-styled code, if possible
        output0[0] = computeFilter( input0[0], ih1_1, ih1_2, oh1_1,oh1_2);
        output0[1] = computeFilter( input0[1], input0[0], ih1_1, output0[0], oh1_1);
        for (int sample=2;sample&amp;lt;sampleFrames;sample++)
            output0[sample] = a1*input0[sample] + a2*input0[sample-1] + a3*input0[sample-2] - b1*output0[sample-1] - b2*output0[sample-2];

        // save history
        ih1_1 = input0[sampleFrames-1];
        ih1_2 = input0[sampleFrames-2];
        oh1_1 = output0[sampleFrames-1];
        oh1_2 = output0[sampleFrames-2];
    }
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Going further: implementing &lt;span class=&quot;caps&quot;&gt;VST&lt;/span&gt; instruments&lt;/h3&gt;


	&lt;p&gt;Instruments are plugins that take midi in and generate audio (short version). We implemented a simple synth &lt;a href=&quot;http://github.com/thbar/opaz-plugdk/tree/master/plugins/HybridSynth/&quot;&gt;here&lt;/a&gt; &#8211; overall I find that Ruby is nice to handle the midi events and logic.&lt;/p&gt;


	&lt;h3&gt;Where do we go from here ?&lt;/h3&gt;


	&lt;p&gt;Well it&#8217;s been an interesting road :) Getting access to the libraries provided by both Java and Ruby, coding in JRuby as long as it&#8217;s beneficial and optimizing when needed is definitely interesting. And I wouldn&#8217;t have achieved anything if I hadn&#8217;t reused the existing infrastructure (jVSTwRapper makes all the heavy lifting here).&lt;/p&gt;


	&lt;p&gt;At some point I&#8217;ll write a sequel post to explain how things work underneath &#8211; I think we&#8217;re going to meet this kind of pattern a lot in the coming years, working either with JRuby or IronRuby.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-09-15:73</id>
    <published>2009-09-15T00:37:00Z</published>
    <updated>2009-09-15T00:37:45Z</updated>
    <category term="learnivore"/>
    <link href="http://blog.logeek.fr/2009/9/15/introducing-learnivore-com" rel="alternate" type="text/html"/>
    <title>Introducing Learnivore.com</title>
<content type="html">
            &lt;p&gt;&lt;a href=&quot;http://www.learnivore.com&quot;&gt;&lt;img src=&quot;http://blog.logeek.fr/assets/2009/9/14/learnivore-tiny.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://www.learnivore.com&quot;&gt;Learnivore.com&lt;/a&gt; is a side-project I&#8217;ve been working on during the past few months. It aggregates screencasts from a variety of Ruby/Rails publishers, like &lt;a href=&quot;http://www.pragprog.com/screencasts&quot;&gt;PragProgs&lt;/a&gt;, &lt;a href=&quot;http://railscasts.com/&quot;&gt;RailsCast&lt;/a&gt; or &lt;a href=&quot;http://www.peepcode.com&quot;&gt;PeepCode&lt;/a&gt; for instance.&lt;/p&gt;


	&lt;p&gt;It&#8217;s possible to filter between free and paid items, search in full-text, filter by tag or publisher (and later on by freshness). It has a &lt;a href=&quot;http://feeds.learnivore.com/learnivore&quot;&gt;&lt;span class=&quot;caps&quot;&gt;RSS&lt;/span&gt; feed&lt;/a&gt; and pushes updates (manually so far) on &lt;a href=&quot;http://www.twitter.com/learnivore&quot;&gt;@learnivore&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;I hope you&#8217;ll find this side-project useful! (suggestions &lt;a href=&quot;mailto:thibaut.barrere@gmail.com&quot;&gt;welcome&lt;/a&gt;).&lt;/p&gt;


	&lt;h3&gt;Looking back&lt;/h3&gt;


	&lt;p&gt;Working on this project on the side has been an amazingly rewarding learning experience. It also brought me money &#8211; indirectly, that is, by giving me ideas of solutions or code I&#8217;ve been then able to reuse for my customers.&lt;/p&gt;


	&lt;p&gt;I went through Merb first, then switched to Ramaze, with the idea to save &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt; on my servers. Last year I didn&#8217;t like Ramaze that much, but updating a couple of months ago really changed the deal. I deployed using thin (I would use passenger today).&lt;/p&gt;


	&lt;p&gt;I learned to use ThinkingSphinx for indexing (a powerful tool, if you ask me).&lt;/p&gt;


	&lt;p&gt;Before that, I deployed CouchDB on Solaris for &lt;strong&gt;nothing&lt;/strong&gt;, because I thought it would be useful to check if deployment would work prior to investing too much development time. It didn&#8217;t work out so easily :) I stopped using it (for that project) but it gave me a whole new perspective on data processing, and ideas that basically bring me interesting gigs and fun customers right now.&lt;/p&gt;


	&lt;p&gt;Data processing was also interesting: I wrote a lightweight &lt;span class=&quot;caps&quot;&gt;ETL&lt;/span&gt; called TinyTL in the process, which got reused in a couple of systems I built for customers. I used a mix of &lt;span class=&quot;caps&quot;&gt;XML&lt;/span&gt; and &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; parsing that feed TinyTL. I implemented a quick tag conforming system to ensure the presented tags would be mostly clean.&lt;/p&gt;


	&lt;p&gt;&lt;span class=&quot;caps&quot;&gt;RSS&lt;/span&gt; was a bit tricky to implement as I wanted to ensure the freshest items would stay on top. I had to grab the update dates on each screencast (either using xml or scrapped html) to then be able to sort by update date.&lt;/p&gt;


	&lt;p&gt;Anyway &#8211; I really encourage other rubyists to start side-projects (this one wasn&#8217;t the first and isn&#8217;t the last either) because it&#8217;s possible to achieve rewarding things with some hours now and then!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-07-02:72</id>
    <published>2009-07-02T10:09:00Z</published>
    <updated>2009-07-02T10:09:35Z</updated>
    <category term="ruby"/>
    <category term="tokens"/>
    <category term="unique"/>
    <link href="http://blog.logeek.fr/2009/7/2/creating-small-unique-tokens-in-ruby" rel="alternate" type="text/html"/>
    <title>How to create small, unique tokens in Ruby</title>
<content type="html">
            &lt;p&gt;A while back I was looking for a way to generate short unique tokens to be used as authorization tokens in urls. I did some research and &lt;a href=&quot;http://groups.google.com/group/ruby-talk-google/browse_thread/thread/f3dd69fe5ca37ef8/89cc29d64e9b3532&quot;&gt;asked on ruby-talk&lt;/a&gt;. Here&#8217;s a summary of what I found.&lt;/p&gt;


	&lt;p&gt;Note that most of these algorithms can be translated to C#, Java, IronRuby/JRuby or your language and platform of choice.&lt;/p&gt;


	&lt;h3&gt;My choice: Dave Bass&#8217;s rand().to_s() trick&lt;/h3&gt;


	&lt;p&gt;Dave Bass proposed this which I picked up for my implementation (here for an 8-chars token):&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; rand(36**8).to_s(36)
=&amp;gt; &quot;uur0cj2h&quot;&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The result can be used as an url; pretty neat. It relies on the ability of Fixnum to translate itself to a string in a given base (here we use base 36, which I rarely use!).&lt;/p&gt;


	&lt;p&gt;This can be used in an ActiveRecord model for instance:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class Customer &amp;lt; ActiveRecord::Base
  validates_presence_of :access_token
  validates_uniqueness_of :access_token

protected
  def before_validation_on_create
    self.access_token = rand(36**8).to_s(36) if self.new_record? and self.access_token.nil?
  end 
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Jamie Macey feedback&lt;/h3&gt;


	&lt;p&gt;Jamie proposed several options. First, use a substring of &lt;span class=&quot;caps&quot;&gt;SHA1&lt;/span&gt;, which is &#8220;small 
enough to be usable, but still pseudo-random enough for temporary 
tokens to not be guessable&#8221; :&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; require 'digest'
=&amp;gt; []
&amp;gt;&amp;gt; Digest::SHA1.hexdigest(&quot;some-random-string&quot;)[8..16]
=&amp;gt; &quot;2ebe5597f&quot;&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Another technique is to rely on ActiveSupport SecureRandom, and tweak the results a bit to get a url-friendly token. Here&#8217;s my final bit of code with this method:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; require 'active_support'
=&amp;gt; []
&amp;gt;&amp;gt; ActiveSupport::SecureRandom.base64(8).gsub(&quot;/&quot;,&quot;_&quot;).gsub(/=+$/,&quot;&quot;)
=&amp;gt; &quot;AEWQyovNFo0&quot; 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Jamie&#8217;s last proposal is &#8220;not terribly robust, but functional&#8221; :&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; chars = ['A'..'Z', 'a'..'z', '0'..'9'].map{|r|r.to_a}.flatten
&amp;gt;&amp;gt; Array.new(6).map{chars[rand(chars.size)]}.join
=&amp;gt; &quot;g64wdR&quot;&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Ryan Davis: let&#8217;s put more words in it&lt;/h3&gt;


	&lt;p&gt;Ryan proposed something totally different:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; words = File.read(&quot;/usr/share/dict/words&quot;).split; max = words.size
=&amp;gt; 234936
&amp;gt;&amp;gt; &quot;#{words[rand(max)]}-#{words[rand(max)]}&quot; 
=&amp;gt; &quot;loquat-motorial&quot;&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The idea is interesting. You&#8217;ll need to ensure your dictionary doesn&#8217;t contain insults, if your user base cares about that :)&lt;/p&gt;


	&lt;p&gt;Another option Ryan got from Eric is to use the quite unknown bubble-babble to make hash values more readable:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; require 'digest/bubblebabble'
=&amp;gt; true
Digest.bubblebabble(Digest::SHA1::hexdigest(&quot;random string&quot;)[8..12]) 
=&amp;gt; &quot;xesik-fymak-gunax&quot;&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;John Mettraux&#8217;s Rufus Mnemo&lt;/h3&gt;


	&lt;p&gt;&lt;a href=&quot;http://github.com/jmettraux/rufus-mnemo/tree/master&quot;&gt;rufus-mnemo&lt;/a&gt; has the ability to translate an integer into easy-to-remember words, based on Japanese syllabes:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;gt;&amp;gt; require 'rufus/mnemo'
&amp;gt;&amp;gt; s = Rufus::Mnemo::from_integer rand(8**5)
=&amp;gt; &quot;bisoshi&quot; 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Pretty neat! The generated words are &#8220;easy to the latin ears&#8221;. Take care of the meaning if your users are Japanese-speaking.&lt;/p&gt;


	&lt;h3&gt;If you use &lt;span class=&quot;caps&quot;&gt;UUID&lt;/span&gt; &#8211; be careful with Solaris zones!&lt;/h3&gt;


	&lt;p&gt;If you deploy to Solaris zones, be careful about that: some other libraries I had a look at, like the very nice &lt;a href=&quot;http://github.com/assaf/uuid/tree/master&quot;&gt;assaf&#8217;s uuid&lt;/a&gt;, are relying on &lt;a href=&quot;http://codeforpeople.com/lib/ruby/macaddr/&quot;&gt;macaddr&lt;/a&gt;,   which doesn&#8217;t seem to work on Solaris Zone.&lt;/p&gt;


	&lt;p&gt;See &lt;a href=&quot;http://discuss.joyent.com/viewtopic.php?id=24470&quot;&gt;here&lt;/a&gt;, &lt;a href=&quot;http://blog.dberg.org/2009/02/uuid-with-rubyjruby-on-solaris.html&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://rubyforge.org/tracker/index.php?func=detail&amp;amp;#38;aid=5220&amp;amp;#38;group_id=914&amp;amp;#38;atid=3591&quot;&gt;here&lt;/a&gt; for solutions if you&#8217;re in trouble.&lt;/p&gt;


	&lt;h3&gt;Have you got more ?&lt;/h3&gt;


	&lt;p&gt;Please share using the comments system.&lt;/p&gt;


	&lt;p&gt;cheers!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-03-04:69</id>
    <published>2009-03-04T10:44:00Z</published>
    <updated>2009-03-04T10:50:27Z</updated>
    <category term="ruby"/>
    <link href="http://blog.logeek.fr/2009/3/4/detecting-which-ruby-interpreter-is-running-jruby-ironruby" rel="alternate" type="text/html"/>
    <title>Detecting Which Ruby Interpreter is Running (JRuby, IronRuby)</title>
<content type="html">
            &lt;p&gt;I&#8217;m currently using a mix of Ruby interpreters for building a system. For instance, I use JRuby for Celerity, IronRuby for Windows Forms (on Mono) and &lt;span class=&quot;caps&quot;&gt;MRI&lt;/span&gt; for other things.&lt;/p&gt;


	&lt;p&gt;To ensure I don&#8217;t mess things up, I came up with some tiny guards to get a clear message when I&#8217;m not using the interpreter I should be using. I&#8217;m sharing these here in case it&#8217;s useful to someone else.&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
# JRuby
abort $0 + &quot; requires JRuby&quot; unless defined?(RUBY_ENGINE) &#38;&#38; RUBY_ENGINE == &quot;jruby&quot; 

# IronRuby
abort($0 + &quot; requires IronRuby&quot;) unless defined?(RUBY_ENGINE) &#38;&#38; RUBY_ENGINE == &quot;ironruby&quot; 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Note that &lt;span class=&quot;caps&quot;&gt;RUBY&lt;/span&gt;_ENGINE doesn&#8217;t seem to be defined under &lt;span class=&quot;caps&quot;&gt;MRI&lt;/span&gt;.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2009-01-28:67</id>
    <published>2009-01-28T22:01:00Z</published>
    <updated>2009-01-30T12:33:48Z</updated>
    <category term="edge"/>
    <category term="howto"/>
    <category term="rails"/>
    <link href="http://blog.logeek.fr/2009/1/28/how-to-create-a-rails-edge-application" rel="alternate" type="text/html"/>
    <title>How to create an empty Rails Edge application</title>
<content type="html">
            &lt;p&gt;I&#8217;ve been looking for this one a bit, so I thought I would share it here. As I wanted to start using the &lt;a href=&quot;http://m.onkey.org/2008/12/4/rails-templates&quot;&gt;Rails 2.3+ application template feature&lt;/a&gt;, I thought I would create a script that is able to create an empty Rails edge application.&lt;/p&gt;


	&lt;p&gt;I found most of the required information &lt;a href=&quot;http://ariejan.net/2009/01/04/how-to-start-a-rails-edge-app-the-easy-way/&quot;&gt;here&lt;/a&gt;. Here it is packaged as a reusable ruby script:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
#!/usr/local/bin/ruby

require 'fileutils'

def launch(cmd); puts cmd; throw &quot;Error!&quot; unless system(cmd); end

abort &quot;Syntax: my_rails app_name&quot; unless (app_name = ARGV.first)
abort &quot;Folder #{app_name} already there! Remove it first.&quot; if File.exists?(app_name)

launch &quot;mkdir -p #{app_name}/vendor&quot; 

FileUtils.chdir app_name

launch &quot;git clone git://github.com/rails/rails.git vendor/rails&quot; 
launch &quot;ruby vendor/rails/railties/bin/rails .&quot; 

FileUtils.chdir '..'
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now I can enjoy the application template!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2008-12-23:66</id>
    <published>2008-12-23T10:22:00Z</published>
    <updated>2008-12-23T10:25:50Z</updated>
    <category term="freeze"/>
    <category term="gems"/>
    <category term="howto"/>
    <category term="rails"/>
    <link href="http://blog.logeek.fr/2008/12/23/how-to-freeze-gems-with-rails-2-1" rel="alternate" type="text/html"/>
    <title>How to Freeze Gems with Rails &gt;= 2.1</title>
<content type="html">
            &lt;p&gt;I spent a couple of minutes trying to figure out how to &#8216;freeze&#8217; gems with a recent Rails app (2.2.2) I&#8217;m developing. A bunch of git repositories have this vendor/gems folder a bit like in Merb, so I was pretty sure something had changed in Rails itself. I finally found this &lt;a href=&quot;http://railscasts.com/episodes/110-gem-dependencies&quot;&gt;screencast&lt;/a&gt;. I&#8217;m sharing it again as I spent a bit of time already myself.&lt;/p&gt;


	&lt;p&gt;Turns out it&#8217;s pretty well integrated. First edit your environment.rb to specify which gem you want to register as a dependency:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
Rails::Initializer.run do |config|
  # ...
  # Specify gems that this application depends on. 
  # They can then be installed with &quot;rake gems:install&quot; on new installations.
  # You have to specify the :lib option for libraries, where the Gem name (sqlite3-ruby) differs from the file itself (sqlite3)
  config.gem &quot;fastercsv&quot;, :version =&amp;gt; '1.4.0'
  # ...
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;If you want to freeze the registered gems automatically under vendor/gems, just do:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
rake gems:unpack
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;There is also the possibility to install them on any machine using this:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
rake gems:install
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Generally, I prefer to keep things in my repository, as I don&#8217;t want my deployment to fail because of network or routing issues.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2008-12-01:65</id>
    <published>2008-12-01T17:30:00Z</published>
    <updated>2008-12-01T17:31:53Z</updated>
    <category term="ironpython"/>
    <category term="ironruby"/>
    <category term="testing"/>
    <link href="http://blog.logeek.fr/2008/12/1/thoughts-on-ironruby-and-dotnet-testing" rel="alternate" type="text/html"/>
    <title>Thoughts on IronRuby and .Net Testing</title>
<content type="html">
            &lt;p&gt;Today I wrote some &lt;a href=&quot;http://rubyforge.org/projects/ironruby/&quot;&gt;IronRuby&lt;/a&gt; tests targetting C# code (using both &lt;a href=&quot;http://github.com/jflam/mspec/tree/master&quot;&gt;MSpec&lt;/a&gt; and the bundled Test::Unit). Things went out absolutely well.&lt;/p&gt;


	&lt;p&gt;While I won&#8217;t share the code in question yet, here&#8217;s a working example provided by John Lam on his blog a while back. This morning I decided I would start running it myself, to see how it goes. You&#8217;ll have to &lt;a href=&quot;http://peepcode.com/products/git&quot;&gt;git&lt;/a&gt; clone &lt;a href=&quot;http://github.com/jflam/mspec/tree/master&quot;&gt;MSpec&lt;/a&gt; under the gems folder to get it working.&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
$LOAD_PATH &amp;lt;&amp;lt; File.dirname(__FILE__) + &quot;/gems/mspec/lib&quot; 
require 'mspec'

require 'mscorlib'
include System::Collections

describe &quot;.Net Stack&quot; do
  it &quot;creates an instance with zero elements&quot; do
    Stack.new.count.should == 0
  end

  it &quot;contains one element after push&quot; do
    s = Stack.new
    s.push &quot;bob&quot; 
    s.count.should == 1
  end

  it &quot;let us peek the element pushed&quot; do
    s = Stack.new
    s.push &quot;bob&quot; 
    s.peek.should == &quot;bb&quot; 
    s.count.should == 1
  end
end
&lt;/pre&gt;&lt;/code&gt;

	&lt;p&gt;Notice how it mixes IronRuby code with the Stack &lt;span class=&quot;caps&quot;&gt;CLR&lt;/span&gt; class.&lt;/p&gt;


	&lt;p&gt;My belief is that IronRuby and IronPython are going to &lt;strong&gt;deeply&lt;/strong&gt; change the way people work with .Net.&lt;/p&gt;


	&lt;p&gt;I have the same feeling I had in 2001, when I started using C#. That feeling was: C++ is going to be used at the driver/infrastructure level, and most application code will move away to C#. Today it&#8217;s very clear to me that the same thing is going to happen: IronRuby or IronPython will be used for most of the daily code, while C# will gradually serve as an infrastructure.&lt;/p&gt;


	&lt;p&gt;As well my feeling is that given the dynamic nature of Ruby, a lot of the features of &lt;a href=&quot;http://www.mbunit.com/&quot;&gt;MbUnit&lt;/a&gt; or &lt;a href=&quot;http://www.nunit.com&quot;&gt;NUnit&lt;/a&gt; (such as combinatorial tests or row tests, see &lt;a href=&quot;http://weblogs.asp.net/astopford/archive/2008/08/29/mbunit-combinatorial-test.aspx&quot;&gt;here&lt;/a&gt; for instance) are going to be easy to reproduce by yourself, as you need it.&lt;/p&gt;


	&lt;p&gt;Here&#8217;s an imaginary example so you get the idea:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;
$LOAD_PATH &amp;lt;&amp;lt; File.dirname(__FILE__) + &quot;/gems/mspec/lib&quot; 
require 'mspec'

describe &quot;SupaComputa&quot; do

  (1..400).each do |value|
    it &quot;computes 2^#{value} without blowing&quot; do
      SupaComputa.new.pow(2,value)
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;It doesn&#8217;t remove the need for testing frameworks like MbUnit of course, but this is definitely going to change the way we test.&lt;/p&gt;


	&lt;p&gt;If you made it to the end of this post, be sure to check out &lt;a href=&quot;http://rubyconf2008.confreaks.com/ironruby.html&quot;&gt;John Lam&#8217;s talk at RubyConf 2008&lt;/a&gt; (should be embedded below as well).&lt;/p&gt;


&amp;lt;embed src=&quot;http://rubyconf2008.confreaks.com/player.swf&quot; height=&quot;260&quot; width=&quot;640&quot; /&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2008-11-30:64</id>
    <published>2008-11-30T23:19:00Z</published>
    <updated>2008-11-30T23:26:30Z</updated>
    <category term="bookmarks"/>
    <category term="delicious"/>
    <category term="json"/>
    <category term="ruby"/>
    <link href="http://blog.logeek.fr/2008/11/30/how-to-retrieve-delicious-tags-and-number-of-bookmarks-for-a-given-url" rel="alternate" type="text/html"/>
    <title>How to Retrieve Delicious Tags and Number of Bookmarks for a Given Url</title>
<content type="html">
            &lt;p&gt;In case that&#8217;s of any use to you, here&#8217;s how you can query &lt;a href=&quot;http://delicious.com/&quot;&gt;Delicious&lt;/a&gt; if you want to retrieve informations about a specific url (ruby code, translate to whatever works for you):&lt;/p&gt;


&lt;pre&gt;
&lt;code class=&quot;ruby&quot;&gt;
require 'rubygems'
require 'json'
require 'open-uri'
require 'digest'

def get_tags(url)
  hash = Digest::MD5.hexdigest(url)
  open(&quot;http://badges.del.icio.us/feeds/json/url/data?hash=#{hash}&quot;) do |input|
    return JSON.parse(input.read)
  end
end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Now let&#8217;s put this code at work:&lt;/p&gt;


&lt;pre&gt;
&lt;code class=&quot;ruby&quot;&gt;
require 'pp'

pp get_tags(&quot;http://blog.logeek.fr/2008/1/19/a-beginner-s-guide-to-datawarehouse&quot;)
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Here&#8217;s the output:&lt;/p&gt;


&lt;pre&gt;
&lt;code class=&quot;ruby&quot;&gt;
[{&quot;title&quot;=&amp;gt;&quot;A Beginner&amp;amp;#039;s Guide to Datawarehouse&quot;,
  &quot;hash&quot;=&amp;gt;&quot;1bd588b55d27f3c3faa60b048632a200&quot;,
  &quot;url&quot;=&amp;gt;&quot;http://blog.logeek.fr/2008/1/19/a-beginner-s-guide-to-datawarehouse&quot;,
  &quot;total_posts&quot;=&amp;gt;82,
  &quot;top_tags&quot;=&amp;gt;
   {&quot;rails&quot;=&amp;gt;6,
    &quot;datawarehousing&quot;=&amp;gt;22,
    &quot;activewarehouse&quot;=&amp;gt;11,
    &quot;datawarehouse&quot;=&amp;gt;55,
    &quot;db&quot;=&amp;gt;8,
    &quot;ruby&quot;=&amp;gt;13,
    &quot;datamining&quot;=&amp;gt;19,
    &quot;database&quot;=&amp;gt;32,
    &quot;data&quot;=&amp;gt;13,
    &quot;guide&quot;=&amp;gt;15}}]
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;So here my &lt;a href=&quot;http://blog.logeek.fr/2008/1/19/a-beginner-s-guide-to-datawarehouse&quot;&gt;datawarehouse article&lt;/a&gt; has been bookmarked 82 times, most of the time with the keywords &#8216;datawarehouse&#8217; and &#8216;database&#8217;.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2008-11-06:63</id>
    <published>2008-11-06T17:10:00Z</published>
    <updated>2008-11-06T17:32:38Z</updated>
    <category term="readline"/>
    <category term="ruby"/>
    <category term="tips"/>
    <link href="http://blog.logeek.fr/2008/11/6/how-to-fix-symbol-not-found-rl-filename-completion-function" rel="alternate" type="text/html"/>
    <title>Fixing Symbol not found _rl_filename_completion_function</title>
<content type="html">
            &lt;p&gt;Not a full article this time but rather a tip that will help poor souls looking for their salvation on Google.&lt;/p&gt;


	&lt;p&gt;By the way I think I will divide this blog in two sections: articles on one side, hints and tips on the other. &lt;a href=&quot;mailto:thibaut.barrere@gmail.com&quot;&gt;I&#8217;d love to hear your opinion on that&lt;/a&gt; &#8211; two separate feeds or just two tabs ?&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/fastjack/&quot;&gt;&lt;img src=&quot;http://blog.logeek.fr/assets/2008/11/6/bug_thumb.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Full credit goes to &lt;a href=&quot;http://matthewhutchinson.net/&quot;&gt;Matthew Hutchinson&lt;/a&gt; who wrote this &lt;a href=&quot;http://hivelogic.com/articles/2007/02/updates-to-the-rails-os-x-build-instructions#comment_1262&quot;&gt;comment&lt;/a&gt;. I believe the article will make it easier for people to find the solution.&lt;/p&gt;


	&lt;p&gt;Some context: I&#8217;m working on a &lt;a href=&quot;http://www.merbivore.com/&quot;&gt;Merb&lt;/a&gt; + &lt;a href=&quot;http://incubator.apache.org/couchdb/&quot;&gt;CouchDb&lt;/a&gt; application I will introduce to you in the next few weeks. While installing a dependency with &lt;a href=&quot;http://github.com/wycats/thor/tree/master&quot;&gt;Thor&lt;/a&gt;, I got the following error:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
bash-3.2$ thor merb:gem:install dm-more
dyld: NSLinkModule() error
dyld: Symbol not found: _rl_filename_completion_function
  Referenced from: /usr/local/lib/ruby/1.8/i686-darwin9.5.0/readline.bundle
  Expected in: flat namespace

Trace/BPT trap
bash-3.2$
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;I had recompiled a version of Ruby (switching from 1.8.6 patchlevel 0 to patchlevel 111) and got this error a few days later.&lt;/p&gt;


	&lt;p&gt;&#8220;rl&#8221; and &#8220;readline&#8221; give us clues about what&#8217;s happening &#8211; I googled and found Matthew&#8217;s tip. I went back to where I had downloaded the Ruby source and did:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
cd /usr/local/src/ruby-1.8.6-p111/ext/readline
ruby extconf.rb
make clean # possibly needed
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;And this reinstalled the readline bundle:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
/usr/bin/install -c -m 0755 readline.bundle /usr/local/lib/ruby/site_ruby/1.8/i686-darwin9.5.0
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Then back to thor, everything was back to normal:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
bash-3.2$ thor merb:gem:install dm-more
Installing dm-more...
Successfully installed dm-couchdb-adapter-0.9.6
Successfully installed dm-rest-adapter-0.9.6
... snip ...
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Hope this will be useful to some of you!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.logeek.fr/">
    <author>
      <name>Thibaut</name>
    </author>
    <id>tag:blog.logeek.fr,2008-10-21:61</id>
    <published>2008-10-21T08:40:00Z</published>
    <updated>2008-10-21T08:42:54Z</updated>
    <category term="css"/>
    <category term="gradient"/>
    <category term="rmagic"/>
    <category term="ruby"/>
    <link href="http://blog.logeek.fr/2008/10/21/generating-gradients-for-your-css-using-rmagick" rel="alternate" type="text/html"/>
    <title>How to Generate a Gradient for your CSS using RMagick</title>
<content type="html">
            &lt;p&gt;I find it&#8217;s painful to go back to Photoshop just to generate a gradient, especially when I&#8217;m modifying the &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; at the very same time to find the right colors.&lt;/p&gt;


	&lt;p&gt;Here&#8217;s the Rake task I use now to quickly generate a gradient background:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;desc &quot;Generate the gradient background&quot; 
task :gradient do
  require 'rmagick'
  include Magick

  WIDTH = 1
  HEIGHT = 154

  gradient = GradientFill.new(0, 0, WIDTH, 0, &quot;#84704E&quot;, &quot;#99906E&quot;)
  image = Image.new(WIDTH, HEIGHT, gradient)
  image.write(&quot;images/gradient.jpg&quot;)
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;I put this code under version control, in a Rakefile placed inside the Mephisto (in this case) theme itself.&lt;/p&gt;


	&lt;p&gt;The corresponding &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; contains:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;body {
    padding: 0em;
    margin: 0em;
    color: #333333;
    font-family:&quot;Helvetica Neue&quot;,Helvetica,Arial,Sans-Serif;
    background:#99906E url(../images/gradient.jpg) repeat-x;
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;If I need to &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt; things out, it&#8217;s possible to extract the body background color from the &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt;, to use it in the GradientFill.new call:&lt;/p&gt;


&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;background_color = IO.read(&quot;stylesheets/main.css&quot;).grep(/background:#([\S]+)/) { $1 }
gradient = GradientFill.new(0, 0, WIDTH, 0, &quot;#84704E&quot;, background_color)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Alternatively if you generate your &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; with &lt;a href=&quot;http://ajaxian.com/archives/sass-y-dynamic-css&quot;&gt;&lt;span class=&quot;caps&quot;&gt;SASS&lt;/span&gt;&lt;/a&gt; you could tap into the &lt;span class=&quot;caps&quot;&gt;SASS&lt;/span&gt; source directly.&lt;/p&gt;


	&lt;p&gt;That&#8217;s it. I&#8217;ve used this on the blog you&#8217;re currently reading and also on my &lt;a href=&quot;http://evolvingworker.com&quot;&gt;mind-mapping, self-improvement and useful software blog&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;I&#8217;ll experiment more with shadows and other little customizations when required.&lt;/p&gt;


	&lt;h3&gt;RMagick tutorials&lt;/h3&gt;


	&lt;p&gt;I found the following links helpful while looking for information to do that:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://rmagick.rubyforge.org/src_over.html&quot;&gt;Alpha compositing part 1&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://rmagick.rubyforge.org/web2/web2.html&quot;&gt;Alpha compositing part 2&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://schf.uc.org/articles/2006/10/18/render-greatlooking-collages-with-ruby-and-rmagick&quot;&gt;Render great looking collages with ruby and rmagick&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;http://www.rubyfrance.org/documentations/faire-un-logo-avec-rmagick/&quot;&gt;Faire un logo avec RMagick&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
          </content>  </entry>
</feed>
