Que mange le monde
maj: je viens de me rendre compte que le système pour les commentaires était mis en mode modération, ce que je ne fesait pas. Dorénavant, les commentaires seront acceptés et les spams filtrés automatiquement. Je m'excuse pour ceux qui ont commentés et que ça n'a pas marché.
you.not_nil? { |you| read.this }
A thing that really annoys me is to check for nil values. For example having to do, in an ERB template
my template
<% unless a.super['long'].variable['accessor'].nil? %>
<%= do_something_with a.super['long'].variable['accessor'] %>
<%= do_something_else_with a.super['long'].variable['accessor'] %>
<% end %>
You'll say that I could use a local variable:
my template
<% the_var = a.super['long'].variable['accessor'] %>
<% unless the_var.nil? %>
<%= do_something_with the_var %>
<%= do_something_else_with the_var %>
<% end %>
But I prefer to have a more "Ruby" way for that:
my template
<% a.super['long'].variable['accessor'].not_nil? do |the_var| %>
<%= do_something_with the_var %>
<%= do_something_else_with the_var %>
<% end %>
It is mutch nicer. Even more for one liners:
<%= do_something_with some_really_long_var unless some_really_long_var.nil? %>
becomes
<%= some_really_long_var.not_nil? { |v| do_something_with v } %>
Here is the code for the "not_nil?" method:
1 2 3 4 5 |
class Object def not_nil? yield self unless self.nil? end end |
Get rid of user passwords in Rails using SSL client certificate
For an application I'm writing, I needed a way to log in without having to enter a password. You can authenticate a client against a server in Apache using SSL client certificate validation. The problem was to create a link between Apache and the Rails application running under Mongrel. Here is what I've done.
Apache and SSL
First, you'll need Apache set up with SSL and client certificate validation. There are howtos on the web on how to to that, so I won't explain that in full details here. You'll need a server private key and certificate and a client certificate issued by the same authority (or authorities trusting each others). For myself, I use CAcert which provides free "ethic" certificates.
I use Coda Hale great article to configure my rails apps. Here is what I added to the Apache configuration for having it working with SSL and client certificate validation. Please note that only the lines added or changed are listed here. For it to work, you need to enable the Apache 2 SSL and headers modules.
SSLEngine on
SSLCertificateFile /path/to/cert.pem
SSLCertificateKeyFile /path/to/private.key
SSLCACertificateFile /path/to/cacert.pem
SSLVerifyClient none
SSLVerifyClient require
SSLVerifyDepth 1
RequestHeader set X_FORWARDED_PROTO 'https'
RequestHeader set X_SSL_CLIENT_DN_Email "%{SSL_CLIENT_S_DN_Email}s"
So what's the meaning of all this ?
On line 1, we say that this virtual host is for connection on port 443 (don't forget to add a "Listen 443" directive to your Apache config).
Line 2 to 5, we simply configure server side ssl.
On line 6 to 12, we configure client certificate validation. The line 6 is usefull if you want to require validation only for some specific locations (say 'admin' for example). Line 9, we require client certificate validation for the '/' location. Line 10 says that we need to go up in the authority hierarchy of 1 level maximum (my client and server certificates are issued by the same authority).
On line 13, we tell rails that we are using the "https" protocol so that links outputted by call to "link_to", "url_for" and the likes need to start with "https" instead of "http".
SSL, Rails and Mongrel
This is the interesting part, specific to using Rails with Mongrel.
On line 14, we say to Apache to add the "X_SSL_CLIENT_DN_Email" header to the value of the "DN_Email" field in the SSL client certificate. This is the only way I have found to forward to Mongrel the SSL client certifcate values that are of interest to us.
Now in your controller, to fetch the email of the user connected:
1 2 3 4 5 6 7 |
class ApplicationController < ActionController::Base before_filter :ssl_login def ssl_login session[:user] = User.find_by_email(request.env["HTTP_X_SSL_CLIENT_DN_EMAIL"]) end end end |
If your Mongrel instances are only listening to your local host network interface (think 127.0.0.1), this should be secure:
- Apache will prevent anybody not having a valid client certificate to even reach your Mongrel instances,
- for the others, Apache sets the email of the certificate in a header and overwrite the header if already present (so client are unable to forge that header),
- assuming a common authentication scheme, if the user email is in your database, he is allowed to connect.
Please do note that if a hacker is able to connect to your Mongrel instances via your local host network interface, he can forge the header and pretend to be someone else. But if he has access to your local host interface, you probably are already in big troubles.
I hope you found this useful, any comments are of course welcome.
Version 0.2 of the Cache Test plugin
As the title implies, I have released a new version of my cache logic test plugin.
Ok, I must admit I have been a bit motivated by that post =P, but anyway, here you are with some new features.
First you are now able to check the actions and fragments caching logic: there are four new methods:
- assert\_cache\_actions - assert\_expire\_actions - assert\_cache\_fragments - assert\_expire\_fragments
They work quite like the corresponding page method, see the documentation for code and examples.
I also changed the way page caching test works. The API is still the same, just the internals have changed. Instead of checking that the page’s cache files are created or removed, I now hijack the ActionController::Base methods so that they records which page are cached or expired. I then use this information in the assertions. No more fiddling with the file system to know if a file is cached or not.
And finally, this release comes with some unit test cases.
The fragment code seems to be working, but I have no application that makes heavy use of fragments so if you find bug in it or have suggestions, don’t hesitate to tell me!
update: I think I have a better idea for the API something along the line of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def test_caching assert_caching do get "/my/page" assert_page_cached "/my/page" get "/my/action" assert_action_cached :controller => "my", :action => "action" get "/my/fragment" assert_fragment_cached :controller => "my", :action => "fragment", :fragment => "foo" end end |
I’ve already implemented it, I’ll wait for some feedback before releasing a new version.
Testing ruby on rails page caching
For the new version of iSearch, I used the page caching feature of Ruby on Rails a lot. I also needed to refresh those pages when some model where added, updated or deleted, so I needed to use cache sweeping too.
Now caching is great, but you need to be sure it works, and what's the best thing you can do to make sure it works? Write tests! But here we have a problem, as I could not find any information about page caching testing. So I decided to add some page caching test helpers to my application, and while I'm at it, released them as a plugin.
Here, I will explain how the plugin is working. Go to the plugin page if you want more information about how to download, install and use it.
First let's have a look at the init.rb file:
1 2 3 4 5 6 7 |
if RAILS_ENV == "test" require 'page_cache_test' Test::Unit::TestCase.class_eval do include Cosinux::PageCacheTest end end |
On line 1, we make sure that we are in the test environment so we don't interfere with other environments. If it is the case, we first load the page_cache_test.rb file on line 2. Then we include the module Cosinux::PageCacheTest in the Test::Unit::TestCase on line 4 to 6.
Now let's go in the page_cache_test.rb and see what happens when the Cosinux::PageCacheTest module is included.
1 2 3 4 5 6 |
module Cosinux::PageCacheTest def self.included(base) ActionController::Base.public_class_method :page_cache_path ActionController::Base.perform_caching = true end end |
The method self.included on line two, defined in the Module class is executed each time a module is included. So here it is executed because of the include we've just seen in the init.rb file.
In this method, on line 3, we change the visibility of the method page_cache_path of ActionController::Base so that we can use it in our assertions. The method is used to get the path of the cached file for a url.
Then, on line 4, we ensure that caching is enabled (it is normally disabled in the test environment).
So were are we now ? We included the Cosinux::PageCacheTest module in the Test::Units::TestCase class so each method defined in the module is available in the class. There are two instance methods defined in the module:
- assert_cache,
- and assert_expire.
Let's have a look at both of them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def assert_cache(*urls) silence do urls.each do |url| ActionController::Base.expire_page(url) end end if block_given? yield *urls else urls.each { |url| get url } end urls.each do |url| assert_block("#{url.inspect} is not " + "cached after executing block") do File.exists? page_cache_path(url) end end end |
First, on line 3 to 5, we ensure that the urls we are checking are not in cache by expiring them on line 4. We also make sure that no message is logged by using the silence method which is defined at the end of the file.
Then, on line 8 and 9, if a block is given, we execute it, giving it the urls as arguments. Otherwise, on line 11, we execute a get request on each of the given urls.
Finaly, on line 14 to 18, we make sure that the urls are cached by checking that the corresponding files exists on the file system.
Now to the other method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
def assert_expire(*urls) silence do urls.each do |url| ActionController::Base.cache_page("testing", url) end end yield *urls urls.each do |url| assert_block("#{url.inspect} is cached" + " after executing block") do ! File.exists? page_cache_path(url) end end end |
As you can see, it looks quite the same, excepts a few things.
First it makes sure that the urls are cached by writing "testing" in the corresponding files on line 4.
Then we always execute a block, so that the user can take some actions that should make the urls expire, like triggering an action which defines a sweeper.
And finally, on line 12, we assert that the files corresponding to the urls do not exists anymore.
That's it. I hope this will explain how this small plugin work and that it will help somebody. Next time, I will write about my experiments with a plugin to create multiple page forms.
new iSearch version
I have updated my index search engine, you can see (and use) the results at http://isearch.cosinux.org/.
What's new ?
- it uses Ferret to search the links, so you can use ferret search syntax, and search is quicker,
- it uses Hpricot to extract the links from the index so you can extract only the needed links,
- and I hope it has a better look.
I have created to plugin for this project that I will release one I find the time:
- a plugin to creates multiple page form,
- and a plugin to test page caching.
I'll try to blog about that soon.
Microformats
Have you heard of microformats ?
Microformats are usings predefined XHTML snippets to show common elements in web pages. For example to display a contact, you could use the hCard which allow you to represent a vCard (like those used in most email tools).
Why do that ? The hope is that in the near future, you will be able for example to drag and drop a contact on a web page directly into your contact manager application.
I think I will try to follow those recommendation, so that when the day has come, I will be microformats ready!
UPDATE: the day has already come, (technorati)[http://technorati.com/contacts/] has a small web application that converts and hCard to a vCard and allows your browser to download it and add it to your contact manager.
Quick Ruby on Rails tip
For one of my project, I have a Project model where project are referenced by their, hum..., reference. I use this reference to create nice URL like /project/myproj. The referenced is stored in the database in upper case so I always needed to use project.reference.downcase in the link_to or url_for methods.
I just found out that if you define a to_param method for an object. This method will be called when the object is given as a parameter for a URL. So I added
def to_param reference.downcase end
And now I can use url_for(:controller => "project", :reference => project)
Javascript 1.7 and new features
Firefox 2.0 beta is out and it supports javascript 1.7. The main new features of javascript 1.7 seems to be
- the introduction of generators (did you say yield?),
- the let expressions,
- and destructuring assignment
amongst others.
You can read an explanation on the Mozilla developers web site.
I find the generators thing a bit awkward. I find the Ruby one more intuitive. It's like it is used upside down.
I know the let expression thing from Lisp.
The destructuring assignment is a nice addition to javascript and allow you to do things like
[ a, b ] = function_returning_array()
Hopefully, these will make programming javascript a bit more intuitive. Let's hope Microsoft will integrate the technology in Internet Explorer 7.
Rails History 0.2
I've just release a new version of my Ruby On Rails history plugin.
It fixes a bug where a user variable was stored in a class variable instead of a session one so a user going back would make another one skip a page in its history.
It now doesn't store PUT and DELETE request.
It adds a new method peek_last_location to look at the history without modifying it, and a force parameter to the store_location method to force storing of a location even if it would normally be skipped (being a post request for example).
See the changelog for the full details or download the new release here.
Interview of CSS author Håkon Wium Lie
Slashdot has a nice interview of the men behind CSS, Håkon Wium Lie. If you don't know why we can't make a simple two column layout in pure CSS, read on.
Emacs and Ruby on Rails
;;;
;; css mode
(setq cssm-indent-function 'cssm-c-style-indenter)
;;;
;; mmm mode for editing rhtml files
(require 'mmm-mode)
(require 'mmm-auto)
(setq mmm-global-mode 'maybe)
(setq mmm-submode-decoration-level 2)
(set-face-background 'mmm-output-submode-face "LemonChiffon2")
(set-face-background 'mmm-code-submode-face "LavenderBlush2")
(set-face-background 'mmm-comment-submode-face "tan1")
(set-face-foreground 'mmm-output-submode-face "Black")
(set-face-foreground 'mmm-code-submode-face "Black")
(set-face-foreground 'mmm-comment-submode-face "Red")
(mmm-add-classes
'((erb-code
:submode ruby-mode
:match-face (("<%#" . mmm-comment-submode-face)
("<%=" . mmm-output-submode-face)
("<%" . mmm-code-submode-face))
:front "<%[#=]?"
:back "%>"
:insert ((?% erb-code nil @ "<%" @ " " _ " " @ "%>" @)
(?# erb-comment nil @ "<%#" @ " " _ " " @ "%>" @)
(?= erb-expression nil @ "<%=" @ " " _ " " @ "%>" @))
)))
(add-hook 'html-mode-hook
(lambda ()
(setq mmm-classes '(erb-code))
(mmm-mode-on)))
(add-to-list 'auto-mode-alist '("\\.rhtml$" . html-mode))
;;;
;; yaml mode
(autoload 'yaml-mode "yaml-mode" "YAML" t)
(setq auto-mode-alist
(append '(("\\.yml$" . yaml-mode)) auto-mode-alist))
;;;
;; rails mode
(defun try-complete-abbrev (old)
(if (expand-abbrev) t nil))
(setq hippie-expand-try-functions-list
'(try-complete-abbrev
try-complete-file-name
try-expand-dabbrev))
(require 'rails)
(setq rails-use-mongrel t)
Dissecting the Rails History plugin, or a little metaprogramming by example
Xavier Noria has written an article on one of the cool features of Ruby, metaprogramming. He does so by looking through the source code of my Rails History plugin.
Barcamp is behind us
So yersteday was Brussel's Barcamp day. I had a good day, meeting Belgian Rails developer, notably the two people of shoob.
I first assisted at Denis Lamotte presentation of Ruby on Rails. It was a well done quick tour of the main ROR features, it just lacked a bit of examples.
Then I went to see Denis Balencourt & Clo Willaert talking about History & Future of the web. The history part was mostly things I know like the birth of the net, the Amazon revolution, Slashdot, and events all the way up to today. It was nevertheless quite interesting. The second part was about the future of the net. Interesting ideas but the thing with predicting future trends is that you never know when a big revolution will occurs, nor what those revolutions will be about, so maybe in two month from know all those predictions will prove false. All in all, it was a good presentation, although I most enjoyed the first part.
The next presentation I saw was from Frank Lauwers about Hosting Web 2.0. He explained the current trends in web hosting, most notably that as web application are evolving, the customers need switched from disk space and bandwith to CPU and RAM. CPU is now the barrier on how much host you can put on a same server. He then explained that today, their problem is that customer still want to pay for space and bandwidth and that they are having a hard time getting paid for CPU time. He then went onto debatting with us about the specific needs of Web 2.0 developer. It was enlightening to hear a hardware guy and a nice presentation.
Bruno Peeters, Measuring web sites was the next presentation. He talked about ways of measuring blog influence on the web. He first explaind the criteria that can be used for measuring influence and then listed some of the existing solutions. Has I am more of a technical guy, this presentation was less interesting to me.
I then went on with Top of the Pops in Ruby, Peter Forret's presentation. He showed the application he had developped, and explained where ruby helped him. His application is grabbing playlists from Studio Brussel and make statistics out of them. I think he has a good idea, but he will shortly have IP problems.
Then François Lamotte & Denis Balencourt demonstrated their system for leaving audio comments on a Typo blog. It's a nice example of integration of Ruby on Rails and Asterisk, but I don't see myself phoning to a server to leave a comment on a blog. But it surely serves as a demonstration of OpenSource technologies integration and the innovations they allow.
The last thing I saw was "A RubyOnRails application for a broadcast company: success factors and lessons learned" by Alain Ravet. Alain showed us the contact management application he created for the VRT. A great example of agile development with Rails. He then explained us what he has learned from his project. It was a really interesting talk as it is one of the few project I know of in Belgium developed for the enterprise with Ruby On Rails.
We then had a drink with all the Barcampers and that was the end of the day.
I did not present my talk as I did not think it would interest people. I was going to show the CONNIE web site I've developed, but it's just another web site. The only interesting thing is that it is made with Rails, and that it didn't took long to create, but that's hard to show in a presentation. The next time, I'll present a more technical topic, diving in some code. As I'm more interested in that kind of thing, I will surely feel more confident in myself.
All in all, it was a great day. I enjoyed meating Ruby On Rails people and the presentations I saw were all good. We're planning on creating a community around Ruby On Rails developer in Belgium, we'll see how that goes in the next month.
Last but not least, I have to thank Peter Forret for organizing such a nice event.
Brussels barcamp
On the 20th of May will be hold the Brussels barcamp . I will be meeting other Belgian rails developers there (and other people too) and I'll be giving a presentation of my work on the CONNIE Belgian Portal.
The CONNIE Belgian Portal is a web site developed using RubyOnRails which use most of the plugins I've developed and has some Web 2.0 features using the great Scriptaculous javascript library. I'll give a general overview of the portal and then proceed with presentation of the interesting features using Scriptaculous and how I integrate them in RubyOnRails in a way wich makes the site work even without javascript:
- the box shading and reordering tool,
- the javascript popup framework,
- the bookmark system.
I might also presents the plugins I've developed:
I find these thing interesting to present as it shows how extensible the Rails framework is.
I could also talk about the use of the Globalize plugin to allow translation of RoR web applications, as it is particularly usefull in Belgium.