UPDATE Now packaged as a plugin! Makes fixtures also. And, is MySQL friendly! UPDATE
Sometimes you need to save data and use it again when you deploy a server or continue a project on a different machine.
I wrote a plugin for ActiveRecord that lets you do this easily.
It adds two class methods that let you dump_to_file and load_from_file. The contents are written to the ‘db’ directory with the table name as the filename. When you call Model.load_from_file, existing data in that table is deleted and reloaded, but primary key id’s are kept intact.
An additional Model.to_fixture method makes fixtures from existing data. It writes a file to ./test/fixtures/models.yml that can be loaded normally in your unit and functional tests.
Install with the new plugin script. (Included in Rails 1.0).
./script/plugin install http://topfunky.net/svn/plugins/ar_fixtures
Use it in code, or use the built-in ‘runner’ command.
# Dump user table to db/users.yml ./script/runner "User.dump_to_file" # Or ./script/runner "User.to_fixture"
# Read it back into the database ./script/runner "User.load_from_file"
If I say “I love you,” would that be weird?
This exemplifies the phrase “any good idea seems obvious.” I’m going to integrate it into a few projects in the morning.
This comes at an interesting time where the developer of Railfrog were talking about the need for this. I was just about to write something like this myself, but as a daily visitor to your blog, didn’t fail to miss this!
Thank you for saving my time! :D
now this just needs to put in a plugin with a rake task
Congrats: You have the highest tipped entry on Rails Weenie.
This code rules.
I found the column order in the generated fixture disturbing, so I rewrote the yml generation as follows:
self.find(:all).inject(“
-\n”) { |s, record| self.columns.inject(s+”#{record.id}:\n”) { |s, c| s+” #{c.name.to_yaml[4..-1]}: #{record.attributes[c.name].to_yaml[4..-1]}\n” }})I have only checked it briefly, and there must be a better way to yaml escape values than .to_yaml[4..-1]. Perhaps someone can run with this.
Hmm.. link to code:
http://www.pylonhead.com/code/yaml.html
And also…
I had problems with the built-in ‘fixtures’ method loading my YAML fixtures that contained binary (Base64 encoded) data. It would get partway through, then print all the binary data to the screen.
A solution is to use MyModel.load_from_file in the setup() method of your tests. It’s slower, but at least it works.
Nicely done, easy install and works great!
Note: I’m having some problems with this code and the YAML libraries in Ruby 1.8.4.
I rolled back to 1.8.2 for now.
Looks like you have trackbacks disabled for the article… anyway, I tried the to_fixture with a HABTM and it failed. I’ve posted code on my blog with a work around, http://kekova.ca/articles/2006/01/11/habtm_to_fixture
I also had problems getting this to work, perhaps due to yaml changes in ruby 1.8.4. But the problem seemed to be in Rails.
Here’s the error I was getting:>> Person.load_from_file opening db/people.yml NoMethodError: You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occured while evaluating nil.include? from /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1402:in `respond_to?' from /usr/local/lib/ruby/1.8/yaml.rb:133:in `load' from ./lib/yaml.rb:13:in `load_from_file' from (irb):2I fixed it by modifying line 1402 of active_record/base.rb to beelsif @attributes && @attributes.include?(method_name = method.to_s)This was with activerecord-1.13.2.
Awesome, exactly what i needed.
ofcourse it would be nice to specify only one row, as well as automatically include the relationship models as fixtures.
Thanks!
Very useful! Thanks!
Sometimes, I’d like to be able to tell you how to generate the fixture name instead of using table+id. How about adding a method to allow overriding it for certain classes?
I could define in my own class as ”#{self.class}_#{uniquename}”...
Not all primary keys in Rails are numeric. This plugin makes that assumption for the sake of pretty formatting of the fixture name. Any chance you can check for the type of primary key before sprintf’ing it with %05i ? Or, just use the primary key (id) as-is?
-Chris
Reading doesn’t appear to work with ruby 1.8.4 and rails 1.2.2: /sw/lib/ruby/gems/1.8/gems/rails-1.2.2/lib/commands/runner.rb:47: undefined method `attributes’ for # (NoMethodError)
from ./script/../config/../vendor/plugins/ar_fixtures/lib/ar_fixtures.rb:27:in `load_from_file’
from (eval):1
from /sw/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `eval’
from /sw/lib/ruby/gems/1.8/gems/rails-1.2.2/lib/commands/runner.rb:47
from /sw/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’
from script/runner:3
awesome! Just what the doctor ordered. Works with Ruby 1.8.6, and Rails 1.2.3, on Mac OS X, if your data is simple strings and integers. don’t know about floats or base64 or other binary stuff, because I’m not dealing with that.
Thanks for this plugin, you rock!
Help! Great plugin by design, but I can’t get it to work. Installed on Ruby 1.8 Rails 1.2.3, rebooted server and get the following on ‘Method Undefined’ on load_to_file. Dump_to_file worked fine.
/usr/local/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/commands/runner.rb:47: undefined method `load_to_file’ for Region:Class (NoMethodError)
Carson: It’s not load_to_file, it’s load_from_file.
Hooah! Kudos for the to_fixture portion—it makes my job SO much easier. I love you, man!
I am new to the ruby on rails environment. I am installing this setup: Ruby-1.8.4,Rubygems-0.9.0,and rails-1.1.6 and every thing was moving along well until >> sudo gem install rails—include-dependencies /usr/local/lib/ruby/1.8/yaml/stream.rb:40: [BUG] terminated node (0×8113e40) ruby 1.8.4 (2005-12-24) [i386-solaris2.11]
Abort (core dumped)
—-—-—-—-any ideas-—-—-—-I wrote a simple patch to add an offset option to to_fixture etc (in addition to the limit option). Where can I send it?
My email address is at the bottom of this page. Send it there and I’ll include it in the plugin.
Using Rails preview release 2, was getting an error:
Apparently I fixed it by replacing line 29 of ar_fixtures.rb with
record_copy.id = record[“id”]
instead of
record_copy.id = record.id
Thanks for the bug report. I’ll look into this.
i notice that ar_fixtures defines to_skeleton to generate fixtures without any data. i needed just that to generate an initial set of fixtures (don’t have any test data in the database), so i extended the rake tasks to define db:fixtures:skel
after that:
now i’ve got a complete set of fixtures for my models. one drawback: the id field doesn’t get dumped to the fixture.
I am quite new user on rails, using Rails 1.2.3 and Gems 1.8 and when I run the “ruby script/runner “.dump_to_file” command from my application directory, it shows me this error. How can I solve that problem? Any suggestion will be appreciated.
c:/ruby/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/commands/runner.rb:47: undefined method `dump_to_file’ for TestConfigDatum:Class (NoMethodError) from (eval):1 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `eval’ from c:/ruby/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/commands/runner.rb:4 7 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require’ from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’ from script/runner:3
@Vab: Did you install the plugin? It looks like it can’t find the methods on your model.
And make sure that there is a test_config_data table to match your TestConfigDatum model.
http://nubyonrails.com/tools/pluralize/TestConfigDatum
Yes I did install the plugin by the command: “ruby script/plugin install http://topfunky.net/svn/plugins/ar_fixtures”
but when i run the “script/runner” , it shows this error and I don’t know why it cannot find this method. The plugin got installed at ”..\vendor\plugins\ar_fixtures”. Is this a right location where all the plugin files regarding ar_fixtures should go?
I have some legacy tables that do not use integers as primary keys. When I dump it fails on line 55 in #{‘%05i’ % record.id}
I rewrote the line with ‘rescue’ so that now it reads:
hsh.merge(”#{table_name.singularize}_#{‘%05i’ % record.id rescue record.id}” => record.attributes)
It now handles my legacy tables perfectly.
Thanks, Mike. It has been fixed.
Hi, we have problem with saving object that contains BigDecimal (it’s described on my blog http://blog.skrdla.net/2008/01/arfixture-bigdecimal-and-yaml-problem.html)
we got amount_03246: id: 3246 value: !ruby/object:BigDecimal {}
instead of any value, solution is to override BigDecimal.to_yaml as is shown on my blog
hi, any chance of updating this to generate rails 2.0 smarter fixtures? (eg no id’s, cross reference by name, etc)
That’s a great suggestion. I’ll see what I can do in that department.
I’m having a problem with HABTM as well – is there any way to dump that associated info?
What a fantastic plugin! I especially like how it can be run from the command line. Thanks!!
I needed this for a “better nested set” model and had to add one line of code to hook into the model instantiation loop so that I could move the child to the parent. Here’s the line of code and the surrounding lines:record_copy.save yield(record_copy, record) if block_given? endThis gave my block the object and the parent_id from the record. Thanks again for this code!Thank you…very useful.
Very useful – would be cool if it generated the YAML file in ID order. Rails 2.0 style migrations would be nice. Would be really nice if there was a good way to dump out join tables.
Just added a pastie, allowing you to load and dump all habtm tables for a model with one function call. The pastie explains it all. email me with any questions or bug fixes. Thanks!
http://pastie.caboo.se/189648
I’ve just tried to install the plugin using the command line you gave in the article, and… It doesn’t work. Can’t find the server. I’ve checked and there’s nothing there. Is the plugin still available?
Anyway to automate this for every table?
Thank you, your plugin saved me a lot of trouble.
Excelent. Works fine for me.
Hummm, something is wrong for me, using RoR 2.1.1
(NoMethodError) From vendor/plugins/ar_fixtures/lib/ar_fixtures.rb:27:in `each’
Maybe is the line in 27 records.each do |record| 28 record_copy = self.new(record.attributes)
Sorry, but i had to comments some lines and add some changes to line 27 and 28 Now i can import the data I created with to_fixture method
27 records = YAML::load( File.open( File.expand_path(path, RAILS_ROOT) ) ) 28 records.to_a.each do |record| 29 record_copy = self.new(record1) 30 #record_copy.object_id = record.id