The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Rails Fixtures from Database Contents

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Britt

Posts: 1319
Nickname: jamesbritt
Registered: Apr, 2003

James Britt is a principal in 30 Second Rule, and runs ruby-doc.org and rubyxml.com
Rails Fixtures from Database Contents Posted: Sep 20, 2005 10:57 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by James Britt.
Original Post: Rails Fixtures from Database Contents
Feed Title: James Britt: Ruby Development
Feed URL: http://feeds.feedburner.com/JamesBritt-Home
Feed Description: James Britt: Playing with better toys
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by James Britt
Latest Posts From James Britt: Ruby Development

Advertisement

If you use the generator scripts that are a part of Rails, you get assorted niceties, such as stub files for unit tests, and the ability to load fixture files to drive tests with predefined test data.

These fixture files use YAML to define tables, and look something like this:

user_2:

account_id: 1 id: 1 user_type: "0" first_name: James access: 0 password: d2048f35eatmeb6970717a6ef753aa209 login: james last_name: Britt email_address: foo.bar@gmail.com

The file will typically have several of these entries; it's a hash that maps a field name to a value, for each table row.

There should be a fixture file for each model you plan to test (i.e., all of them, right?)

Fixtures are also handy for pre-populating a test or development database so you can examine or work with well-defined conditions. See this code snippet for a way to do this.

Quite handy.

I've found, though, that as I'm working through some development task, using the application and adding/altering data, I often reach a point where I want to be able to continue later on working with that same data set. Perhaps I'm thinking through how the application should behave under certain conditions.

It's too tedious to re-enter data by hand, so I looked for an automatic process.

One option would be to use some database tool to execute a SQL dump, and then use that tool again to reload the data later.

Or write a Rake task to do this for me.

But I like the YAML fixture format better than raw SQL calls for two reasons. First, the YAML is easier to read and modify than the SQL. It is quite simple to see what data goes where, and I can munge things up to tweak conditions. Second, the fixture files can (theoretically) be loaded into any database without concern for quirks between different databases, or different versions of the same database. (I've had problems with databases refusing to import a file created by its own export tool.)

I poked around, and asked on the Rails dev list, but there was, it seems, no existing tool for this. So I rolled my own.

First, I swiped some code from Nicholas Seckar, who responded to my rails-dev query with an example of turning a record set into a YAML doc. It was cleaner than my first attempt at this, so I replaced my code and made a few changes to create a class method suitable for each model:

 

def to_fixture records = self.find(:all) underscored = self.name.underscore records.inject({}) do |hash, record| key = "#{underscored}_#{hash.length + 1}" hash = record.attributes hash end.to_yaml end

 

This is part of a base-model library, in a file cleverly named base-model.rb. It gets included in all models for my application, which lets me add a set of handy class methods to every model.

How a method defined in a moduke becomes a class method, rather than an instance method, by virtue of module inclusion, may be less than obvious. (It certainly was to me until some searching brought me to a helpful post by John Wilger.

The magic happens here:

 

module BaseModel

# Methods defined within this module will be added as class methods to any # class that includes BaseModel. module BaseModelClassMethods # ... many method definitions, much like to_fixture end

 

def self.included(mod) class <

 

end

My models require the file base-model.rb, and call include BaseModel.

This triggers the call to self.included, which handles adding all the module methods as class methods.

So now each model class knows how to emit itself as a fixture document. The next part is creating a method to loop over each model. I took the stupid-but-fast hack of simply iterating over a hard-coded list. I suppose the "right" way might be to ask the database to loop over its tables; it wasn't instantly obvious to me how to do that, but it was instantly obvious how I could hand-write a list. So there you go.

My first pass embedded the call in a template file; I could then create a Web page with my fixture docs by fetchng a URL. I liked this because it was easy to play around and try stuff out. Even better, it would pull the data from whatever was the current database in use.

Of course, while it gave me some immediate gratification, it also meant repeated cut-n-paste or other manual twiddling.

The next version was a rake task.

(The usual caveat: All code is primarily judged by "Does it work, and can I now go back to my real tasks?")

 

desc "Create fixture data from a database"

task :create_fixtures => [ :environment, :zip_fixtures] do

if defined? RAILS_ROOT fixtures_dir = "#{RAILS_ROOT}/test/fixtures/" else fixtures_dir = File.dirname(__FILE__) + 'test/fixtures/' end ActiveRecord::Base.establish_connection( :development ) text = '' [ Message, Account, User, Address, Client, ].each do |klass| x = eval( "#{klass}.to_fixture " ) File.open( "#{fixtures_dir}/#{klass.to_s.plural.downcase}.yml", "wb"){ |f| f.puts x } text << "#{x}\n" end puts text

end

 

Two notes: I have a platform/machine-dependent task for zipping up the existing fixture files so that I don't lose anything, Just In Case. I also needed to follow YARNC (yet another Rails naming convention) for the fixture files, meaning I had to pluralize the class names. There may be some built-in Rails method for this, but it was simpler for me to just whip up an addition to the String class to do the work.

 

And that's it.

 

Oh, wait. One more thing. In writing this up, I thought a bit more about the hard-coding of the database used to create the fixtures. Surely, I thought, there is a way to define this at the command line when calling the task. Indeed there is. (And note, too, that you could also simply pull the database configuration from either an environment variable or your Rails config.)

So, at the start of the task, I've added:

  db = ENV[ 'db' ] || :test

 

Then I changed the database connection call to

  ActiveRecord::Base.establish_connection( db )

 

Now I can call the task like this:

  rake create_fixtures db=jgb_dev

 

Read: Rails Fixtures from Database Contents

Topic: RubyConf '05 turns exclusive at 195 Previous Topic   Next Topic Topic: The svn+rbot plugin, so far

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use