Sun, 19 Apr 09

Rails 2.3 and the ability to update created_at, created_on, updated_at and updated_on timestamps

The timing of the automatically generated timestamps and attribute assignment in rails 2.3 security note 1 was interesting to me because I’d just spent a while really trying to get my head around attr_accessible and attr_protected and the best way to test their effects in an app I was working on. Although I intend to write about the testing separately, the conclusion I came to was that I wanted to test the behaviour (more specifically, I was interested in what I could/couldn’t assign) of my objects and not that I was specifically using attr_accessible or attr_protected.

Reading Alex’s post made me wonder whether the rails API ever guaranteed that the timestamps would be readonly: If that was the case then I can see arguments for not wanting to test the behaviour of your objects (because you’re essentially testing the framework itself). I couldn’t find any mention of whether they’d specifically be readonly so chose to do a bit of digging. I created a script that allowed me to, with relative ease, run tests against rails apps created with different versions of rails. Using this I was able to test the mass assignment of timestamps (created_at, created_on, updated_at and updated_on) against multiple versions of rails. The conclusion I came to (and I’d love for other people to replicate my experiment and prove/disprove my results) was that the created_at and created_on pair of attributes have always (at least as far back as rails 1.0.0) been assignable, while the updated_at and updated_on attributes have only recently become assignable. While digging I managed to find the lighthouse ticket (#1612) that requested the ability to set the updated_* timestamps and the rails commit that closed that ticket.

Replicating my experiment

  $ cd /path/to/code
  $ git clone git://github.com/rails/rails.git
  mysql> CREATE DATABASE rails_timestamps_test;
  mysql> USE rails_timestamps_test;
  mysql> CREATE TABLE people (id INTEGER AUTO_INCREMENT, created_on DATE, created_at DATETIME, updated_on DATE, updated_at DATETIME, PRIMARY KEY (id));
  $ cd /path/to/code
  $ svn co http://chrisroos.googlecode.com/svn/trunk/scratch/rails_timestamps_test
  $ cd /path/to/code/rails_timestamps_test
  $ ruby setup_rails_project.rb 2.3.0 /path/to/code/rails
  
  *** Removing the rails app at /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0
  *** Checking out the rails version tagged v2.3.0
  HEAD is now at beca1f2... Template#mime_type should not use Mime::Type when Action Controller is not included
  *** Creating the rails app at /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0
  *** Vendorising rails from /path/to/code/rails to /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0
  *** Generating rails_version_test.rb in /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0/test/unit to ensure we are testing against the correct version of rails
  *** Copying assets to the new rails app
  ****** Linking /path/to/code/rails_timestamps_test/assets/person_test.rb to /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0/test/unit/person_test.rb
  ****** Linking /path/to/code/rails_timestamps_test/assets/person.rb to /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0/app/models/person.rb
  ****** Linking /path/to/code/rails_timestamps_test/assets/database.yml to /path/to/code/rails_timestamps_test/projects/rails-app-2-3-0/config/database.yml
  *** Running the rails version test to ensure that we're testing against the correct version of rails
  Loaded suite test/unit/rails_version_test
  Started
  .
  Finished in 0.000327 seconds.

  1 tests, 1 assertions, 0 failures, 0 errors
  *** Running the timestamps test
  Loaded suite test/unit/person_test
  Started
  ....
  Finished in 0.078819 seconds.

  4 tests, 8 assertions, 0 failures, 0 errors
  $ cd /path/to/code/rails_timestamps_test
  $ ruby setup_rails_project.rb 2.2.2 /path/to/code/rails
  
  ... some lines snipped ...
  *** Running the timestamps test
  Loaded suite test/unit/person_test
  Started
  ..FF
  Finished in 0.069429 seconds.

    1) Failure:
  test_should_be_able_to_set_updated_at(PersonTest)
      [test/unit/person_test.rb:35:in `test_should_be_able_to_set_updated_at'
       /path/to/code/rails_timestamps_test/projects/rails-app-2-2-2/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `__send__'
       /path/to/code/rails_timestamps_test/projects/rails-app-2-2-2/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `run']:
  FAIL: Couldn't persist the mass assigned updated_at attribute.
  <Thu Jan 01 00:00:00 +0000 2009> expected but was
  <Sun, 19 Apr 2009 09:23:46 UTC +00:00>.

    2) Failure:
  test_should_be_able_to_set_updated_on(PersonTest)
      [test/unit/person_test.rb:28:in `test_should_be_able_to_set_updated_on'
       /path/to/code/rails_timestamps_test/projects/rails-app-2-2-2/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `__send__'
       /path/to/code/rails_timestamps_test/projects/rails-app-2-2-2/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `run']:
  FAIL: Couldn't persist the mass assigned updated_on attribute.
  <Thu, 01 Jan 2009> expected but was
  <Sun Apr 19 09:23:46 UTC 2009>.

  4 tests, 8 assertions, 2 failures, 0 errors

A couple of notes about the script

  $ cd /path/to/code/rails_timestamps_test
  $ ruby setup_rails_project.rb made.up.tag /path/to/code/rails
  
  ... some lines snipped ...
  Loaded suite test/unit/rails_version_test
  Started
  F
  Finished in 0.045543 seconds.

    1) Failure:
  test_should_be_using_rails_made_up_tag(RailsVersionTest)
      [test/unit/rails_version_test.rb:6:in `test_should_be_using_rails_made_up_tag'
       /path/to/code/rails_timestamps_test/projects/rails-app-made-up-tag/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `__send__'
       /path/to/code/rails_timestamps_test/projects/rails-app-made-up-tag/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:94:in `run']:
  <"made.up.tag"> expected but was
  <"2.2.2">.

  1 tests, 1 assertions, 1 failures, 0 errors

1 Alex MacCaw raised the issue in reference to number eight in the list of 10 cools things in Rails 2.3.