27/08/10

Rails3 on joyent shared (rvm way) - Followup

In my last post I wrote about installing rails3 on ruby 1.8.7 on a joyent shared accelerator. After finishing up my install an restarting my new app/toy/site I noticed a couple things.

On Ruby versions

Ruby 1.8.7 works whether its p299 or p302, the thing that fails is net-ssh with it's weird relocation error.

Ruby 1.9.x doesnt compile, period.

On deploying the production environment


Joyent recommends 2 ways of setting up a production environment on its shared environment:
  • using mongrel
  • using lighttpd
Now, I played with lighttpd back in v2.x, and found it hard to keep running, si I wanted to try mongrel.

Plainly put, I am out of luck: mongrel even 1.2.0pre doesn't work well with rails3. it seems it integrates at a low level in the core rails stack. Except that there were massive changes in the rails3 stack with the inclusion of rack as the core of the stack. As a result some file/class called "dispatcher" is missing and trying the normal mongrel_rails start command just fails with the error shown below:
jean@xps:~/dev/rails/giftr$ mongrel_rails start
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
/home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `require': no such file to load -- dispatcher (LoadError)
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `block in require'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:225:in `block in load_dependency'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:591:in `new_constants_in'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:225:in `load_dependency'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `require'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/rails.rb:148:in `rails'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:116:in `block (2 levels) in run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:149:in `call'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:149:in `listener'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:102:in `block in run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:50:in `call'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:50:in `initialize'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:86:in `new'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:86:in `run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/command.rb:210:in `run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:282:in `<top (required)>'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/bin/mongrel_rails:19:in `load'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/bin/mongrel_rails:19:in `<main>'
This problem is not joyent specific though (and the captured output comes from my dev station hence the ruby 1.9.x, but I have the exact same stack trace on the joyent shared.

Since mongrel is trying to keep backward compatibility with all versions of rails we are in a tight spot. Defects already exist about this in both rails and mongrel, but no solution in sight for the moment (and this is way over my head for now) :
At the same time, using rails server works like a charm starting up mongrel. I am not sure what the difference is to be honnest.

The deploy solution which works for me

In the meantime, starting rails server seems to work fine.Therefore, here is what I did, found a stop script somewhere on the net (sorry I don't remember where) which works fine:
#!/usr/bin/env bash 
EXPECTED_ARGS=1
E_BADARGS=65

if [ $# -ne $EXPECTED_ARGS ]
then
  echo "Usage: `basename $0` {pid_file}"
  exit $E_BADARGS
fi

if [ -f $1 ]
then
  cp $1 $1.stopping
  cat $1 | xargs kill
  mv $1.stopping $1.stopped 
else
  echo "Usage: `basename $0` {pid_file}"
  exit $E_BADARGS
fi
combined with a tweaked deploy.rb recipe to both link the bundler gem folder and the database config file, and to use my scripts :
set :application, "giftr"
set :repository,  "git@github.com:jeantil/giftr.git"

set :scm, :git # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

set :deploy_to, "/users/home/#{user}/domains/giftr.byjean.eu/apps/giftr"

#role :web, "#{host}.joyent.us"                          # Your HTTP server, Apache/etc
#role :app, "#{host}.joyent.us"                          # This may be the same as your `Web` server
#role :db,  "#{host}.joyent.us", :primary => true # This is where Rails migrations will run

server "#{kemp}.joyent.us", :app, :web, :db, :primary => true
set :user, "#{user}"
set :use_sudo, false
# If you are using Passenger mod_rails uncomment this:
# if you're still using the script/reapear helper you will need
# these http://github.com/rails/irs_process_scripts
set :port_number, "#{portnum}"  


namespace :deploy do

  task :start, :roles => :app do
    run "cd #{deploy_to}/current; rails s -e production -p #{port_number} -d -P #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN STARTED\""
  end
  task :stop, :roles => :app do
    run "cd #{deploy_to}/current; script/stop  #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN STOPPED\""    
  end
  task :restart, :roles => :app do
    run "cd #{deploy_to}/current; script/stop  #{deploy_to}/shared/pids/giftr.pid ; rails s -e production -p #{port_number} -d -P #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN RESTARTED\""
  end
  after 'deploy:update_code', 'bundler:bundle_new_release'
end

namespace :bundler do
  task :create_symlink, :roles => :app do
    shared_dir = File.join(shared_path, 'bundle')
    release_dir = File.join(current_release, '.bundle')
    run("mkdir -p #{shared_dir} && ln -s #{shared_dir} #{release_dir}")
  end

  task :bundle_new_release, :roles => :app do
    bundler.create_symlink
    run "cd #{release_path} && bundle install --without test"
  end
  after "bundler:bundle_new_release", :link_production_files
end


# database.yml task
desc "Link in the production database.yml"
task :link_production_files do
  run "ln -nfs #{deploy_to}/shared/config/database.yml #{release_path}/config/database.yml"
end
Obviously, you will want to replace #{host}, #{user} and #{portnum} with the appropriate values for your host.

25/08/10

Rails3 on joyent shared (rvm way)

Here is the story of my attempts to get a rails3 app to run on a shared account @joyent.

Forword

First note the following:
- The app I want to run there is a toy I made mostly for myself and for fun but I would still like to have it on a 'real' server
- I own a lifetime joyent account (back when it was textdrive)
- I didn't upgrade to a private accellerator back then cuz I was short on cash
- The shared account sits mostly idle and I don't want to shell out monthly money for a toy app

Environment

I got the app working once using rvm and installing ruby-1.8.7-p299.
Before installing rvm you need to make sure it can find all the tools it needs. Many gnutools are defined as aliases in your .bashrc, rvm doesn't like that. the best way is to
[~] mkdir ~/bin
[~] cd bin
[~] ln -s /usr/local/bin/gdiff diff
[~] ln -s /usr/local/bin/gfind find
[~] ln -s /usr/local/bin/ggrep grep
[~] ln -s /usr/local/bin/gmake make
[~] ln -s /usr/local/bin/gpatch patch
[~] ln -s /usr/ucb/ps ps
[~] ln -s /usr/local/bin/gsed sed
[~] ln -s /usr/ucb/whoami whomai
[~] echo 'export PATH=$HOME/bin:$PATH' >> .bashrc

Make sure you logout / log back in

Installing rvm

From there, installing rvm is pretty straightforward following the installation guide works just fine:
[~] bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
The next step the rvm install guide recommends is to put
[~] echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"'>> .bashrc  # This loads RVM into a shell session.
I put it in bashrc as I wanted to have it for non interactive shells too (running rails and such).

Installing ruby

At the time of my first install, I didn't have a problem but with the recent version of rvm I recommend making sure that .rvm/gemsets/ruby/1.8.7/global.gems points to bundler --pre and doesnt force any kind of version.
[~] rvm install ruby-1.8.7 
which installed ruby-1.8.7-p299 (yes this is important)

Installing gems

I recommend you pre-install your gems. Bundler can use a lot of memory and go over the shared account limits. Manually installing the biggest gems first works just fine
[~] gem install rails --pre --no-ri --no-rdoc
[~] gem install bundler --no-ri --no-rdoc
[~] gem install sqlite3-ruby --no-ri --no-rdoc
[~] gem install capistrano --no-ri --no-rdoc
[~] gem install webrat --no-ri --no-rdoc
This would do it for me, bundler works fine after that.

What did _not_ work for me

ruby-1.9.2

installing ruby-1.9.2 fails on compilation with a link error, from what I understand opensolaris linker has a bug where it doesn't compile some bit of ruby correctly and makes ruby crash seemingly at random. To avoid random crashes ruby developpers added a test called bug-3662 which tried to link a lib called bug.so to detect the bug. I wasn't able to go any further.

capistrano

on either ruby-1.8.7-p299 or 1.8.7-p302 capistrano dies instantly with :
/users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so: ld.so.1: ruby: fatal: relocation error: file /users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so: symbol EC_GROUP_new_curve_GF2m: referenced symbol not found - /users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so (LoadError)
I assume this is linked with ruby 1.9.2 not compiling. Hopefully joyent will upgrade the environment and fix the linker... In the meantime, cap deploy works from my laptop

20/05/10

Kata Mastermind in ioke

A few months ago I was introduced to the Dojo XP France. A french and parisian occurrence of a programming dojo concentrating on practicing and discussing TDD.

While "every monday 18:30" is a bit too much for me to follow (not to mention go to as I have almost an hour of transit to go to the usual dojo place) I have been working on some of the Katas.

The last I played with was the Mastermind kata, more precisely a Ioke implementation of the kata.

I have been able to find a very pleasing solution to the kata (hopefully a valid solution too :) ) which I present below. I tried to separate several stages of the code, I hope this will make the progression easier to follow. Obviously the best way to see the code evolve is to come to the Dojo.

Stage 1 : find the correct and well placed pegs

First the tests ...

describe("evalm", 
 ;stage 1
 it("should return [0,0] when all pegs are the wrong color", 
  evalm([]("M","M","M","M"), []("B","B","B","B")) should == [](0,0)
 )
 it("should return [1,0] when the first peg of both secret and guess is the same", 
  evalm([]("B","M","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [1,0] when the second peg of both secret and guess is the same", 
  evalm([]("M","B","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [2,0] when the first 2 peg of both secret and guess is the same", 
  evalm([]("B","B","M","M"), []("B","B","B","B")) should == [](2,0)
 )
)

Then the code ...

;stage 1
evalm=method(guess, secret, 
 if(
  guess[0]==secret[0],
  if(
   guess[1]==secret[1],
   [](2,0),
   [](1,0)
  ),  
  if(
   guess[1]==secret[1],
   [](1,0),
   [](0,0)
  ) 
 ) 
 )

Stage 2 : Refactor the code to DRY

Same tests as we refactor, thus the code ...

;stage2 == stage 1 refactored
evalm=method(guess, secret, 
 good=guess zip(secret) filter(inject(==)) count
 [](good, 0)
)

Stage 3 : Introduce the notion of misplaced pegs for the "R"ed color

By adding More tests ..

describe("evalm", 
 ;stage 1
 it("should return [0,0] when all pegs are the wrong color", 
  evalm([]("M","M","M","M"), []("B","B","B","B")) should == [](0,0)
 )
 it("should return [1,0] when the first peg of both secret and guess is the same", 
  evalm([]("B","M","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [1,0] when the second peg of both secret and guess is the same", 
  evalm([]("M","B","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [2,0] when the first 2 peg of both secret and guess is the same", 
  evalm([]("B","B","M","M"), []("B","B","B","B")) should == [](2,0)
 )
 ;stage 2 we refactor the good to use zip
 ;stage 3 introduce the misplaced
 it("should return [0,1] when one red peg is misplaced",
  evalm([]("M","R","M","M"), []("R","B","B","B")) should == [](0,1)
 )
 it("should return [0,2] when two red pegs are misplaced",
  evalm([]("M","R","M","R"), []("R","B","R","B")) should == [](0,2)
 )
)

And writing more code ...

;stage 3
evalm=method(guess, secret, 
 good=guess zip(secret) filter(inject(==)) count
 bad=guess zip(secret) filter(inject(!=))
 bad=bad filter([1]=="R") count
 [](good, bad)
)

Stage 4 : Generalize to all the colors

And yet more tests :) ...

describe("evalm", 
 ;stage 1
 it("should return [0,0] when all pegs are the wrong color", 
  evalm([]("M","M","M","M"), []("B","B","B","B")) should == [](0,0)
 )
 it("should return [1,0] when the first peg of both secret and guess is the same", 
  evalm([]("B","M","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [1,0] when the second peg of both secret and guess is the same", 
  evalm([]("M","B","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [2,0] when the first 2 peg of both secret and guess is the same", 
  evalm([]("B","B","M","M"), []("B","B","B","B")) should == [](2,0)
 )
 ;stage 2 we refactor the good to use zip
 ;stage 3 introduce the misplaced
 it("should return [0,1] when one red peg is misplaced",
  evalm([]("M","R","M","M"), []("R","B","B","B")) should == [](0,1)
 )
 it("should return [0,2] when two red pegs are misplaced",
  evalm([]("M","R","M","R"), []("R","B","R","B")) should == [](0,2)
 )
 ;stage 4 generalize to all colors
 it("should return [0,2] when a red peg and a yellow peg are misplaced",
  evalm([]("M","R","M","Y"), []("R","B","Y","B")) should == [](0,2)
 )
)

For more code ...

;stage 4
evalm=method(guess, secret, 
 good=guess zip(secret) filter(inject(==)) count
 bad=guess zip(secret) filter(inject(!=))
 colors=bad map:set(x,x[0])
 bad=colors map(x,bad filter([1]==x) count) sum 
 [](good, bad)
)

Stage 5 : Refactor for DRY

Same tests, different code ...

;stage 5
evalm=method(guess, secret, 
  sorted = guess zip(secret) groupBy(inject(==))
 good = (sorted[true]||[]) count
 badpairs = sorted[false]||[]
 colors = badpairs map:set(x,x[0])
 bad = colors map(x,badpairs filter([1]==x) count) sum || 0
 [](good, bad)
)

While this code is longer, it uses groupBy instead of zipping and filtering the list twice. I would like to improve on this by ensuring that groupBy always returns an empty list [] whether there are values or not. However I haven't found an elegant way to do this ... yet.

Stage 6: the complete solution with acceptance tests

The tests

describe("evalm", 
 ;stage 1
 it("should return [0,0] when all pegs are the wrong color", 
  evalm([]("M","M","M","M"), []("B","B","B","B")) should == [](0,0)
 )
 it("should return [1,0] when the first peg of both secret and guess is the same", 
  evalm([]("B","M","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [1,0] when the second peg of both secret and guess is the same", 
  evalm([]("M","B","M","M"), []("B","B","B","B")) should == [](1,0)
 )
 it("should return [2,0] when the first 2 peg of both secret and guess is the same", 
  evalm([]("B","B","M","M"), []("B","B","B","B")) should == [](2,0)
 )
 ;stage 2 we refactor the good to use zip
 ;stage 3 introduce the misplaced
 it("should return [0,1] when one red peg is misplaced",
  evalm([]("M","R","M","M"), []("R","B","B","B")) should == [](0,1)
 )
 it("should return [0,2] when two red pegs are misplaced",
  evalm([]("M","R","M","R"), []("R","B","R","B")) should == [](0,2)
 )
 ;stage 4 generalize to all colors
 it("should return [0,2] when a red peg and a yellow peg are misplaced",
  evalm([]("M","R","M","Y"), []("R","B","Y","B")) should == [](0,2)
 )
)
 ;stage 5 refactor to use groupBy
 ;stage 6 acceptance tests
describe("recette",
 it("should return [0,0] for guess(M,M,M,M) secret(B,B,B,B)", 
  evalm([]("M","M","M","M"), []("B","B","B","B")) should == [](0,0)
 )
 it("should return [4,0] for guess(B,B,B,B) secret(B,B,B,B)", 
  evalm([]("B","B","B","B"), []("B","B","B","B")) should == [](4,0)
 )
 it("should return [0,4] for guess(A,B,C,D) secret(D,A,B,C)", 
  evalm([]("A","B","C","D"), []("D","A","B","C")) should == [](0,4)
 )
 it("should return [2,2] for guess(A,B,C,B) secret(C,B,A,B)", 
  evalm([]("A","B","C","B"), []("C","B","A","B")) should == [](2,2)
 )
)

And the code

;stage 5
evalm=method(guess, secret, 
  sorted = guess zip(secret) groupBy(inject(==))
 good = (sorted[true]||[]) count
 badpairs = sorted[false]||[]
 colors = badpairs map:set(x,x[0])
 bad = colors map(x,badpairs filter([1]==x) count) sum || 0
 [](good, bad)
)

25/03/10

KawaCampParis1: NoSQL vs SGDB

C'est le premier sujet auquel j'ai participé. Lancé par Philippe Antoine, nous avons commencé par un petit tour des bibliothèques/serveurs NoSQL connus par les participants. J'ai surtout retenu Persevere (persvr) et Neo4J, ont aussi été mentionnés Voldemort et cassandra et quelques autres que je n'ai pas retenu.
La discussion m'intéressait particulièrement parce que lors de mes stages en école j'avais eu l'occasion de voir des bases non SQL sous forme de fichiers séquentiels indexés auxquels une application BASIC accédait avec des perfs assez impressionnantes. Du coup c'est la première remarque que j'ai faite, avec comme question en tête de savoir ce que le NoSQL apportait de nouveau au séquentiel indexé. Oui, la question était à la limite du troll mais c'était quand même intéressant.
D'autant plus intéressant que j'ai eu des réponses. Voilà ce que j'en ai retenu:
- NoSQL n'est pas destiné à remplacer totalement SQL, mais à le compléter. Vous savez bien, toutes ces tables de pseudo référentiel avec 2 colonnes, un id et une donnée. NoSQL offre des perfs inégalées pour charger et accéder à ce genre de tables.
- NoSQL, c'est aussi des bibliothèques/serveurs capables de gérer les architectures distribuées. Certains (il me semble qu'on parlait de cassandra et de ses dérivés) offrant la possibilité de configurer le niveau de cohérence de la base en distribué.
Ont également été mentionnés, BigTable de google ce qui a mené a une discussion sur les dictionnaires.
Enfin la discussion s'est terminée (trop vite comme toujours) sur une comparaison entre les avantages/inconvénients de NoSQL vs les caches de données des SGDB, en particulier nous avons parlé du cache Cohérence proposé par Oracle. Des paramètres qui amènerait à choisir cet outil plutôt que NoSQL ou l'inverse. Aucun consensus n'a d'ailleurs été trouvé sur ce point.
Bref, une discussion surprenante, et plutôt imprévue car ne correspondant nullement aux tags annnoncés par les participants mais finalement pleine d'information. Vivement le KawaCampParis2

02/03/10

KawaCampParis1, c'est reparti

Le KawaCampParis1 avait été reporté la date est maintenant officielle ce sera le 23/03. Ca se passe toujours au Quigley's Point aux Halles (paris 1er).
Les inscriptions et les infos sont toujours sur le wiki barcamp.org : KawaCampParis1

09/02/10

Dos tricks that make me sick

It's been a while since I have had to use the dos shell for anything more than trivial scripting.

The DOS shell is Evil it was _designed_ to make people crazy, I can't think of any other explanation.

Here, have a quick look at some of the tricks you will have to come up with if you want some basic shell scripting features.

Set a variable with a command's result:

for /F %%x in (`echo value`) do set var=%%x

Then var is equal to "value".That's because having

set var=`echo value`
would have been to easy....

Did I mention you can only have one command in a for loop, unless you use labels and gotos ? that's just plain sick.

While I am at it, assume you have a variable, let's call it %var%, imagine its value is 1 like so :

set var=1
Now try and append that to a file with an echo, like so:
echo foo=%var%>>test.txt
what do you expect in test.txt ? why, but nothing more than
foo=
why in hell whould the 1 be present in the output? obviously you must use
echo foo=^%var%>>test.txt
to get
foo=1
Who could _not_ think of using ^ for such an obvious corner case as a NUMBER ?!! Seriously ?

I want my posix shell back !

21/01/10

Kawa Camp Paris la première

Partis du constat que les javacamps Paris ont une tendance a déraper vers des sujets pas vraiment Java philippa a décidé de monter le kawa camp. Moins orienté Java toujours destiné aux caféinés, on espère que les sujets abordés au kawa camp seront un peu plus variés. Les tags déjà inscrits sont prometteurs :) Le kawa camp Paris 1 c'est le 3 février et ça se passe là : http://barcamp.org/KawaCampParis1

25/08/09

Random hex string generator in ruby

I was looking around for a random hex string generator (pseudo random is fine for me too) and I wasn't too happy with what I found (source : http://snippets.dzone.com/posts/show/491 ):

def newpass( len )
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
    newpass = ""
    1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
    return newpass
end

I tried to come up with something more to my liking, on my own but didn't fare that much better:

def rand_hexstring(length=8)
  (1..length).inject("") {|x,i|
    x+=((rand()*128)).to_i.chr
  }.unpack("H*").to_s.slice 0..length
end

I had thought of filtering it at some point and came up on a blog ( http://www.rubyrailways.com/rubys-most-underused-keyword/ ) with the following :

  (0..9).map{rand(?z).chr[/[^_\W]/]||redo}*"" 

While in the end I had no use for filtering, and shed the nice redo trick, it made me realize I could improve on my answer by using map instead of inject :

  def rand_hexstring(length=8)
    ((0..length).map{rand(256).chr}*"").unpack("H*")[0][0,length]
  end

What this does is : for each element of the range ( map ), create a one char string ( rand(256).chr ) join the resulting array ( *"" ) unpack it to hexes, get the only element ( [0] ) of the resulting array and slice it to the requested length

16/08/09

Eclipse 3.5 and Glassfish

I just tried to install a new instance of galileo and got bitten again:/ try to install glasfish support in galileo following the obvious path and you will be presented with a totally cryptic error message that basically tells you to go to hell with your request. ( I remember reading somewhere that p2 error messages are beyond the understanding of mortalsm I can only concur ).
Anyway, it looks like I am not the only one having trouble installing Glassfish v3 support for my favorite Java IDE : http://hardikmehta.wordpress.com/2009/06/29/glassfish-plugin-on-eclipse-3-5-galileo To sum it up, install glassfish support from the following update site: https://ajax.dev.java.net/eclipse/ and you will be setup in a sec

15/08/09

JavaCamp Paris 5ème édition

Pour sa cinquième édition le JavaCamp paris prendra la forme d'un grand picnic au jardin des tuileries, ça se passe le jeudi 27 Août à 18h30. Le barcamp c'est un format totalement informel, l'inscription sert surtout a annoncer les sujets qui nous interessent, de façon que l'on sache un peu si on trouvera des gens pour discuter de ça pendant le barcamp. Comme cette fois on ne sera pas chez google, il n'y aura pas de buffet, il faut donc penser a amener a manger et a boire. En échage : pas de contrainte de place, je doute que nous remplissions la totalité du jardin des tuileries :D

10/08/09

Cool iris

En fouinant dans les addons firefox à la recherche de l'addon delicious, je suis retombé sur cooliris. Il est maintenant disponible pour ma plateforme (linux 64bits) Je ne connais pas de meilleur facon de visionner une grande quantité d'images rapidement, pour moi qui passe beaucoup de temps a "lurker" sur flickr, cet addon c'est le bonheur. Merci cooliris!

06/08/09

Impressions of TDD

A little think back on yesterday's TDD experiment. I left the tests in the order I wrote them. If you look closely enough, you will see that the test test_get_open_lockers_4_est_1_et_4 couldn't work until the last two tests were written. What that means is that I tried to take too big a step and wrote a test I couldn't make pass. I wrote more tests, leaving a failing test behind, fixing first the folowing tests and then that one. Was that the correct answer to too taking too big a step ?

Nerd, Jocks ans Lockers

I am a fanatic reader of the Daily WTF but until now I had never taken the time to try and solve a problem from the programming praxis category. So this time, I have gone and done it for the Nerds, Jocks and locker problem. the following is my entry done trying to follow TDD. test_locker_solver.rb:
require 'test/unit'
require 'locker_challenge'

class TestLockerSolver < Test::Unit::TestCase
 def test_prime_set_number_de_1_est_1_1() 
  solver=LockerSolver.new
  assert_equal [1,1], solver.get_prime_set(1)
 end
 def test_prime_set_number_de_2_est_1_2() 
  solver=LockerSolver.new
  assert_equal [1,2], solver.get_prime_set(2)
 end
 def test_prime_set_number_de_3_est_1_3() 
  solver=LockerSolver.new
  assert_equal [1,3], solver.get_prime_set(3)
 end
 def test_prime_set_number_de_4_est_1_2_2() 
  solver=LockerSolver.new
  assert_equal [1,2,2], solver.get_prime_set(4)
 end

 def test_get_open_lockers_1_est_1
  solver=LockerSolver.new
  assert_equal [1], solver.get_open_lockers(1)
 end
 
  def test_get_open_lockers_4_est_1_et_4
  solver=LockerSolver.new
  assert_equal [1,4], solver.get_open_lockers(4)
 end
 
 def test_count_primes_1_est_1x2
   solver=LockerSolver.new
   
   assert_equal({1 => 2}, solver.count_primes(1))
 end
  def test_count_primes_4_est_1x1_2x2
   solver=LockerSolver.new   
   assert_equal({1 => 1, 2=>2}, solver.count_primes(4))
 end
end
and locker_solver.rb:
class LockerSolver
 def get_prime_set(n=1)
  i=2
  while n%i!=0 and i<=n  
   i=i+1
  end
  i>=n ? [1,n] : [i,get_prime_set(n/i)].flatten.sort  
 end
 
 def count_primes(n=1)
  pset=get_prime_set(n)
  map=pset.inject({}) { |memo,n| memo[n].nil? ? memo[n]=1 : memo[n]+=1; memo }
 
 end
 
 def get_open_lockers(n)
  r=[]
  (1..n).each do |l|
    h=count_primes(l).delete_if {|key, value| key<2}
    isOpen=true
    h.each do |prime,count|
      isOpen &= (count%2==0)
    end
    isOpen ? r << l : false
  end
  r
 end
end

19/06/09

Introducing dstx/wtx unit

Context

At my current client, I work with Datastage Transformation Extender (DSTX) recently re branded Websphere Transformation Extender since it was bought by IBM. To me DSTX looks like a strongly, statically typed functional programming language with added seemingly arbitrary limitations, but that's not the point for today. When I was asked to develop a completely new transformation map for my current project, I immediately thought, cool let's practice Test first, where do I find DstxUnit ? I didn't find it :(

Building DstxUnit

I could have done a small shell framework but I wanted it to both run under Unix (because I intend to make some kind of continuous integration later on) and Windows (developers). However I didn't want to bother with writing 2 shell scripts, also I was really looking forward to getting the green bar. Since DSTX is used for data transformation it is easy to make it take text files as input and write text files as output (you can even override input and output settings of the map for specific runs) So I went the java way (I am used to working with java) and extended a JUnit Testcase which provides runTx and assertTx.

runTx

runTx will take the transformation map name, the file extension for input and output, a test case number for the given map and a flag to know if errors are acceptable or not for this run. With that and a configuration file (to know where the maps are and where the resources are) it will build all the correct paths to run the map and place its output in a know location for comparison (see assertTx). Here is how my files are organized :
project/
  DSTX/
    Map/
     map.mms
     common/
       lib.mms
    Test/
      testmap.mms
      common/
        testlib.mms
      resources/
        in/
          test_map_1.txt
          common/
            test_lib_1.txt
            test_lib_2.txt
            test_lib_3.txt
            test_lib_4.txt
        out/
          [output of map run will be generated here]
          common/            
            [output of lib run will be generated here]
        ref/
          test_map_1.out
          common/
            test_lib_1.out
            test_lib_2.out
            test_lib_3.out
            test_lib_4.out
        trace/

assertTx

I chose to separate the run itself from asserting the results, mostly to be able to see an "assert" line in the test, but also because run errors are not the same as assertion errors. assertTx thus requires again the name of the map, the output extension, and the test case number and compares the produced output (in out/ ) with the reference file (in ref/ ) If the files are different, the assertTx method will raise an AssertionFailed exception including a detailed messages explaining the differences in the usual "expected <...> but got <...>" format.

28/04/09

Headless gecko browser attempt 3

I reported my trouble in the comments of the bug which first proposed a new headless renderer. I got helpful comments from both Chris Lord and Sylvain Pausch, basically if your embedded application segfaults like this:
WARNING: NS_ENSURE_TRUE(compMgr) failed: file nsComponentManagerUtils.cpp, line 165
###!!! ASSERTION: You can't dereference a NULL nsCOMPtr with operator->().: 'mRawPtr != 0', file ../../../../dist/include/xpcom/nsCOMPtr.h, line 796
Segmentation fault
Make sure you have MOZILLA_FIVE_HOME environment variable pointing to the directory containing libxul.so ($prefix/lib/xulrunner-1.9.2a1pre/) when running the sample.