jeudi, juillet 10, 2008

Rubik’s Cube a 25 ans

Vu le nom que j'ai choisi pour mon blog, je me devais de fêter les 25 ans du Rubik's Cube, le vrai!
Une campagne de pub pour l'occasion: Article ViaComIT.

mercredi, juin 25, 2008

LinkedIn la plus grosse application rails au monde ?

Plus précisément "BumperSticker" de LinkedIn.
C'est en tout cas ce que les gens de LinkedIn proclament sur leur blog (relayé par Matt Raible connu pour ses comparaisons de framework web java).
En tout cas si la video dit vrai: "Scaling Rails to 1 Billion Pages a Month"... c'est impressionnant!
Et surtout ça prouve que rails est capable de supporter la charge... (après tout dépend du talent des developpeurs qui font l'application).
Technorati tags:

mercredi, juin 04, 2008

Ruby Riddle: "Trouver le nom de la nouvelle société où je travaille"

J'ai posé ma démission de Anyware Technologies depuis quelques temps maintenant mais j'ai gardé le nom de la nouvelle ssii secrète pour l'instant :p
Pour favoriser les personnes que je connais qui ont installé Ruby sur leur machine et pour pousser ceux qui sont curieux à le faire :D
Voici un petit programme ruby en forme de devinette.
old_job = "anyware"
puts "old job: #{old_job}"
magic = 345756
new_job = old_job[0, 1]
magic.times {|j| new_job = new_job.next}
puts "new job: #{new_job.upcase}"
Il suffit de l'exécuter pour avoir la réponse ;)
Je posterai l'annonce officielle sur mon autre blog demain ;)

lundi, avril 28, 2008

Ruby à l'Université

Comme rien n'échappe à Google et à ses "Google Alerts", je viens de découvrir un cours à l'Université de Toulouse II - Le Mirail qui utilise Ruby comme langage de programmation pour les débutants:
Une bien bonne nouvelle et une excellente initiative :)
Si vous doutez de l'adéquation entre Ruby et Débutants, je vous conseille de jeter un coup d'oeil au livre: "Learn to Program"
Technorati tags:

lundi, mars 31, 2008

Experimentations avec les Mixins / Modules et les méthodes statiques (self)

On m'a posé une question aujourd'hui: "Comment définir une méthode statique dans un module et l'inclure dans une classe ?". Simple non ?!
Justement j'avais eu le même problème quand j'avais fait mon projet "natural_sort"
Spontanément j'ai essayé:

module Carnivore
  def self.manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

class GrosMinet
  include Carnivore
end

Carnivore.manger "titi" # ok ca foncionnne
GrosMinet.manger "titi" # undefined method `manger' for GrosMinet:Class (NoMethodError) WTF ?!
Malheureusement ça ne fonctionne pas :(

Dans NaturalSort, j'ai contourné le problème:

module NaturalSort
  def self.naturalsort(object)
    ...
  end
  def natural_sort
    NaturalSort::naturalsort(to_a)
  end
end
Je suis tombé sur cet article: "Ruby Mixin Tutorial" et j'ai essayé différentes combinaisons, il n'y en pas une qui soit satifaisante dans tous les cas...
Ici vous pourrez voir les principales étapes jusqu'à la version finale, celle qui me satisfait le plus.
Etape n°1: classe statique dans le module uniquement

module Carnivore
  def self.manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

Carnivore.manger "titi"
Etape n°2: méthode d'instance de classe (l'utilisation classique du module par excellence):

module Carnivore
  def manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

class GrosMinet
  include Carnivore
end

gros_minet = GrosMinet.new 
gros_minet.manger "titi"
Version finale. 2 grosses subtilités:
  • il n'y a pas de 'self' dans la définition de la méthode 'manger'
  • On utilise 'extends' au lieu de 'include'

module Carnivore
  def manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

module Chat
  def miauler
    puts "miaou..."
  end
end

class GrosMinet
  include Chat
  # include Carnivore # necessaire à gros_minet.manger("titi")
  extend Carnivore
end

GrosMinet.manger "titi"

gros_minet = GrosMinet.new 
gros_minet.miauler
# gros_minet.manger "titi" # necessite le include Carnivore pour fonctionner

Conclusion: Ca fonctionne et le code est court mais je trouve la syntax un peut étrange, je ne comprend pas trop pourquoi le 'include' n'inclus pas les méthodes 'self' ? il y a quelquechose qui m'échappe pour l'instant...

Du coup je pense que c'est une bonne pratique de ne pas utiliser de méthodes préfixé par 'self' dans le modules et que faire MonModule.ma_methode n'est peut-être pas très orienté objet... après tout les modules ne sont pas vraiment des objets et ne devraient pas être utilisés seuls ? Qu'est ce que vous en pensez ?


Technorati tags:

mercredi, mars 19, 2008

Rails 2 - Renommer des .rjs en .js.rjs

Suite à ma migration en rails 2.0, j'avais renommé mes .rjs en .js.rjs mais les pages ne fonctionnaient plus :(
C'est parce qu'il faut ajouté le block respond_to dans la méthode qui appelle l'ajax dans le controller:
  # Ajax response for validation
  def validate_new_holiday
    @holiday = self.init_holiday
    respond_to do |format|
      format.js      # validate_new_holiday.js.rjs
    end
  end 
Technorati tags:

mardi, mars 18, 2008

Migration "Pas à pas" d'une application ruby on rails de 1.2.6 vers 2.0.2 (part 2)

Dans le post précédent nous avons vu comment migrer de 1.2.2 vers 1.2.6
Maintenant nous allons passer de 1.2.6 à 2.0.2


Je ne l'ai pas dit dans mon post précédent tellement ça me paraissait évident, mais utiliser systématiquement subversion (ou un autre système: cvs, git...) pour pouvoir revenir en arrière. C'est indispensable !!


> rake rails:unfreeze

> svn delete vendor/rails

> gem list rails

*** LOCAL GEMS ***

rails (1.2.6)

> gem update rails
Successfully installed rails-2.0.2
1 gem installed
Gems updated: rails

> gem list rails

*** LOCAL GEMS ***

rails (2.0.2, 1.2.6)

> edit config/environment.rb

Changer
RAILS_GEM_VERSION = '1.2.6' unless defined? RAILS_GEM_VERSION
En:
RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION

>ruby script\about

      *******************************************************************
      * config.breakpoint_server has been deprecated and has no effect. *
      *******************************************************************

About your application's environment
Ruby version              1.8.6 (i386-mswin32)
RubyGems version          1.0.1
Rails version             2.0.2
Active Record version     2.0.2
Action Pack version       2.0.2
Active Resource version   2.0.2
Action Mailer version     2.0.2
Active Support version    2.0.2
Application root          C:/developement/ruby/appli
Environment               development
Database adapter          mysql
Database schema version   8

Si vous avez le même warning que moi, éditer le fichier development.rb et enlevé les lignes suivantes:

# Enable the breakpoint server that script/breakpointer connects to
config.breakpoint_server = true
puis installer ruby-debug comme expliqué ici: Fixing-config-breakpoint-server

> rake rails:update

modifie 
  config/boot.rb

  public/javascripts/control.js
  public/javascripts/dragdrop.js
  public/javascripts/effects.js
  public/javascripts/prototype.js

> rake log:clear tmp:clear db:test:purge

Editer database.yml J'aime bien ajouter l'encoding pour MySql:

development:
  adapter: mysql
  *encoding: utf8* (sans les étoiles autour ;) )
  database: appli_development
  username: appli
  password: appli
  host: localhost

> rake db:reset
(optionnel ?)

> rake db:migrate:reset
...

m à j de db/schema.rb

> rake test
...
36 tests, 95 assertions, 0 failures, 0 errors
...
33 tests, 63 assertions, 0 failures, 0 errors

> ruby script/server
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready.  INT => stop (no restart).
** Mongrel 1.1.4 available at 0.0.0.0:3000
** Use CTRL-C to stop.
/!\ FAILSAFE /!\  Tue Mar 18 10:16:56 +0100 2008
  Status: 500 Internal Server Error
  A secret is required to generate an integrity hash for cookie session data. Use config.action_controller.session = { :session_key => 

"_mya
pp_session", :secret => "some secret phrase of at least 30 characters" } in config/environment.rb
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/session/cookie_store.rb:91:in `ensure_secret_secure'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/session/cookie_store.rb:60:in `initialize'
...

http://localhost:3000/ retourne une erreur 500
Le plus simple que j'ai trouvé c'est de créer une nouvelle appli rails à côté:
> rails dummy

Remplacer le contenu de appli/config/environment.rb par celui de dummy (en garder une copie qq part)

> rake secret
Remplacer la ligne suivante:

  config.action_controller.session = {
    :session_key => '_dummy_session',
    :secret      => '1234abcde'
  }

par
  config.action_controller.session = {
    :session_key => '_appli_session',
    :secret      => 'secret generé par la commande rake secret'
  }

Ajouter toutes les conf specifiques à votre appli.
Par exemple dans mon cas j'avais ActionMailer, donc j'ai ajouté à la fin:
ActionMailer::Base.smtp_settings = {
  :address  => 'smtp.truc.com',
  :port  => '25',
  :domain => 'www.truc.com'
}

Continuons la migration...
> rake test
...
> svn commit -m "updating to rails 2.0.2"

> rake rails:freeze:gems
...

> svn add vendor/rails

> rake deprecated
...

> ruby script/plugin remove deprecated
...
inutile maintenant

> svn commit -m "freezing rails 2.0.2"

> gem uninstall rails --version 1.2.6

A partir de là vous avez fait l'essentiel!!
Vous pouvez rentrer chez vous après une dure journée de labeur... ou bien fignoler le travail

Créer un fichier migrate_views.rb à la racine de l'appli: Ajouter:

Dir.glob('app/views/**/*.rhtml').each do |file|
  puts `svn mv #{file} #{file.gsub(/\.rhtml$/, '.html.erb')}`
end

> ruby migrate_views.rb
A         app\views\layouts\application.html.erb
D         app\views\layouts\application.rhtml
...

> rake test
...

> ruby script/server
...
Tester l'appli

Ajouter à production.rb

config.action_view.cache_template_loading            = true

Ajouter à test.rb
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection    = false
Concernant routes.rb, je n'ai pas de solution miracle :(
Créer un une appli rails dummy à coté et utiliser un outil de conmparaison de fichier pour voir les différences et mettre votre routes.rb à jour.

Par exemple si vous n'utiliser pas le wsdl comme moi vous pouvez supprimer la ligne:
map.connect ':controller/service.wsdl', :action => 'wsdl'

On peut aussi copier/coller le README à la racine de l'appli dummy vers son appli rails

Dans test_helper.rb, on peut ajouter:

  # The only drawback to using transactional fixtures is when you actually 
  # need to test transactions.  Since your test is bracketed by a transaction,
  # any transactions started in your code will be automatically rolled back.
juste avant
self.use_transactional_fixtures = true
et
  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
  #
  # Note: You'll currently still have to declare fixtures explicitly in integration tests
  # -- they do not yet inherit this setting
  fixtures :all

Si vous avez un script/breakpointer je pense que vous pouvez le supprimer...

Vous pouvez aussi changer tous vos fichiers dans db/migrate pour utiliser la nouvelle syntaxe des migrations (optionnel)

Il y a plein d'autre améliorations possibles dans les controller, dans les fixtures etc... cf: rails-2-0-it-s-done

Et Voilà ! Votre appli est migrée en Rails 2.0.2! Félicitations ;)

Technorati tags:

lundi, mars 17, 2008

Migration "Pas à pas" d'une application ruby on rails de 1.2.2 vers 1.2.6

Comment migrer "Pas à pas" une application ruby on rails de 1.2.2 vers 1.2.6 (Nous verrons de 1.2.6 à 2.0.2 dans un autre post).

Extrait du blog officiel de Ruby on Rails:
So how do I upgrade? If you want to move your application to Rails 2.0, you should first move it to Rails 1.2.6. That’ll include deprecation warnings for most everything we yanked out in 2.0. So if your application runs fine on 1.2.6 with no deprecation warnings, there’s a good chance that it’ll run straight up on 2.0. Of course, if you’re using, say, pagination, you’ll need to install the classic_pagination plugin. If you’re using Oracle, you’ll need to install the activerecord-oracle-adapter gem. And so on and so forth for all the extractions.

> ruby script\about

About your application's environment
Ruby version                 1.8.6 (i386-mswin32)
RubyGems version             1.0.1
Rails version                1.2.2
Active Record version        1.15.2
Action Pack version          1.13.2
Action Web Service version   1.2.2
Action Mailer version        1.3.2
Active Support version       1.4.1
Edge Rails revision          64
Application root             C:/developement/ruby/prophet/trunk/prophet
Environment                  development
Database adapter             mysql
Database schema version      8



> rake rails:unfreeze

> gem install rails --version 1.2.6

> gem list rails

*** LOCAL GEMS ***

rails (2.0.2, 1.2.6)

> edit appli/config/environment.rb
Changer:
RAILS_GEM_VERSION = '1.2.2' unless defined? RAILS_GEM_VERSION
En:
RAILS_GEM_VERSION = '1.2.6' unless defined? RAILS_GEM_VERSION

> rake rails:update:configs

modifie config/boot.rb

> rake rails:update:javascripts

modifie rien ?

> rake rails:update:scripts

modifie rien ?

> rake log:clear tmp:clear db:test:purge

> ruby script\about

About your application's environment
Ruby version                 1.8.6 (i386-mswin32)
RubyGems version             1.0.1
Rails version                1.2.6
Active Record version        1.15.6
Action Pack version          1.13.6
Action Web Service version   1.2.6
Action Mailer version        1.3.6
Active Support version       1.4.4
Application root             C:/developement/ruby/prophet/trunk/prophet
Environment                  development
Database adapter             mysql
Database schema version      8


> rake test
...
36 tests, 95 assertions, 0 failures, 0 errors
...
33 tests, 63 assertions, 0 failures, 0 errors

> ruby script\server
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready.  INT => stop (no restart).
** Mongrel 1.1.4 available at 0.0.0.0:3000
** Use CTRL-C to stop.
March 17, 2008 17:39 --

...

Regarder dans les logs s'il y a des "DEPRECATION WARNING"
Jeter un coup d'oeil à http://www.rubyonrails.org/deprecation
Installer le plugin deprecated: http://nubyonrails.com/articles/deprecated-plugin-find-old-rails-code
> ruby script/plugin install http://topfunky.net/svn/plugins/deprecated
+ ./deprecated/CHANGELOG
+ ./deprecated/MIT-LICENSE
+ ./deprecated/README
+ ./deprecated/about.yml
+ ./deprecated/tasks/deprecated.rake


> rake deprecated
(in C:/.../app)
--> component
 Clean! Cheers for you!

--> @session
 Clean! Cheers for you!

--> paginate
 Clean! Cheers for you!
...
S'il n'y a que des "Clean" c'est que tout va bien :)
> svn cleanup .
(optionnel ?)

> svn commit -m "upgrading to 1.2.6"

> rake rails:freeze:gems
(in C:/developement/ruby/prophet/trunk/prophet)
Freezing to the gems for Rails 1.2.6
rake aborted!
uninitialized constant Gem::GemRunner

(See full trace by running task with --trace)


>gem update --system
...

ATTENTION HACK
Supprimer le rep 'rails' dans 'vendor' à la main pour revenir à zéro...
Il y a un probleme de compatibilité entre les dernières version de rubygems et rails 1.2.6
Il faut ajouter 'require 'rubygems/gem_runner' dans C:\ruby\lib\ruby\gems\1.8\gems\rails-1.2.6\lib\tasks\framework.rake
cf: rubygems-095-and-rails-126-uninitialized-constant-gemgemrunner
> gem list rails

*** LOCAL GEMS ***

rails (2.0.2, 1.2.6)

ATTENTION
Bien préciser la version sinon ça va freezer la plus récente (ici on veut 1.2.6 pas la 2.0.2)
> rake rails:freeze:gems VERSION=1.2.6

Maintenant ça devrait marcher...

> svn add vendor/rails

> svn commit -m "freezing to 1.2.6"

Et voilà !
Plus vous avez de tests moins il y aura de régressions ;)
Passez un peu partout dans l'appli pour plus de sureté aussi, on est jamais trop prudent ;)

lundi, mars 10, 2008

Déployer une application ruby on rails dans Tomcat

Je viens de déployer ma première application rails dans un Tomcat avec JRuby.
Contrairement à ce que la longueur de ce post pourrait laisser penser c'est très simple et très rapide (env 2H30 pour quelqu'un d'expérimenté en tomcat, mysql et rails et 30 min si tout est pré-installé).
Le lien indispensable pour déployer une application rail 1.1 ou 2.0 se trouve sur le wiki de JRuby: Jruby on Rails on Tomcat
Je vous conseille la page du wiki qui est beaucoup plus succinte en premier!
Sinon voilà une autre version si vous partez vraiment de zéro avec beaucoup plus de détails et/ou de "bonnes pratiques" ;)
En gros il y a 2 cas:
  1. Vous préparez le ".war" (généralement un développeur ruby/java en charge de la livraison) Nous l'appellerons Mr R.
  2. Vous déployez sur la machine de production (généralement l'admin système ou un gars des opérations). Nous l'appellerons Mr A.
Versions et environnent testé: Windows XP + Tomcat(6.0.16) + JRuby(1.0.3) + MySql(5) + Java(jre1.6.0_03)
Cas numéro 1: Création du "War"
  1. JRuby
    • Télécharger JRuby
    • Dezipper JRuby (ex: c:\jruby)
    • Ajouter JRUBY_HOME=c:\jruby
    • Ajouter %JRUBY_HOME%\bin à la variable d'environnement 'Path'
  2. Tomcat
    • Télécharger l'installer: apache-tomcat-6.0.16.exe
    • Installler (suivre le wizard)
    • Générallement Tomcat est installé ici: C:\Program Files\Apache Software Foundation\Tomcat 6.0
  3. MySQL
    • Installer MySql (encoding utf-8 de préférence)
    • Se Connecter en 'root'
    • Créer un schema appli_[env]
    • Créer un user spécial:
      GRANT ALL ON appli_production.* TO 'appli'@'localhost' IDENTIFIED BY 'appli';
  4. Warbler:
        c:\...\appli> jruby -S gem install -y rails warbler
        c:\...\appli> jruby -S rake db:migrate RAILS_ENV=production
        c:\...\appli> jruby -S rake db:structure:dump RAILS_ENV=production
        c:\...\appli> jruby -S warble war 
        c:\...\appli> jruby -S warble config
    
    Avec la commande "db:structure:dump", on a généré un fichier: production_structure.sql (utilisé par Mr. A)
  5. Aller dans le "appli>tmp/war/WEB-INF/web.xml" et ajouter les lignes suivantes si vous êtes dans le cas décrit dans le wiki de JRuby:
    <context-param>
        <param-name>jruby.session_store</param-name>
        <param-value>db</param-value>
    </context-param>
  6. Copier/Coller le web.xml modifié dans le répertoire config pour ne pas avoir a refaire cette manip à chaque fois que vous généré le war.
  7. Vérifiez les paramètres de connexion à la production dans database.yml
  8. Figer toutes les versions de rails et warbler:
        c:\...\appli> jruby -S rake rails:freeze
        c:\...\appli> jruby -S warble pluginize
    
  9. Regénérer le war avec le web.xml modifié:
        c:\...\appli> jruby -S warble war:clean
        c:\...\appli> jruby -S warble war
    
  10. Le copier dans le répertoire webapp de Tomcat
  11. Démarrer Tomcat
  12. Tester l'application: http://localhost:8080
Cas numéro 2: Production
  1. Installer Tomcat
  2. Installer MySql
  3. Créer le schéma de production
    CREATE SCHEMA appli_production;
  4. Charger les tables dans le schéma (j'utilise MySql Query Browser pour ça; je ne connait pas la commande exacte), On utilisera le fichier généré par Mr. R (cf Cas numéro 1: production_structure.sql)
  5. Se mettre d'accord sur les paramètres de connexion à la base avec Mr. R (schema name/username/password)
  6. Créer un user spécial:
    GRANT ALL ON appli_production.* TO 'appli'@'localhost' IDENTIFIED BY 'appli';
  7. Récupérer le war de Mr. R
  8. Le copier dans le répertoire webapp de Tomcat
  9. Démarrer Tomcat
  10. Tester l'application: http://localhost:8080
Conclusion: Voilà j'ai peut-être oublié des choses ou fait des erreurs de frappe mais l'essentiel est là ;)
Rem: Il y a autant de manières de créer une application que de personnes, certaines pratiques sont une affaire de goût :)
N'hésitez pas à ajouter vos commentaires...
Technorati tags:

jeudi, mars 06, 2008

Améliorer la lisibilité avec Array.collect

Je n'ai pas l'habitude (venant du monde java) d'utiliser les méthodes collect, reject, select de Array.
Voilà un cas d'utilisation où j'ai nettement vu l'amélioration de lisibilité du code.
Imaginez qu'on veut afficher la liste des noms de users séparés par un espace.
Avant:

names = ""
for user in users
    names << "#{user.user_username} "
end
return names.chomp

Après:

names = users.collect { |user| user.user_username}
return names.join(' ')

C'est tout simple mais quand on pas l'habitude; on y pense pas forcément ;)
Je découvre avec un peu de retard l'utilité de "collect()" mais mieux vaut tard que jamais :D
Technorati tags:

mercredi, mars 05, 2008

Assertion en ruby

Je me demandais si le mot clef "assert" existe en ruby... je ne l'ai pas trouvé mais par contre c'est très simple de le réinventer:

def assert(*msg)
  raise "Assertion failed ! #{msg}" unless yield if $DEBUG
end
Un exemple d'utilisation:

$DEBUG = true
i = 0
assert("mon msg") {i == 1}
Résultat:
Exception `RuntimeError' at assert.rb:2 - Assertion failed ! mon msg
assert.rb:2:in `assert': Assertion failed ! mon msg (RuntimeError)
        from assert.rb:7
source: dzone.com

Technorati tags:

mardi, mars 04, 2008

Problème avec Webrick et authenticate_or_request_with_http_basic

Je ne sais pas pourquoi mais il semblerait que le authenticate_or_request_with_http_basic de rails 2 ne fonctionne pas sous webrick (sous windows ?).

Je n'arrive pas à le faire marcher (je passe l'authentification comme si elle n'existait pas) en mode development ou production.
Par contre avec Mongrel ça fonctionne ?!

J'ai trouvé un thread où d'autres personnes ont l'air d'avoir le même genre de problème...
C:\...>ruby --version
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
C:\...>rails --version
Rails 2.0.2


Technorati tags:

lundi, mars 03, 2008

MD5 en Ruby

Pour mon autentification d'un utilisateur avec rails en utilisant la méthode "authenticate_or_request_with_http_basic" de Basic HTTP authentication, j'avais besoin de vérifer le mot de passe.

Pour celà, j'avais besoin de transformer le mot de passe saisi par l'utilisateur en md5 pour le comparer avec le md5 stocké en base (pour éviter de stocker des mots de passe en clair dans la base de données).
Et bien c'est très simple, il suffit de 2 lignes de code:

require 'digest/md5'
hash = Digest::MD5.hexdigest(password)
Dans le cas de mon application rails pour voir le code complet, ça donne:

class ApplicationController < ActionController::Base
  before_filter :authenticate
  
  protected
  
  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      auth = User.auth?(username, password)
      if(auth)
        session[:auth], session[:username] = true, username
        return true
      end
      return false
    end
  end
end

require 'digest/md5'

class User < ActiveRecord::Base
  # return true if (username and hashed_password) are correct
  def self.auth?(username, password)
    hashed_password = Digest::MD5.hexdigest(password)
    user = User.find(:first, 
        :conditions => "username='#{username}' and password='#{hashed_password}'" )
    logger.info "#{username}/#{hashed_password} > auth? : #{!user.nil?}"
    return !user.nil?
  end
end
Technorati tags:

jeudi, février 21, 2008

Enlever les lignes en double dans un fichier

Une petie astuce que j'ai posté sur le blog de torsten en réponse à sa question:
Comment afficher les lignes d'un fichier en enlevant les doublons (faire une sorte de 'unique' ou 'distinct' sur les lignes du fichier)

Je me suis dit que ça pourrait servir à d'autres donc je le repost ici:
puts IO.readlines('data.txt').uniq

Par contre sur un gros fichier ça doit consommer beaucoup de mémoire...

Technorati tags:

mardi, février 19, 2008

Convertir de l'utf-16 en iso-8859-1

Je savais que la gestion de l'utf en ruby (1.8) était un gros problème auquel je n'avais pas encore été confronté. Mais dans mon cas le plus gros problème a été de trouver une page web qui m'explique comment faire au lieu de se plaindre :(

Il y a tellement de pages sur des forums etc... qui expliquent qu'il y a un problème sans fournir de solution claire!

Finallement je suis tombé sur cette page de Obie qui m'a mis sur la bonne piste :)

Malheureusement dans son exemple je perdais les accents (c'est embêtant :( )

Voilà l'exemple corrigé pour transfromer de l'UTF-16 vers de l'ISO-8859-1. Pour UTF-8 il suffit de remplacer 16 par 8 étonnant non ? ;)



require 'iconv'

class String
  def utf16_to_iso
    converter = Iconv.new('ISO-8859-1//IGNORE//TRANSLIT', 'UTF-16')
    converter.iconv(self)
  end
end

content = IO.read("utf16.txt").utf16_to_iso
content.split("\n").each do |line|
  do_something(line)
end

IGNORE veut dire que Iconv va ignorer les erreurs de conversion. Si vous voulez être prévenu quand vous avez un caractère exotique, enlevez cette option.


TRANSLIT essaye de trouver le caractère le plus proche de celui de utf dans iso.

Petite astuce UTF-16, je n'ai pas pu utiliser la méthode: IO.foreach que j'aime beaucoup en temps normal parce que cette méthode split sur le caractère "\n" or en UTF-16 comme c'est stocké sur deux octets le retour à la ligne est "\n\000". J'ai donc converti la chaine avant de faire le split.


Technorati tags:

vendredi, février 15, 2008

Comment tester null ou chaine (string) vide

Récemment dans un petit script j'avais le code suivant:
unless(item.nil? or item.emty?)
  ...
end
Où item est soit nil, soit "", soit "foo". Je n'aimais pas faire ça partout dans le code, je voulais être plus "DRY" en faisant quelque chose comme:
unless(item.blank?)
  ...
end
La solution que j'ai trouvé dans un premier temps pour résoudre ce problème "esthétique" était de déclarer la fonction blank? dans String et NilClass; ça conne ça:

class NilClass
  def blank?
    true
  end
end

class String
  def blank?
    to_s.empty?
  end
end
Mais Benjamin C. m'a montrer une manière plus élégante en étendant Object:
class Object
  def blank?
    respond_to?(:empty?) ? empty? : !self
  end
end
Code re-pompé honteusement de rails.
Technorati tags:

mardi, janvier 15, 2008

Natural Sort 1.1.0

Sur les conseils de plusieurs personnes, notamment dans les commentaires, j'ai supprimé les méthodes "alias" du projet natural sort et j'ai fait une nouvelle release pour l'occasion: 1.1.0

Si vous voulez participer, n'hésitez pas à jeter un coup d'oeil dans les tests unitaires il y a quelques Fixme/Todo qui vous attendent ;)

Autre axe d'évolution: rendre paramétrable les différents cas de tri, avec une hash. Sachant que le cas par défaut doit être le plus facile possible pour les utilisateurs ;)

vendredi, janvier 04, 2008

Supprimer les répertoires de subversion d'un dossier

Rien de bien extraordinaire, juste un petit script pour supprimer tous les répertoires ".svn" d'un dossier et de ses sous-dossiers (quand on est sous windows et qu'on a pas de ligne de command/bash digne de ce nom)
require 'fileutils'
FileUtils.rm_r Dir["mon_dossier/**/.svn"]

mardi, décembre 18, 2007

Nouvelle Skin 2008

Pour finir l'année en beauté, j'ai changé la skin du site: rubyscube :)

Plus précisément je suis passé au nouveau système de template de blogger et j'ai cherché (difficilement) des skins (pour une liste de sites de template gratuit, allez voir mon del.icio.us à ce sujet).

J'ai essayé de repassé dans tous les posts (147 quand même!) pour vérifier qu'ils s'affichent à peu près correctement... mais c'est tout à fait possible que j'en ai raté.

J'en ai profité pour supprimer ma version "custom" de Syntax Highlighter avec la syntax Ruby (de toute façon je ne l'utilisais plus dans mes nouveaux posts).

Je ne fais pas trop d'illusion, la plupart des personnes que je connais utilisent des aggrégateurs (style google reader) et ne voit jamais le site... mais bon c'est pour me faire plaisir ;)

D'ailleurs à ce sujet j'ai peur de me lasser assez vite de cette nouvelle skin, seulement le changement a été suffisament long pour me disuader de recommencer de si tôt ;)

Ma deuxième inquiétude concernent les images qui pointent vers un site extérieur, si celui-ci est "down", ou pire, si les images sont supprimées, ma skin risque de ne pas être très jolie. On verra...


Technorati tags:

Nouveau projet "Natural Sort"

Suite à un article sur Coding Horror sur le fait que les tri par défaut (ASCII) ne sont pas très "human friendly" et après quelques recherches sur Google assez infructueuses sur le sujet en ruby... Je me suis amusé à créer un projet rubyforge pour implémenter ce type de tri :) Ca donne quelque chose comme ça:
['a1', 'a11', 'a12', 'a2', 'a21'].natural_sort => ['a1', 'a2', 'a11', 'a12','a21']
Si vous voulez en savoir plus: J'aimerai avoir votre avis sur le fait d'avoir fait plein de méthodes "alias" avec des noms différents pour contenter tout le monde... J'ai fait ça pour 2 raisons:
  1. plus de mots clef sous google pour trouver le projet
  2. moins à réfléchir quand on tape
A vos commentaires :)

Remarques:
  • Je me suis loupé dans la version 1.0.0 (un bug avec la méthode static), donc utilisez la version 1.0.1
  • Le gem met environ 1 jour avant d'apparaître dans la liste lorsqu'on fait "gem list naturalsort --remote"... soyez patient ;)
  • C'est loin d'être parfait mais ça fait déjà l'essentiel, si vous voyez des améliorations possible n'hésitez pas à utiliser le Tracker de rubyforge, j'ai aussi quelques tests unitaires marqués TODO ou FIXME qui ne demande qu'à être corrigés ;)

    Update: La version 1.0.1 est dispo maintenant :)
Technorati tags:

vendredi, décembre 14, 2007

Double Pipes

Sans jeux de mot scabreux hein ;)
Je me suis amusé un peu avec les || qui permet de sélectionner la première valeur en partant de la gauche qui n'est pas nil. Bien pratique pour initaliser des variables sans if/else
hash = {"b" => "bbb", "c" => "ccc"}
test1 = hash["a"] || hash["b"] 
puts "test1: #{test1}"

test2 = ENV['FAKE'] || ENV['JAVA_HOME']
puts "test2: #{test2}"

v1, v2 = "aaa", "bb"
test3 = v1.size || v2.size
puts "test3: #{test3}"

test4 = [1, 2].max
puts "test4: #{test4}"

Résultat:
test1: bbb
test2: C:\Program Files\Java\jdk1.5.0_10
test3: 3
test4: 2
Le plus intéressant d'après moi est le test n°2.
Technorati tags:

[RDoc] Paramètres de retour d'une fonction et exemple de code

Juste un petit post pour donner 2 astuces pour améliorer sa rdoc (alias ruby doc).
Comment faire une jolie rdoc comme celle de Enumerable par exemple:
Le code:
 # call-seq:
 #     class.new_method(string)                    => array
 #
 # Description.
 #     MyClass.new.new_method("hello")         #=> [1, 2, 3]
 def new_method(str)
   ...
 end
Deux points à remarquer:
  • Le call-seq (+ 1 ligne vide) pour désigner le type d'objet retourné et les paramètres
  • les 5 espaces (whitespace) devant l'exemple d'utilisation pour le faire apparaitre sous forme de code. (rem: <tt>code</tt> marche aussi)
Technorati tags:

mercredi, novembre 14, 2007

Variables d'environements N°2

Dans ma "quête" des variables d'environements en ruby (cf épisode n°1), je cherchais une manière "élégante" de trouver toujours une valeur quelque soit les variables d'environnement configurées et même l'OS (windows ou linux).

J'ai trouvé cette manière de faire qui ma foi me plaît bien :)
[ENV['PUBLIC'], ENV['APPDATA'], "/tmp"].compact.first
Imaginons qu'on se trouve sous linux, après évaluation des ENV ca donnera:
[nil, nil, "/tmp"].compact.first
"compact" va enlever les nil, donc on aura:
["/tmp"].first
et voilà!

d'autres idées ?

Technorati tags:

lundi, novembre 12, 2007

Variables d'environements

Comment récupérer la valeur d'une variable d'environement du système ?
Et bien c'est très simple ruby contient une variable ENV qui permet de récuper n'importe quelle valeur:
puts ENV['CLASSPATH']
Pour voir toutes les variables de votre système:
ENV.to_hash.each do |k, v|
 puts "#{k}\t => #{v}"
end
Technorati tags:

dimanche, septembre 23, 2007

Un peu d'humour... dans un monde de blog

Petit "comics" qui résume bien la situation sur la petite guerre entre Gavin King (Hibernate) et Obie Fernandez (Ruby) et plus globalement anti-Java/anti-Ruby. (le plus marrant c'est que Obie ressemble beaucoup)

Dès que j'ai vu le post de Gavin (que j'estimais beaucoup pour hibernate mais qui me décoit beaucoup depuis plusieurs posts), j'ai vu que ça allait partir en "cacahuètes" et ça n'a pas loupé!

Je crois qu'on va pouvoir ranger ce genre de troll (discussion sans fin où tout le monde reste sur ses positions avec comme argument "mon truc il est mieux que le tiens et puis c'est tout !") avec les autres:
  • vi(m) vs emacs
  • java vs .net
  • windows vs linux (vs mac)
  • langage compilé vs non compilé
  • postgres vs mysql
  • etc...

Technorati tags:

mercredi, septembre 12, 2007

Ruby net/https and invalid server certificate

Again in English since there is so few documentation. Here's the code to connect to a https server using ssl and certificate (using net/https standard lib):
def get_url(url, &block)
 puts "Getting url: #{url}"

 uri = URI.parse(url)
 http = Net::HTTP.new(uri.host, uri.port)
 http.use_ssl = true
 http.verify_mode = OpenSSL::SSL::VERIFY_PEER
 http.ca_file = 'ca.server.domain.com.crt'
 pem_file = 'certificate-privateKey.pem'
 http.cert = OpenSSL::X509::Certificate.new(File.open(pem_file).read)
 http.key = OpenSSL::PKey::RSA.new(File.open(pem_file).read)

 http.start {
   http.request_get(uri.path) {|res|
     @page.response = res
     yield
   }
 }
end
end
For the pem format see my other article: Https, Ssl and Ruby
If the server ca is "strange" (I don't know in which way I'm not an ssl expert), you will get those kind of error messages:
.../http.rb:586:in `connect': certificate verify failed (OpenSSL::SSL::SSLError)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = 'ca.server.domain.com.crt'
can be replace with
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
Technorati tags:

Installing ruby on Ubuntu

I know there is plenty of those post everywhere but with so much FUD.
The way I see it (and worked best):
  • sudo apt-get install ruby
  • sudo apt-get install rubygems
  • export RUBYOPT=rubygems (see rubygem doc for more details)
  • sudo gem install httpclient (only use "gem install" for ruby lib)
Technorati tags:

jeudi, septembre 06, 2007

Ecrire une bonne API en Ruby

Une fois passée la phase d'apprentissage du language (quelque soit le language d'ailleurs), on passe à l'étape suivante "Comment écrire du bon/beau code ?" (bon ou beau pour moi sont synonymes, plus d'info : "Code like a girl").

Le plus dur pour moi a été de dé-apprendre la manière de faire des api en Java ;)
Je vous conseille de lire le post de Chad Fowler: "Writing APIs to Wrap APIs", je trouve la partie sur l'utilisation des "method_missing" très intéressante.

I love the tricks you can do with Ruby. method_missing, const_missing, autoloading, and their friends make really powerful things possible.

But they do so at a price. When something goes wrong in a piece of code that relies heavily on one of these tricks, it can be much much harder to track down. So the decision to use such a tool shouldn’t be taken lightly. These are power tools. Used effectively, really cool things can happen. Used incorrectly, you can easily find yourself limb-less and bloody. So when you decide to use one of these power tools, you have to ask yourself: is it worth the risk?


Technorati tags:

Un exemple plus simple de "retry"

Un exemple d'utilisation de "retry" plus simple que mon post précédent:
i = 0
MAX = 3
begin
  puts "foo"
  raise "bar"
rescue
  i += 1
  retry if (i < MAX)
end

Résultat:
foo
foo
foo

Technorati tags:

Le mot clef "retry"

J'ai eu une drole de suprise quand j'ai voulu appeller une variable "retry", il existe un mot clef "retry"!
Ni une, ni deux je fonce voir la doc dans le "pickaxe": retry.
Devinez quoi ? Ca fait exactement ce que je voulais faire mais en mieux ;)
Je voulais implémenter un système de retry quand j'appelle un url qui me retourne un timeout.
Donc voilà le code:
require 'httpclient'

client = HTTPClient.new
url = "http://localhost:3000/foo"

i = 0
begin
 puts url
 resp = client.get(url)
rescue Timeout::Error
 i += 1
 $stderr.print "Timeout error: " + $!
 retry if (i < 2)
end
Ca a l'air de bien marcher :)
J'en ai découvert un autre en lisant la doc "redo"! Faut que je regarde la différence avec "retry"...
Sinon j'ai trouvé une petite list des mots clefs en ruby.
Technorati tags:

mardi, août 21, 2007

How to use HTTPS/SSL in Ruby

For once I will write this post in english since I found really few documentation / pages in google about this subject.

I made a simple tutorial to explain how to do get / post request using https/ssl, pem files and ruby, go check it out at: http://benjamin.francisoud.googlepages.com/howtodogetorpostrequestinhttpssslandruby

You can leave comments in this post :)


Technorati tags:

jeudi, juin 14, 2007

[ANN] Plugin Skinnable

Un peu d'auto promotion ;)

Le pitch:
Skinnable permet d'ajouter une skin à votre appli rails rapidement.
Ca fait un moment que j'ai ce plugin dans les cartons, je l'ai utilisé pour moi sur 2 projets (pour lesquels je ferais une annonce séparé). Mais je n'avais pas eu le temps de faire un jolie screencast.
Maintenant c'est chose faite :)

Si vous voulez que j'ajoute 2 ou 3 skins de plus; vous pouvez laisser un commentaire et je m'en occuperais quand j'aurai du temps :)

Technorati tags:

mercredi, juin 06, 2007

Aptana

Petit rappel pour ceux qui ne suivent pas dans le fond de la classe: Les développements sur RadRails (l'IDE pour rails basée sur eclipse) se sont arrêtés et que tout a été déplacé dans Aptana.

J'ai téléchargé la dernière version et je suis très agréablement surpris la plus part des fonctionnalités bien pratique dans l'équivalent pour Java sont présentes:
  • Completion
  • Rappel de l'assignation de la variable quand on laisse la souris dessus
  • Rdoc de la classe...

Quelques captures d'écran tiré du blog:




Pour plus de "tips & tricks" sur aptana jetez un coup d'oeil à leur blog.

Technorati tags:

DRb alias Distributed Ruby

Je viens de découvrir DRb (Distributed Ruby), une librairie incluse par défaut dans la distribution ruby (pas besoin de gem) pour faire du client/serveur.
Et alors me direz-vous ? Et bien c'est super simple !
Le mieux c'est un petit exemple:
Serveur:
require 'drb'

class MyServer
  def say
    "hello world"
  end
end

DRb.start_service("druby://:7777", MyServer.new)
DRb.thread.join
Client:
require 'drb'

DRb.start_service
remote_object = DRbObject.new(nil, 'druby://localhost:7777')
puts remote_object.say
Résultat dans la console du client:
hello world
Quand je pense aux EJB en java et que je vois ça...
Petit bémole, il faut redémarrer le serveur si on modifi