Wednesday, December 30, 2009

Migrer vers ruby 1.9

Je viens de migrer deux applications Rails 2.3.5 vers Ruby 1.9.1 avec deux problèmes majeurs concernant les encodings. Bien que Ruby soit unicode par défaut, pas mal de gems ne parlent pas l'UTF-8 par défaut...

Premier soucis avec ERB qui compile les templates en utilisant ASCII-8BIT par défaut. Cela génère l'erreur suivante:
ActionView::TemplateError (incompatible character encodings: ASCII-8BIT and UTF-8)

Il y a pas mal de discussions sur le sujet ici: https://rails.lighthouseapp.com/projects/8994/tickets/2188-i18n-fails-with-multibyte-strings-in-ruby-19-similar-to-2038

Une solution hacky à coup de monkey patch est de créer un fichier lib/actionview_utf8 comme ceci (c'est la ligne avec le source.force_encoding qui est rajoutée au code original):

module ActionView
  module Renderable #:nodoc:
    private
      def compile!(render_symbol, local_assigns)
        locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join

        source = <<-end_src
          def #{render_symbol}(local_assigns)
            old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
          ensure
            self.output_buffer = old_output_buffer
          end
        end_src
        source.force_encoding(Encoding::UTF_8) if source.respond_to?(:force_encoding)

        begin
          ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
        rescue Errno::ENOENT => e
          raise e # Missing template file, re-raise for Base to rescue
        rescue Exception => e # errors from template code
          if logger = defined?(ActionController) && Base.logger
            logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
            logger.debug "Function body: #{source}"
            logger.debug "Backtrace: #{e.backtrace.join("\n")}"
          end

          raise ActionView::TemplateError.new(self, {}, e)
        end
      end
  end
end

puis d'ajouter les lignes suivantes à environnement.rb:

Rails::Initializer.run do |config|
  ...
  config.after_initialize do 
    require 'lib/actionview_utf8'
  end
end

Ensuite je suis tombé sur le problème des adaptateurs aux bases de données qui retournent de l'ASCI-8BIT au lieu d'UTF-8 à la lecture des données. Une solution du même acabit est décrite ici: http://gnuu.org/2009/11/06/ruby19-rails-mysql-utf8/. Ceci dit ça ne fonctionne qu'avec MySql.

Donc on créé le fichier lib/mysql_utf8:

require "mysql"

class Mysql::Result
  def encode(value, encoding = "utf-8")
    String === value ? value.force_encoding(encoding) : value
  end

  def each_utf8(&block)
    each_orig do |row|
      yield row.map {|col| encode(col) }
    end
  end
  alias each_orig each
  alias each each_utf8

  def each_hash_utf8(&block)
    each_hash_orig do |row|
      row.each {|k, v| row[k] = encode(v) }
      yield(row)
    end
  end
  alias each_hash_orig each_hash
  alias each_hash each_hash_utf8
end

et pour environnement.rb on se retrouve avec:

Rails::Initializer.run do |config|
  ...
  config.after_initialize do 
    require 'lib/actionview_utf8'
    require 'lib/mysql_utf8'
  end
end

ouf, tous les tests passent...

Tuesday, December 29, 2009

Zend Framework avec lighthttpd

Je vais travailler sur un projet en PHP avec Zend Framework. Venant du monde Rails, on s'habitue assez vite à lancer son serveur web dans un terminal, voir plusieurs en parallèle sans avoir à manipuler la configuration d'un Apache qui tourne sous un autre compte utilisateur.

En PHP je n'ai pas trouvé d'équivalent à Webrick ou Mongrel intégré à ZF, mais je m'en suis finalement sorti avec lighthttpd.

A la racine du projet ZF, créez un lighthttpd.conf dans lequel vous configurez entre autres:
  • le port 3000 (on ne change pas de vieilles habitudes :)
  • l'URL rewriting pour que toutes les requêtes passent par index.php (l'équivalent du .htaccess tel que créé par ZF)
  • l'accès à votre application via FastCGI


ce qui donne quelque chose comme suit:


server.document-root = env.PWD + "/public"
server.port = 3000

server.modules = (
  "mod_rewrite",
  "mod_fastcgi"
)


mimetype.assign = (
  ".html" => "text/html", 
  ".txt" => "text/plain",
  ".jpg" => "image/jpeg",
  ".png" => "image/png" 
)

url.rewrite-once = (
  ".*\?(.*)$" => "/index.php?$1",
  ".*\.(js|ico|gif|jpg|png|css)$" => "$0",
  "" => "/index.php"
)

static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

server.indexfiles = ( "index.php" )

fastcgi.server = (
  ".php" =>
    ( "localhost" =>
      (
        "socket" => "/tmp/php-fastcgi.socket",
        "bin-path" => "/usr/bin/php-cgi"
      )
    )
)

Ensuite un petit script bash pour nous faciliter le lancement du serveur et l'initialisation de l'environnement ZF, par exemple start_zf.sh.

#!/bin/sh
export APPLICATION_ENV=development
lighttpd -f lighthttpd.conf -D

N'oubliez pas de rendre le script exécutable:
chmod +x start_zf.sh

puis lancez l'accès à votre projet ZF:

./start_zf.sh

Si tout va bien le site est accessible sur http://localhost:3000

Friday, December 18, 2009

SimpleWebDoc et yUML

Je viens d'interfacer SimpleWebDoc et yUML pour générer les diagrammes d'héritage entre les classes d'un même package. Le tout à un clic de souris.

Le package Tests:



Le package Kernel:



Saturday, December 12, 2009

SimpleWebDoc sur SqueakSource

Suite à mon billet précédent, Pharo et documentation des packages, j'ai continué a explorer Pharo pour ressortir les méthodes des classes et les commentaires contenus en en-tête des méthodes.

J'ai mis le package SimpleWebDoc sur SqueakSource. Cela me donne l'occasion d'expliquer comment charger un package depuis Monticello. Tout d'abord lancez le Monticello Browser depuis le menu World.




Cela ouvre une fenêtre dans laquelle on voit les packages installés dans le panneau de gauche, les repository (dépôts) dans le panneau de droite. L'étape suivante est d'ajouter le repository où se trouve SimpleWebDoc dans la liste de ceux enregistrés dans Monticello. Cliquez sur le bouton +Repository et sélectionnez HTTP (Note: un repository Monticello est un simple répertoire local ou distant où se trouve les packages zippés, avec l'extension .mcz).



Saisissez ensuite l'emplacement du repository, ici: http://www.squeaksource.com/SimpleWebDoc




Le repository ajouté, sélectionnez-le et cliquez Open.



Une fenêtre s'ouvre listant tous les packages présents dans le repository (panneau de gauche) et la liste des versions disponibles (panneau de droite), la plus récente en tête. Sélectionnez la version la plus récente et cliquez sur Load pour charger le package SimpleWebDoc.



SWDocView est automatique enregistré comme application Seaside au chargement. Si vous regardez la méthode de classe initialize de SWDocView:


initialize
  self registerAsApplication: 'view-doc'

Une classe est un objet, et comme tout objet son instanciation appelle initialize. En chargeant le package SimpleWebDoc, on instancie la meta-classe SWDocView (elle même instance de MetaClass). SWDocView s'enregistre alors comme application nommée view-doc.


Vous pouvez donc ouvrir votre navigateur web préféré et aller à l'adresse:




Les méthodes de classe sont en italique et soulignées:



Thursday, December 10, 2009

The Smalltalk way to learn Smalltalk

Pour apprendre un nouveau langage, avant de me jeter sur le compilateur / interpréteur et tenter d'écrire un (mauvais) programme, j'achète un ou deux livres bien perçus pour m'imprégner de la philosophie et objectifs du langage. Je lis quelques programmes.

Pour Smalltalk je crois avoir commis une erreur et j'ai mis du temps à tout simplement "rentrer dedans", bien plus que tous les langages profondément objets étudiés auparavant (dont Ruby ).

I'm still amazed by how many people think they can grok Smalltalk by seeing syntax examples. Smalltalk isn't its syntax, it's its environment. Smalltalk is a living world of running objects, there are no files, no applications, just what's running. To understand Smalltalk, you have to either actually use it for a while, or have a seasoned Smalltalker demonstrate it to you. Reading sample code just won't cut it.
(Ramon Leon, Why Smalltalk)

C'est un vrai conseil ;)

Et je comprends maintenant ceci: Object oriented programming means something entirely different to a Smalltalker than to someone doing OO in another language.

Au passage, le blog de Ramon Leon OnSmalltalk est une vrai mine d'or, articles complets, bien écrits, profonds et les commentaires n'en valent pas moins.