Dumping selected attributes to YAML

Hi,

I am trying to dump an ActveRecord tree structure (created using acts_as_tree) with elements that also have associations.

I want to dump the YAML representation as text, edit it and then reload it. The current state of my project means that this is the most efficient way to get bulk changes working.

After spendign a day Googling I cannot see a way to dump a subset of the ActiveRecord attributes.

For example:

- !ruby/object:Expectation   attributes:     status: WRITTEN     name: tpe2     updated_at: 2008-03-19 14:34:28     evidence:     metric_table:     order_column_name:     goal_diff_filename:     metric_select_column_name:     goal_diff_options:     id: "2"     order_direction:     goal_numeric_comparison:     parent_section_id: "1"     goal_numeric_value:     owner: "222"     documentation:     reviewer:     created_at: 2008-03-19 14:34:28     implementor:   attributes_cache: {}

is what I get when I dump an instance to YAML.

But I don't want the *id, created_at and updated_at fields, since they're irrelevant to my needs and will change when I reload this data.

I can see how to_yaml_properties works for simple objects and indeed if I use that method to limit the instance variables I want to see then I get:

  def to_yaml_properties     props = self.attributes     props.delete "id"     props.delete "created_at"     props.delete "updated_at"     props.delete "parent_id"     props.delete "name"     props.keys.map{|prop| "@#{prop}"}   end

- !ruby/object:Expectation   status:   metric_select_action_math:   metric_select_action_index:   evidence:   metric_table:   order_column_name:   goal_diff_filename:   metric_select_column_name:   goal_diff_options:   testplan_section_id:   order_direction:   goal_numeric_comparison:   owner:   goal_numeric_value:   documentation:   reviewer:   implementor:   goal_regexp:

But now this cannot be reloaded as an Expectation object because these instance variables are now no longer in the attributes section as they were in the previous dump

Other than running a post-dump textual deletion of these entries I am completely stumped.

I note that the to_xml methods allow this kind of feature but to_yaml doesn't seem to offer anything like this.

Allan

Here is a bit of code I copied from techno-weenie and made some modifications to.

# http://rails.techno-weenie.net/tip/2005/12/23/make_fixtures ActiveRecord::Base.class_eval do   # Write a fixture file for testing   def self.to_test_fixture     self.write_fixture_file("test")   end

  # Write a fixture file for the CMS system   def self.to_cms_fixture     self.write_fixture_file("cms")   end

  private     def self.write_fixture_file(fixture_path)       raise "fixture_path cannot be blank!" if fixture_path.blank?

      # collect the data in an array to preserve ordering by id       data = find(:all, :order => :id).inject() do |array, record|         array << {record.id => record.attributes(:except => [:created_at, :updated_at])}       end

      # add !omap to the YAML header       string = YAML.dump data       string = string[0..3] + "!omap" + string[4..-1]

      File.open(File.expand_path("#{fixture_path}/fixtures/ #{table_name}.yml", RAILS_ROOT), 'w') do |out|         out.puts string       end       logger.info "Writing #{self.to_s.titleize} to #{fixture_path == "test" ? fixture_path.titleize : fixture_path.upcase} path"     end end

Combine those updates to ActiveRecord with this simple class and you can easily create a YAML based CMS for use from script/console.

require 'active_record/fixtures'

class FixtureManager   @@cms_classes = [AllTheModelsIWishToSave, InAnArray]

  def self.save_all(test = false)     RAILS_DEFAULT_LOGGER.info "Saving CMS data"

    @@cms_classes.each do |klass|       begin         test ? klass.to_test_fixture : klass.to_cms_fixture       rescue Exception => e         RAILS_DEFAULT_LOGGER.error "Error saving: #{klass}"         RAILS_DEFAULT_LOGGER.error e.message         RAILS_DEFAULT_LOGGER.error e.backtrace.join("\n\t")         return false       end     end

    RAILS_DEFAULT_LOGGER.info "All data saved"     return true   end

  def self.load_all(test = false)     RAILS_DEFAULT_LOGGER.info "Loading CMS data"     begin       tables = @@cms_classes.collect{|klass| klass.table_name}       Fixtures.create_fixtures(File.join(RAILS_ROOT, "#{test ? "test" : "cms"}", "fixtures"), tables)     rescue => e       RAILS_DEFAULT_LOGGER.error e.message       RAILS_DEFAULT_LOGGER.error e.backtrace.join("\n\t")       return false     end

    RAILS_DEFAULT_LOGGER.info "All data loaded"     return true   end end

I will take a thorough look at this and see where it gets me.

Thanks,

Allan