When I’m testing admin controllers, I often have tests that follow this form:

test/functional/admin/orders_controller_test.rb

1 class OrdersControllerTest < ActionController::TestCase
2 logged_in_as :active_user do
3 context "on GET to :index" do
4 setup do
5 get :index
6 end
7
8 should_deny_access
9 end
10 end
11
12 not_logged_in do
13 context "on GET to :index" do
14 setup do
15 get :index
16 end
17
18 should_deny_access
19 end
20 end
21 end

Well, this is all Ruby, right? And Ruby has wonderful blocks, and blocks can be passed around…

test/functional/admin/orders_controller_test.rb

1 class OrdersControllerTest < ActionController::TestCase
2 deny_access_tests = lambda do
3 context "on GET to :index" do
4 setup do
5 get :index
6 end
7
8 should_deny_access
9 end
10 end
11
12 logged_in_as :active_user, &deny_access_tests
13 not_logged_in, &deny_access_tests
14 end

This is valid for any block of code that you want to test again and again:

test/functional/admin/orders_controller_test.rb

1 class OrdersControllerTest < ActionController::TestCase
2 successful_index_render = lambda do
3 should_respond_with :success
4 should_render_template "new"
5 should_assign_to :orders
6 end
7
8 logged_in_as :admin do
9 context "", &successful_index_render
10 end
11
12 logged_in_as :sub_admin do
13 context "", &successful_index_render
14 end
15 end

Alternatively, and it might be easier in the end, you could use methods:

test/functional/admin/orders_controller_test.rb

1 class OrdersControllerTest < ActionController::TestCase
2 def self.should_render_successful_index_response
3 should_respond_with :success
4 should_render_template "new"
5 should_assign_to :orders
6 end
7
8 logged_in_as :admin do
9 should_render_successful_index_response
10 end
11
12 logged_in_as :sub_admin do
13 should_render_successful_index_response
14 end
15 end

Note thought that you must define your methods at the top of your test case. Remember that Ruby executes a class definition, so when you suddenly call should_render_successful_index_response, the method definition has to be available, or else Ruby will complain with a NoMethodError.

Ain’t Ruby sweet?

Yesterday, I read Brian Cardarella‘s post entitled Brian Cardarella’s post entitled A case against Mocking and Stubbing. In the article, Brian says:

SQLite3 can be an in-memory database. Problem solved, right? Not quite. SQLite3 is pretty limited. Most people are probably using MySQL and rely upon many of the SQL functions that are included.

What would be nice (and well beyond my ability) is to have a Gem that simulated the database you use, only it is in-memory. Optimized for small data sets. No need to go through a heavy hashing algorithm. Keep it light. Keep it fast.

Brian Cardarella in A case against Mocking and Stubbing

MySQL already has the MEMORY storage engine, and I wanted to see if that would help for testing purposes. Since we’re staying in MySQL-land, this should have been a simple matter.

First, the good news. I had to change only a couple of lines:


1 diff -git a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/vendor/rails/activerecor
2 index 1e452ae..c207080 100644
3 -
- a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
4 + b/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
5 @ -442,7 +442,13 @ module ActiveRecord
6 end
7
8 def create_table(table_name, options = {}) #:nodoc:
9 – super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
10 + engine = case Rails.env
11 + when "test"
12 + "MEMORY"
13 + else
14 + "InnoDB"
15 + end
16 + super(table_name, options.reverse_merge(:options => "ENGINE=#{engine}"))
17 end
18
19 def rename_table(table_name, new_name)
20 diff -git a/vendor/rails/railties/lib/tasks/databases.rake b/vendor/rails/railties/lib/tasks/databases.rake
21 index 5cb27f1..c520d4a 100644
22 -
- a/vendor/rails/railties/lib/tasks/databases.rake
23 + b/vendor/rails/railties/lib/tasks/databases.rake
24 @ -368,9 +368,9 @ namespace :db do
25
26 desc ‘Check for pending migrations and load the test schema’
27 task :prepare => ‘db:abort_if_pending_migrations’ do
28 – if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
29 – Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].i
30 – end
31 + # if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
32 + # Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]]
33 + # end
34 end
35 end
36

Great, but there’s little benefit. First, a regular run (InnoDB):


1 $ time rake
2 (in /Users/francois/Documents/work/fasttest)
3 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/password_reset_mailer_test.rb" "test/unit/person_test.rb"
4 Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
5 Started
6 ……………………….
7 Finished in 0.520087 seconds.
8
9 28 tests, 33 assertions, 0 failures, 0 errors
10 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/functional/accounts_controller_test.rb" "test/functional/password_resets_controller_test.rb" "test/functional/people_controller_test.rb" "test/functional/sessions_controller_test.rb"
11 [DEPRECATION] should_be_restful is deprecated. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
12 [DEPRECATION] should_be_restful is deprecated. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
13 Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
14 Started
15 ……………………………………………..
16 Finished in 1.828175 seconds.
17
18 53 tests, 72 assertions, 0 failures, 0 errors
19 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb"
20
21 real 0m10.365s
22 user 0m6.582s
23 sys 0m1.925s

Next, a run with the MEMORY engine:


1 $ time rake
2 (in /Users/francois/Documents/work/fasttest)
3 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/password_reset_mailer_test.rb" "test/unit/person_test.rb"
4 Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
5 Started
6 ……………………….
7 Finished in 0.602607 seconds.
8
9 28 tests, 33 assertions, 0 failures, 0 errors
10 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/functional/accounts_controller_test.rb" "test/functional/password_resets_controller_test.rb" "test/functional/people_controller_test.rb" "test/functional/sessions_controller_test.rb"
11 [DEPRECATION] should_be_restful is deprecated. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
12 [DEPRECATION] should_be_restful is deprecated. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
13 Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
14 Started
15 ……………………………………………..
16 Finished in 1.132142 seconds.
17
18 53 tests, 72 assertions, 0 failures, 0 errors
19 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb"
20
21 real 0m9.111s
22 user 0m6.862s
23 sys 0m1.870s

Note that in test/test_helper.rb, I had to disable transactional fixtures. This would account for a lot the lost time difference.

If you want to play with this further, the sample application’s code is available at http://github.com/francois/fasttest

I would be interested in seeing other people’s runs, to know if it’s just my machine that runs at essentially the same speed.

I was working on my family budget application and wanted to validate existing fixtures. I wrote the following:

test/shoulda_macros/validation_macros.rb

1 module ValidationMacros
2 def self.included(base)
3 base.send :extend, ClassMethods
4 end
5
6 module ClassMethods
7 def should_have_valid_fixtures(klass=self)
8 should "have all valid fixtures" do
9 klass.name.sub("Test", "").constantize.all.each do |object|
10 assert object.valid?, "Fixture #{object.inspect} is invalid"
11 end
12 end
13 end
14 end
15 end
16
17 Test::Unit::TestCase.send :include, ValidationMacros
18
19 # Usage Example
20 class AccountTest < Test::Unit::TestCase
21 should_have_valid_fixtures
22 end
23
24 class StrangeNameTest < Test::Unit::TestCase
25 # Pass the ActiveRecord (or anything that respond_to?(:valid?)
26 # and respond_to?(:all)) class to validate against.
27 should_have_valid_fixtures Account
28 end

This might be interesting if you use fixtures.

If you are exploring Erlang and want an xUnit implementation, look no further than eunit.

UPDATE 2007-04-16: I did not build eunit. Richard Carlsson (the current maintainer) pointed me to it on the erlang-questions mailing list.

I started writing my own, and I will document that in the coming days as it was a very good learning experience for me.

Erlang ? Yes, I like it. A lot. I bought Programming Erlang as a beta book from the Pragmatic Programmers to help me get started faster.

My plan is to build Battleship in Erlang. That should prove very interesting.

I just hit a case which I think will interest the community at large.

Did you know it is possible to conditionnaly define methods ? Something like this is quite possible:

app/models/player.rb

1 class Player < User
2 if test == RAILS_ENV then
3 # Conditional method definition to ease testing
4 def play_in(tournament, final_score)
5
6 end
7 end
8 end

Is that a code smell, or is it a really useful feature ? I can’t decide…

The method defined above removes three lines of code from each of the tests where my players play in a tournament. And since this is what the site is about, I think I just slashed a few hundred lines of code from my functional and integration tests. That is not trivial.

These days, I use Thoughtbot’s Shoulda to do the same thing. The nice thing about that is that we still only have one test case per file, allowing autotest to do it’s automagical file resolution.

Yesterday, I was coding a new set of tests for a Picture class. I wanted to introduce an on-disk cache, when the picture is public.

I already had an existing test/unit/picture_test.rb, which I extended.

The tests looked like this:

test/unit/picture_test.rb

1 class PictureTest < Test::Unit::TestCase
2 def test_picture_exists_on_disk
3 pict = uploaded_file(large.jpg, image/jpeg, somepicture.jpg)
4 picture</span> = <span class="co">Picture</span>.build(pict) <span class="no"> <strong>5</strong></span> <span class="iv">picture.public = true
6 assert_model_saved picture</span> <span class="no"> 7</span> <span class="no"> 8</span> assert <span class="co">File</span>.exist?(<span class="iv">picture.cache_filename), file copied to disk
9 end
10
11 def test_thumbnail_exists_on_disk
12 pict = uploaded_file(large.jpg, image/jpeg, somepicture.jpg)
13 picture</span> = <span class="co">Picture</span>.build(pict) <span class="no">14</span> <span class="iv">picture.public = true
15 assert_model_saved picture</span> <span class="no">16</span> <span class="no">17</span> assert <span class="co">File</span>.exist?(<span class="iv">picture.cache_thumbnail), file copied to disk
18 end
19 end

I knew this was the wrong way to go – the fixture was repeated more than once. Then, I realized that what I wanted was a new TestCase. I had already reached for my mouse to hit New File when I had a flash: what prevents me from simply adding a new class to picture_test.rb ?

test/unit/picture_test.rb

1 class PictureTest < Test::Unit::TestCase
2 def test_read_from_stringio
3
4 end
5 end
6
7 class PublicPictureTest < Test::Unit::TestCase
8 def setup
9 pict = uploaded_file(large.jpg, image/jpeg, somepicture.jpg)
10 picture</span> = <span class="co">Picture</span>.build(pict) <span class="no">11</span> <span class="iv">picture.public = true
12 assert_model_saved picture</span> <span class="no">13</span> <span class="r">end</span> <span class="no">14</span> <span class="no"><strong>15</strong></span> <span class="r">def</span> <span class="fu">test_picture_exists_on_disk</span> <span class="no">16</span> assert <span class="co">File</span>.exist?(<span class="iv">picture.cache_filename), file copied to disk
17 end
18
19 def test_thumbnail_exists_on_disk
20 assert File.exist?(picture</span>.cache_thumbnail), <span class="s"><span class="dl">'</span><span class="k">thumbnail copied to disk</span><span class="dl">'</span></span> <span class="no">21</span> <span class="r">end</span> <span class="no">22</span> <span class="r">end</span> <span class="no">23</span> <span class="no">24</span> <span class="r">class</span> <span class="cl">PrivatePictureTest</span> &lt; <span class="co">Test</span>::<span class="co">Unit</span>::<span class="co">TestCase</span> <span class="no"><strong>25</strong></span> include <span class="co">PicturePathHelper</span> <span class="no">26</span> <span class="no">27</span> <span class="r">def</span> <span class="fu">setup</span> <span class="no">28</span> pict = uploaded_file(<span class="s"><span class="dl">'</span><span class="k">large.jpg</span><span class="dl">'</span></span>, <span class="s"><span class="dl">'</span><span class="k">image/jpeg</span><span class="dl">'</span></span>, <span class="s"><span class="dl">'</span><span class="k">somepicture.jpg</span><span class="dl">'</span></span>) <span class="no">29</span> <span class="iv">picture = Picture.build(pict)
30 picture</span>.public = <span class="pc">false</span> <span class="no">31</span> assert_model_saved <span class="iv">picture
32 end
33
34 def test_picture_not_copied_to_disk_when_private
35 assert !File.exist?(picture</span>.cache_filename), <span class="no">36</span> <span class="s"><span class="dl">'</span><span class="k">image should not be on disk when picture is private</span><span class="dl">'</span></span> <span class="no">37</span> <span class="r">end</span> <span class="no">38</span> <span class="no">39</span> <span class="r">def</span> <span class="fu">test_thumbnail_not_copied_to_disk_when_private</span> <span class="no"><strong>40</strong></span> assert !<span class="co">File</span>.exist?(<span class="iv">picture.cache_thumbnail),
41 thumbnail should not be on disk when picture is private
42 end
43 end

I also had another flash: is TestCase the right name ? Shouldn’t it be called TestFixture instead ? Because that is what instances of this class represent – a test fixture.

This is very much akin to Spec::Context, from RSpec.

And you see the tests ? They all respect the one assertion per test. I was quite happy with the results.

Now, I need to refactor those other instances in my test suite…

Trevor blogged Time Bomb Test in 2009, which made this article obsolete. Then, the day after, Jeremy McAnally wrote Deprecate, a gem to automate some versions of time bombing your tests.

Sometimes, I have an idea for a test, but I am testing something else and I don’t want to break my thought flow.

In those cases, I will write myself a time bomb:


1 def test_has_more_entries_after_update
2 flunk if Time.now > Time.local(2005, 12, 9, 10, 0, 0)
3 end

This test will pass, until Dec 9 2005, 10 AM. Nice way to not forget to add some tests.

In one of my projects, I wanted to prevent instances of particular classes to be deleted if they were in any way associated to another object. In database terms, I wanted an ON DELETE RESTRICT constraint.

Since I cannot rely on the database to enforce it for me (MySQL 4, MyISAM engine), I coded the following:

test/unit/city_test.rb

1 require File.dirname(FILE) + /../test_helper
2
3 class CityTest < Test::Unit::TestCase
4 fixtures :cities, :contacts
5
6 def setup
7 city</span> = <span class="co">City</span>.find(<span class="sy">:first</span>) <span class="no"> 8</span> <span class="r">end</span> <span class="no"> 9</span> <span class="no"><strong>10</strong></span> <span class="r">def</span> <span class="fu">test_prevent_destruction_if_associated_to_any_contact</span> <span class="no">11</span> <span class="iv">city.contacts << contacts(:jill)
12 city</span>.destroy <span class="no">13</span> assert_not_nil <span class="co">City</span>.find(<span class="iv">city.id), should not have been destroyed
14 assert_match /cannot destroy.*contacts/i, city</span>.errors.on_base, <span class="no"><strong>15</strong></span> <span class="s"><span class="dl">'</span><span class="k">reports error condition to user</span><span class="dl">'</span></span> <span class="no">16</span> <span class="r">end</span> <span class="no">17</span> <span class="no">18</span> <span class="r">def</span> <span class="fu">test_allow_destruction_if_not_associated_to_any_contact</span> <span class="no">19</span> <span class="iv">city.destroy
20 assert_raises ActiveRecord::RecordNotFound do
21 City.find(@city.id)
22 end
23 end
24 end

app/models/city.rb

1 class City < ActiveRecord::Base
2 has_and_belongs_to_many :contacts, :join_table => contacts_cities
3
4 def destroy
5 unless self.contacts.empty?
6 self.errors.add_to_base \
7 We cannot destroy this instance since one or more contacts refer to it)
8 return
9 end
10
11 super
12 end
13 end

I had to override destroy because in has_and_belongs_to_many relationships, Rails deletes join table records before deleting the record. This means that in before_destroy filters, self.contacts.empty? will always report true. Ticket #1183: dependents are destroyed before client before_destroy hooks are called is already opened on this issue.

UPDATE (2006-03-08) Fixed since 2006-02-13

This article and the plugin it describes are obsolete. There are replacements for neighter. The code is still available if you want to play around with it.

A new day, a new release !

What’s changed ?

  • A heinous bug was corrected that prevented flash.now from being correctly appended to.
  • #show_flash_messages was always returning the enclosing div. Now, it will only return something if the keys contain something.
  • Assertions for your unit tests. See below for details.
  • Renamed middle package from flash_plugin_helper to flash_helper_plugin. This should not have any impact on your code.

The interesting feature in this version are the new assertions. For example, the following code:


1 def test_destroy
2 post :destroy, :id => contacts(:bob).id
3
4 assert_match /bob.*destroyed successfully/i,
5 flash[:notice],
6 "User notified that Bob was deleted"
7 end

can be expressed like this instead:


1 def test_destroy
2 post :destroy, :id => contacts(:bob).id
3
4 assert_success_flash_contains \
5 /bob.*destroyed successfully/i,
6 User notified that Bob was deleted
7 end

The reverse is also true:


1 def test_pre_authorization_granted
2 get :index, {}, {:user_id => accounts(:bob).id}
3
4 assert_any_flash_does_not_contain \
5 /not authorized/i,
6 Authorization granted
7 end

There are eight assertion methods added to Test::Unit::TestCase:

  • #assert_any_flash_contains(string_or_regexp, msg=nil)
  • #assert_any_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_failure_flash_contains(string_or_regexp, msg=nil)
  • #assert_failure_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_success_flash_contains(string_or_regexp, msg=nil)
  • #assert_success_flash_does_not_contain(string_or_regexp, msg=nil)
  • #assert_message_flash_contains(string_or_regexp, msg=nil)
  • #assert_message_flash_does_not_contain(string_or_regexp, msg=nil)

The any variants of the assertions check all message keys: :notice, :message and :warning. All messages are concatenated, and the assertion is made against the resulting string.

In addition, four building block assertions are also added:

  • #assert_flash_contains(key, string_or_regexp, msg=nil)
  • #assert_flash_does_not_contain(key, string_or_regexp, msg=nil)
  • #assert_in_contents(string_or_regexp, contents, msg=nil)
  • #assert_not_in_contents(string_or_regexp, contents, msg=nil)

And last, but not least, the #flash_contents(key) method returns the flash’s contents, as a string, whether it was an Array or a String to begin with.

If anybody is using this in production, would you drop me an E-Mail ?

Enjoy !

Just got bitten by a bug. In a model, I was doing:


1 self.unit_price = self.product.current_price(Date.today) \
2 if self.unit_price.cents.blank?

After upgrading to Rails 0.14.3, I found some failures in my tests. About 30 minutes of sleuthing around, and I found my bug to be with the new object.blank? behavior.

Some console code to “prove” it:


1 $ ruby script\console
2 Loading development environment.
3 >> 0.blank?
4 => true
5 >> exit
6
7 $ svn up vendor\rails
8
9 U vendor\rails
10 Updated external to revision 2932.
11
12 $ ruby script\console
13 Loading development environment.
14 >> 0.blank?
15 => false

So, I rewrote my code to now do:


1 self.unit_price = self.product.current_price(Date.today) \
2 if 0 == self.unit_price.cents

Again, this shows how a good set of unit and functional tests can help prevent problems before they hit you.

This article is obsolete. It does not have a replacement.

After updating my externals to Rails 0.14.1, I ran into a few snags. First one is a difference in the handling of boolean attributes between both versions. I had the following assertion:


1 class EstimateTest < Test::Unit::TestCase
2 def test_converts_base_information_to_quote
3 # …
4 assert_equal e.pst_active, q.pst_active, copied pst active
5 end
6 end

q is an instance of Quote, and e is an instance of Estimate. In both cases, pst_active determines if the provincial tax is active. It is a boolean attribute, but I had forgotten to use the attr_name? form. In 0.13.1, this worked, but with 0.14.1, it failed with the following:


1 1) Failure:
2 test_converts_base_information_to_quote(EstimateTest) [./test/unit/estimate_test.rb:493]:
3 copied pst active.
4 <1> expected but was
5 <true>.

When I switched both calls to pst_active to the boolean form, the assertion passed again.

Next, I had 44 failures in my functionnal tests:


1 44) Error:
2 test_destroyed_pictures_dont_appear_in_the_slideshow(WelcomeControllerTest):
3 TypeError: cannot convert Symbol into String
4 D:/rails-app/config/../lib/productize.rb:38:in `+’
5 D:/rails-app/config/../lib/productize.rb:38:in `full_template_path’

This application makes use of the Rails Product Generator. Notice the bug is in lib/productize.rb, and not my code per se. Line 38 of lib/productize.rb looks like this:


1 site_specific_path = File.join(SITE_ROOT, app, views, template_path + . + extension)

I corrected the error by simply calling to_s on both template_path and extension.

After that, my tests ran perfectly ! Shows how important it is to have even a minimal test suite.

Named Test Fixtures

2005-10-18

In one of my projects, I have tons of units and functional tests. To speed things up, I set my configuration like this:

test/test_helper.rb

1 class Test::Unit::TestCase
2 self.use_transactional_fixtures = true
3 self.use_instantiated_fixtures = :no_instances
4 end

In my tests, instead of accessing @bob, I now have to do:

test/unit/party_test.rb

1 def test_party_is_destoryed
2 party = Party.find(@parties[bob][id])
3 party.destroy
4 assert party.destroyed?, "Party wasn’t marked destroyed"
5 end

Works, but isn’t this very verbose ? So, I created myself a little helper::

test/test_helper.rb

1 def party(fixture_name)
2 Party.find(@parties[fixture_name.to_s][id])
3 end

All great and dandy. I wanted to share with the community, so I started digging into fixtures.rb to implement it. Much to my surprise, this already exists:

vendor/rails/activerecord/lib/active_record/fixtures.rb

1 class Test::Unit::TestCase #:nodoc:
2 def self.fixtures(*table_names)
3 table_names = table_names.flatten
4 self.fixture_table_names |= table_names
5 require_fixture_classes(table_names)
6 setup_fixture_accessors(table_names)
7 end
8 end

So, I guess I’m going to send a documentation patch instead of code !

UPDATE 1 (Oct 18, 2005): Well, would you look at that, this is all documented in the Class: Fixtures (See the last paragraph under Using Fixtures)

UPDATE 2 (Oct 25, 2005): Rails 0.14.1 is pre-configured like that. In fact, Mike Clark wrote Faster Testing with Rails 1.0 to explain the new behavior.

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on