I'm pleased to announce the very first release (v0.1) of RTF-Extensions, a Ruby library that adds a few useful extensions to the excellent Ruby RTF package originally developed by Peter Wood.

RTF-Extensions has two main interests for Ruby developers who write code for generating RTF documents.

  1. It can handle character strings encoded in UTF-8 and generate RTF documents that preserve those Unicode characters.
  2. It offers a facility to add hyperlinks to a RTF document.

RTF-Extensions is NOT a replacement for Ruby RTF. It comes as an addition and shall be used on top of the original Ruby RTF.

For more details, please visit the project homepage on GitHub.

I work with a few clients who do not own a source code repository. I try to educate them about the benefits of putting their project under a Version Control System but it takes time to convince them.

As the developer of their application, I feel the need for such a service. I want to track the changes made to the source code, tag a version when I deliver a release or be able to figure out what precisely changed between 2 releases.

That’s why I had to find my own solution. Since I know Subversion, I came up with a Subversion repository installed on my Macbook. Actually it’s pretty simple and here are the steps to do that.

Firstly, install the whole Subversion package on your Mac. There used to be many variants available, depending on the version of OS X you use. Now it looks like CollabNet glue them all into one Universal binary object, which is a great initiative.

Once installed, use a terminal (iTerm is cool) to create your own SVN repository. I put it under my home directory since it’s for a personal use.

$ mkdir -p ~/repositories/svn
$ svnadmin create ~/repositories/svn/local

Since I want a simple setup, I use the svnserve server available with the Subversion package. Edit svnserve.conf to configure how authentication and authorization works. I just change the general section to disallow anonymous access, grant all permissions to any authenticated users and ask svnserve to look into the passwd file for valid credentials.

$ vi ~/repositories/svn/local/conf/svnserve.conf

[general]
### These options control access to the repository for unauthenticated
### and authenticated users.  Valid values are "write", "read",
### and "none".  The sample settings below are the defaults.
anon-access = none
auth-access = write
### The password-db option controls the location of the password
### database file.  Unless you specify a path starting with a /,
### the file's location is relative to the directory containing
### this configuration file.
### If SASL is enabled (see below), this file will NOT be used.
### Uncomment the line below to use the default password file.
password-db = passwd

Now edit the passwd file to create a valid login/password credential you will use to access the local repository.

$ vi ~/repositories/svn/local/conf/passwd
[users]
mylogin = mypassword

At this stage, you’ve got the simplest Subversion configuration and it’s time to test it. Launch the Subversion server in test mode and try to connect to it through your favorite SVN client.

$ svnserve -d -r ~/repositories/svn/local

Use the svn://localhost URL and try to create a folder or import an existing project to be sure you are granted the commit permission. If everything works fine, congratulations: you’ve got your own local Subversion repository where you can put under control the code of your project. One more thing you can do is to ensure it’s started when you boot your Mac. I use xinetd to do that. The Subversion service is already mentioned in /etc/services (under the svn alias) so you just have to add a configuration file for xinetd.

$ # kill the existing svnserve daemon
$ killall svnserve
$ sudo vi /etc/xinetd.d/svnserve

# default: on
# Subversion server

service svn
{
    socket_type = stream
    protocol    = tcp
    user        = <mylogin>
    wait        = no
    disable     = no
    server      = /usr/local/bin/svnserve
    server_args = -i -r /Users/<mylogin>/repositories/svn/local
    only_from   = 127.0.0.1
    port        = 3690
}

Replace <mylogin> by your own login. Note that I restrict the access to local clients. Then ask xinetd to reload its configuration.

$ sudo kill -HUP `cat /var/run/xinetd.pid`

and verify that xinetd listens to incoming SVN clients.

$ netstat -an | grep LISTEN | grep 3690
tcp4       0      0  *.3690                 *.*                    LISTEN

You’re done ! Being located on my laptop, I can have it with me anytime, regardless of any connectivity constraints. However, it makes it more difficult to share with other developers. May be Git would be more adequate to fulfill such a requirement.

UPDATE for Snow Leopard (and probably Leopard too)

After migrating to Snow Leopard, I lost the xinetd configuration file because Apple deprecates xinetd in favor of their own startup technology, launchd. Though I had a nice /etc/xinetd.d-migrated2launchd/ directory on my filesystem, it was empty and nothing was actually migrated.

After googling a bit, I figured out I had to configure svnserve for launchd. Here is the org.tigris.subversion.svnserve.plist file I put in place under /Library/LaunchDaemons.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Debug</key>
  <false/>
  <key>GroupName</key>
  <string>mygroup</string>
  <key>Label</key>
  <string>org.tigris.subversion.svnserve</string>
  <key>OnDemand</key>
  <true/>
  <key>Program</key>
  <string>/usr/local/bin/svnserve</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/svnserve</string>
    <string>--listen-port=3690</string>
    <string>--listen-host=127.0.0.1</string>
    <string>--inetd</string>
    <string>--root=/Users/<mylogin>/repositories/svn/local</string>
  </array>
  <key>ServiceDescription</key>
  <string>SVN Version Control System</string>
  <key>Sockets</key>
  <dict>
    <key>Listeners</key>
    <array>
    <dict>
      <key>SockFamily</key>
      <string>IPv4</string>
      <key>SockServiceName</key>
      <string>svn</string>
      <key>SockType</key>
      <string>stream</string>
    </dict>
    <dict>
      <key>SockFamily</key>
      <string>IPv6</string>
      <key>SockServiceName</key>
      <string>svn</string>
      <key>SockType</key>
      <string>stream</string>
    </dict>
    </array>
  </dict>
  <key>Umask</key>
  <integer>2</integer>
  <key>UserName</key>
  <string>mylogin</string>
  <key>inetdCompatibility</key>
  <dict>
    <key>Wait</key>
    <false/>
  </dict>
</dict>
</plist>

From here, you can start the service.

$ sudo launchctl load /Library/LaunchDaemons/org.tigris.subversion.svnserve.plist
$ sudo launchctl start org.tigris.subversion.svnserve

References

Subversion Book

  1. Creating and Configuring Your Repository
  2. svnserve, a Custom Server

Kasia in a nutshell Setting up Subversion as an xinetd process (on Linux)

svnserve launchd item for OS X 10.4

Hiding passwords in Rails log files

If you matter a bit about security, you certainly don't want the user's passwords to be cleartext in the log files of your Rails application. Believe me (or not) but your application is very likely making all the passwords available in production.log if you have not taken any counter-measure yet.

Just because a password is a parameter like the others most of the time, it consequently gets logged by Rails when it reaches a controller method. This is a bit annoying considering the time we spend on advanced security options like a SSL setup or the hashing of passwords in the database.

Hopefully, Rails has the solution, easy to setup. Once again, it's a bit hidden in the tons of methods the framework proposes, but this is the charm and beauty of it.

Assuming you use the word 'password' to name every parameter that deals with a password (in the registration, login and user edition forms), just add one line of code in the app/controllers/application.rb file.

class ApplicationController < ActionController::Base
  # filter out password parameters from log files
  filter_parameter_logging :password  
end

If your naming scheme is a bit more complex, don't worry because filter_parameter_logging accepts a list of words and a block that should be enough for matching all the cases. If it does not convey your choices, think about changing your code to simplify your naming of parameters.

After doing so, the value of any password parameter received by a controller appears as [FILTERED] in the log files of your Rails application, whatever the current environment is.

Thanks Maintainable Software for mentioning this Rails feature in their Rails Logging Tips article.

agiletour.jpgHier après-midi, j'ai assisté à la conférence Agile Tour 2008 (AT2008) organisée à Grenoble par le Club Agile Rhône-Alpes (CARA).

Cette manifestation se déroule dans plusieurs villes en France mais aussi à Genève tout au long du mois d'octobre 2008. Elle a pour but de promouvoir les pratiques agiles dans le développement logiciel. Cette année, les (gentils) organisateurs ont recensé 800 participants au total (dont un peu - de 200 à Grenoble), les inscriptions étant clôturées 2 mois avant les séminaires. Un franc succès donc.

Ce sont Alexandre Boutin et Patrice Petit qui nous ont accueillis dans l'amphi nord de l'université de physique, sur le campus de Saint Martin d'Hères. 3 faits et dires marquants ont plus particulièrement retenu mon attention lors de cette introduction.

  • Atos Origin, l'un des sponsors de la manifestation, réalise un projet stratégique sur 7 ans pour le compte d'ERDF (changement des 35 millions de compteurs électriques chez les particuliers) en mettant en oeuvre une démarche agile,
  • Pyxis, l'invité canadien, qui déclare "faire du logiciel et s'éclater", tout cela grâce aux pratiques agiles,
  • Tout le monde se (re)positionne sur l'agilité: c'est visiblement tendance et sans doute vendeur; ce qui est une bonne et une mauvaise chose à mon humble avis.

A suivi une 'performance' appréciée par le public de la part de Jérôme Barrand, "l'invité du 'dîner de con', toute chose égale par ailleurs...", sur l'agilité, un nouveau paradigme managérial. Mr Barrand, professeur à l'Ecole de Management de Grenoble et consultant, ne s'est pas gêné pour accuser l'informatique et les technologies de l'information d'être la cause de tous les maux de notre société actuelle. Provocation qui a réussi à amuser et capter l'attention de l'assistance. J'ai pris pas mal de notes sur le 'show' et tâcherai d'en faire un prochain post. Sans doute la séance la plus marquante de l'après-midi.

J'ai personnellement assisté à la présentation de Ruby on Rails effectuée par Sogilis, dont j'ai fait la connaissance des fondateurs. Il est toujours agréable de constater que d'autres font les mêmes paris que vous (en l'occurrence RoR) et ont une (courte) expérience assez similaire. Au sortir de cette présentation, on a pu sentir la curiosité et l'intérêt suscité par Rails au niveau régional, sans toutefois pouvoir dire si l'adoption augmente significativement.

J'ai aussi assisté à la présentation sur l'Architecture Agile et la maîtrise de la production logicielle en environnement agile. 2 retours d'expérience très intéressants, où l'on a pu vérifier que l'agilité en développement logiciel ne signifiait pas perte de contrôle. De nombreux outils existent ou sont créés par les entreprises qui adoptent ces pratiques pour piloter et accompagner au mieux les équipes agiles.

Il reste que d'outils, de mesure, de maîtrise et de contrôle, il en a été souvent question. Pour ma part, l'agilité reste avant tout une manière d'aborder un projet de développement logiciel, un état d'esprit en quelque sorte. Les principes régissant les pratiques adoptées par l'équipe étant ceux énoncés par le Manifesto. Du moment que vous adhérez et cherchez à appliquer ces principes dans votre environnement, de manière outillée ou non, vous basculez dans l'agilité. A moins que vous n'y soyez déjà...

Enfin, cet après-midi a aussi été l'occasion de retrouvailles ou de rencontres fortuites qui ont donné lieu à des discussions enrichissantes. RDV est donc pris pour l'Agile Tour 2009.

Parsing Accept-Language in Rails

Recently, I had to know which language is preferred by someone visiting the web site I was developing for. Hopefully, HTTP is well designed enough that you should not have to ask your visitors for this preference but can elegantly guess what it is by analyzing the Accept-Language HTTP request header.
This header is seamlessly sent to the HTTP server by your browser. It's clearly defined by the HTTP 1.1 specification if you want all the details. Briefly, the value is a "set of natural languages that are preferred as a response to the request.". Each language "may be given an associated quality value which represents an estimate of the user's preference for the languages specified by that range. The quality value defaults to 1."
To leverage this header, I simply wrote a "helper" method that I made available to all the controllers. I named this method accepted_languages and put it on the ApplicationController class (RAILS_ROOT/app/models/application.rb). It takes no parameter and returns an array where each element is a pair [language tag, quality], sorted by ascending quality.
Here's the code

class ApplicationController < ActionController::Base
  
  # ...
  
  ##
  # Returns the languages accepted by the visitors, sorted by quality
  # (order of preference).
  ##
  def accepted_languages()
    # no language accepted
    return [] if request.env["HTTP_ACCEPT_LANGUAGE"].nil?
    
    # parse Accept-Language
    accepted = request.env["HTTP_ACCEPT_LANGUAGE"].split(",")
    accepted = accepted.map { |l| l.strip.split(";") }
    accepted = accepted.map { |l|
      if (l.size == 2)
        # quality present
        [ l[0].split("-")[0].downcase, l[1].sub(/^q=/, "").to_f ]
      else
        # no quality specified =&gt; quality == 1
        [ l[0].split("-")[0].downcase, 1.0 ]
      end
    }
    
    # sort by quality
    accepted.sort { |l1, l2| l1[1] <=> l2[1] }
  end
end
Look at how you get the request header inside Rails. It's available from the environment attached to the request through a Ruby home-made key (HTTP_ACCEPT_LANGUAGE). This is due to how the HTTP headers are transmitted between the Rails framework and the HTTP server that stands upfront. This is directly derived from how the CGI standard Ruby library does for passing an HTTP request from a web server to a standalone program, here your Rails application.
Most of the time, your controllers only want to retain one language, the one presumably preferred by the visitor. Additionally, your application only supports a few languages. This is the context I had to fit into and I found useful to add a second method to the ApplicationController class.
This method, named preferred_language, takes 2 optional parameters

  1. an array that contains the list of language( tag)s supported by your application; it is used to filter out the set of languages accepted by the visitor.
  2. a default language (tag) that is returned to the caller when the browser does not expose any accepted languages or not a single language that is supported by the application; this second parameter represnts the default language of your application.
By default, the application supports any language but has a preference for english.
class ApplicationController < ActionController::Base
  
  # ...
  
  ##
  # Returns the language preferred by the user.
  ##
  def preferred_language(supported_languages=[], default_language="en")
    
    # only keep supported languages
    preferred_languages = accepted_languages.select {|l|
     (supported_languages || []).include?(l[0]) }
    
    if preferred_languages.empty?
      # the browser does accept any supported languages
      # => default to english
      default_language
    else
      # take the highest quality among accepted (and thus supported) languages
      preferred_languages.last[0]
    end
  end
end
If you want to reuse this piece of code (what it's meant for), please remember that it does not take care of country codes. As explained in the HTTP spec, each language tag starts with an ISO-639 language abbreviation and may be followed by an ISO-3166 country code. The latter is simply ignored by this code.
Finally, the parsing and exposure of all the HTTP headers are worth writing a Rails plug-in or a gem. I might do it one day unless someone points me at a similar contribution.


UPDATE: This morning, I found the HTTP Accept-Language plugin that does exactly what I describe hereby, quite similarly. The methods used for accessing the preferred language(s) are bound to the request object, which I admit is better. I'm quite sure this plugin did not exist when I initiated this development (some months ago).

UPDATE (2013/05/10): Naomi Kyoto suggested a much more concise and elegant code snippet for the accepted_languages method. I recommend to use her gist. Thanks for sharing !



About Argyl

Argyl is the company that I co-founded with Manfred Dardenne. This is a small consulting firm specializing in software develoment for web 2.0 projects.

Located in the French Alps (Grenoble - Isère), Argyl supports companies that have web 2.0-flavoured projects by offering its technological know-how and its capacity of transforming a rough idea into a working system at a fast pace.

Argyl’s offering puts the emphasis on

  • Mashups combining Web 2.0 ingredients like Javascript libraries (Google Maps, YUI, Prototype, ...),
  • Rich User Interfaces, HTML-based, CSS-styled, AJAX-driven,
  • Ruby and Ruby on Rails,
  • The principles and practices of Agile Software Development as described by the Manifesto for Agile Software Development and implemented by XP: customer-driven development, (talented) people first, just-enough processes, continuous integration, unit testing, iterative development.

Both company founders have a solid background in Java, MySQL, security (TLS, SSL, authentication, access control) and search engine technologies (Sphinx, Ferret).We're now fluent in Ruby and Rails

We can contribute in all the essential phases of your web software lifecycle, from gathering requirements to deploying the application on the appropriate infrastructure.

Our recent engagements include

  • Developing Conversation™, a social media marketing application proposed by eCairn Inc.. Based on Ruby, RoR, MySQL, Sphinx, Webistrano, ...
  • Recruiting and coaching for a billboard management company on a cartographic application. Uses Google Maps and Yahoo UI.

DoS Vulnerability in REXML: Patch the fix !

Last month, a denial of service (DoS) vulnerability was detected in REXML by the Ruby security team. Even though (RE)XML is not at the core of the Rails framework, most RoR applications shall be affected by this flaw.

The details are described on Riding Rails, the official RoR weblog. This is where you can get a fix and the instructions to install it.

Unfortunately, this fix does not always act properly. If, after applying it, your application starts complaining about some unexpected nil object on line 21 of rexml-expansion-fix.rb, you probably hit the glitch.

Here's what the error looks like for me.

You have a nil object when you didn't expect it!
The error occurred while evaluating nil.record_entity_expansion!
/lib/rexml-expansion-fix.rb:21:in `unnormalized'
/usr/local/lib/ruby/1.8/rexml/doctype.rb:135:...

As first explained by Dan Croak in the comments of the Riding Rails original post, the fix does not properly handle the DOCTYPE entity. Dan suggested a fix to the fix that makes the code work for any use. But it leaves a vulnerability on the expansion of doctype entities.

It happens that I got to the very same fix before I found Dan's comment... Just like me, you can get happy again with Rails and REXML by replacing the line 21 of the fix by

document.record_entity_expansion! unless document.nil?

Hope that helps.

Recent Entries

Recent Comments

Ruby and Rails> Recommended