Tuesday, July 22, 2008

RSpec & ActiveRecord: working with namespaces

Namespaces can be useful when you don't have the freedom to create as many databases for all of your environments and applications as you would like. Who wants to wait for all the migrations associated with a single database to execute every time tests are run?

There's a great thread out there that addressed this issue very well for me.

The two items towards the end address two different issues.

1. ActiveRecord and RSpec don't seem to work well together when it comes to namespaces; one solution is to use the :sql schema_format. As mentioned in the thread, instead of using schema.rb to recreate the test database, use the development database itself to clone the test database.

2. the second solution could be a real time saver, but it could come back to bite you. It simply omits rebuilding the test database. Therefore, you are responsible for its integrity when running 'rake spec'.

3. then there is the case where #1 doesn't seem to work and RSpec seems to still be prepending the prefix onto the table names multiple times. Someone has found a solution for that as well that deals in a small hack to fixtures.rb.

If you wish to use namespaces, update config/environment.rb to include (putting your own prefix in place)

config.active_record.table_name_prefix = "rcp_"

I believe (at least I remember reading) that you can also accomplish the same thing by adding configuration to the config/database.yml

development:
adapter: sqlite3
database: db/development.sqlite3
timeout: 5000
prefix: rcp_


Everything seems to work well until you run rake rspec . Either one of the above solutions should work for that. When running spec spec/controller/items_controller_spec.rb, you shouldn't run into the same problems that you get when using the rake option.


4:05 PM 7/23/2008

Ok; I have another environment that has Oracle 10.2.0.3.0 and activerecord-oracle-adapter-1.0.0 to provide the oracle_adapter.rb. The fixes I made in my first environment with Sqlite3 to get namespace problems resolved doesn't seem to work in my Oracle environment. Here's how I got things running with Oracle:

1. copy oracle_adapter.rb to ./lib/active_record/connection_adapters
1.1. edit file to include def select_rows...

def select_rows(sql, name=nil)
result = select(sql, name)
result.map{ |v| v.values}
end


It appears the adapter was completely missing a method required by the API. Go figure.

2. copy schema_statements.rb to ./lib/active_record/connection_adapters/abstract
2.1. remove the semicolon at the end of line 305; the Oracle driver doesn't like trailing semicolons when it is read in to clone the test database with the development database (this probably is only the case when schema_format == :sql; see config/environment.rb).

Line 305:
migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" }

For something interesting, try this out in script/console:

ActiveRecord::Base.connection.execute("INSERT INTO <your table> (<field>) VALUES(<value>);")
ActiveRecord::Base.connection.execute("INSERT INTO <your table> (<field>) VALUES(<value>)")


The first one fails and the second one passes; the only difference is that the second line doesn't have a trailing semicolon.

3. edit config/environment.rb
3.1. set config.active_record.table_name_prefix = <namespace> (e.g., "... = 'jrn_'") to create a namespace in the database for your Rails application
3.2. set config.active_record.schema_format = :sql, especially if you are going to use a namespace. Rake tends to trip up (i.e., rake spec) because of how schema.rb is read in multiple times (you end up with the prefix being applied multiple times -- e.g., 'jrn_jrn_items').

4. of course, make sure to update config/database.yml with the appropriate values

Make sure that your migrations are not in the development and test databases and then run RSpec

rake spec

No comments: