14/09/2011

Git-svn and the very very large svn repo

At my current workplace, the svn repository is shared by all the company's projects, that's a lot of projects and quite a lot of commits not to mention branches and stuff, also my project has been going on for a few years and the developpers have tried a wild variety of things on the branch and tags side.

As we have a very complex merge coming up, I decided to give git-svn a quick try to see if it could help us with that, the initial import on windows never finished. It failed at reading a particularly large commit losing the data connection in the middle of the commit. After we were allowed to setup a linux workstation (faster builds which don't fail for hitting the maximum path length on some files), I gave it a second try.

I got myself a working git repo, made mostly unusable by the dozens of branches and the hundreds of tags in it. That's when I started looking into selectively fetching part of the branches and the tags. The thing is there are dozens of articles on the basic use of git-svn but much fewer on more advanced configurations.

It's quite simple and actually it is documented on the main git-svn man page. the first step is to initialize your repo but not clone it:
git svn init --username my_user -s http://my-svn-url/
In the case of our corporate svn we have authentification going on thus the username. I used the s flag to create the default mappings for branches and tags which we will now edit by opening the newly created config file in the .git subdirectory. The file should look like this :
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[svn-remote "svn"]
    url = http://my-svn-url
    fetch = projets/myproject/trunk:refs/remotes/trunk
    branches = projets/myproject/branches/*:refs/remotes/*
    tags = projets/myproject/tags/*:refs/remotes/tags/*
The first thing I did was to remove the tags line as I didn't want the tags to be fetched they are in such disarray at the moment as to be useless. The second thing was to transform the "fetch all branches" into a "fetch only these branches" directive.

This is accomplished by using range expressions instead of the glob * on the left side of the branches directive. A range is expressed like this {branch1, branch2,...} and you have to specify all the branch names as far as I can tell. On the right side of the branches directive you must leave the * at the end it is mandatory. A correct branch directive thus looks like :
branches = projets/myproject/branches/{branch1,branch3}:refs/remotes/*
Once you have selected the branches you want to fetch and configured your git repo, all that is left is to run
git svn fetch
and wait :)

22/03/2011

KawaCampParis4

C'est officiel, Softeam accueillera dans ses locaux la 4ème édition du KawaCampParis et fournira aux participants un buffet et des softs.Il faut s'inscrire sur http://barcamp.org/KawaCampParis4. Pensez à indiquer vos centres d'intérêt!

KawaCamp kesako ?

Un KawaCamp c'est une recontre/table-ronde informelle auto-organisée de personnes partageant un intéret commun pour l'informatique. Les participants indiquent les sujets principaux qui les interessent sur la page d'inscription ce qui vous permet d'avoir une petite idée des sujets qui pourront être abordés. Sur place, chacun sera invité a proposer ses sujets, les sujets ayant le plus de participants seront retenus pour l'une des salles de discussion. Si la discussion fini par vous fatiguer, vous êtes libres d'aller faire un tour dans les autres salles.

07/10/2010

Agile tour Paris: demandez le programme!

Le programme de l'Agile Tour Paris 2010 est sorti:
http://www.agiletour.org/programme_paris2010.html 
Maintenant il va falloir choisir ... et ça promet de ne pas être simple.

14/09/2010

Agile Tour Paris

Je me suis inscrit pour participer à la session parisienne de l'Agile Tour 2010 qui aura lieu le 28 Octobre 2010.

Les infos (assez légères pour l'instant) et les inscriptions c'est par là: http://www.agiletour.org/en/paris.html

13/09/2010

RVM & Ruby 1.9 on joyent shared

In my previous posts I explained that the plain vanilla ruby 1.9 install with rvm wouldn't work on joyent shared accelerators.

The amazing people at joyent actually answered on the forum with a more precise explanation of the problem. So if you get a error like this when compiling ruby on an opensolaris :

Undefined                       first referenced

 symbol                             in file

rb_eArgError                        bug.o  (symbol scope specifies local binding)

ld: fatal: Symbol referencing errors. No output written to ../../../.ext/i386-solaris2.11/test/bug-3662/bug.so

collect2: ld returned 1 exit status

It's not so much a linker error as a problem with gobjcopy (part of the binutils shipped with Solaris). Mamash, a joyent administrator proposes the following solution to this problem :

The problem seems to be related to gobjcopy (part of the binutils shipped with Solaris). Upon extracting Ruby 1.9.x, when you take out the line that calls $(OBJCOPY) in Makefile.in, you should be able to configure/build Ruby just fine. I'm checking whether this has any adverse effects on the resulting binaries/libs, but just playing with the binaries quickly looks fine.

A rare Google find suggests this has to do with the version of gobjcopy shipped with different Solaris Nevada builds.
(source)

The rare google find refers to a blog post on Mandy Waite's old blog at sun now become oracle. The blog post entitled "Building Ruby 1.9.1 on Solaris SPARC" explains the whole gobjcopy thing.

Anyway, here is the command line I used to compile my ruby-1.9.2 :

~]rvm install ruby-1.9.2 --patch ~/makefile_nodir.patch -C --enable-shared,--with-openssl-dir=$HOME/.rvm/usr

The makefile patch can be found in the following gist

27/08/2010

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/2010

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.

Actually a joyent admin had a look into it and proposed a solution

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/2010

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/2010

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/2010

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/2010

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/2010

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/2009

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/2009

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/2009

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