require 'yaml' class FoxifyFixtures class << self def foxify # foxify both traditional and rspec fixtures folders = Dir.glob("#{RAILS_ROOT}/test") + Dir.glob("#{RAILS_ROOT}/spec") folders.each do |folder| id_to_name = map_id_to_fixture_name(folder) table_names = id_to_name.keys foreign_key_to_reflection = map_foreign_key_to_reflection(table_names) data = load_data(folder) # Replace any occurences of: # ': ' # --- with --- # : ' table_names.each do |table_name| data[table_name].each_key do |record_name| # get rid of pre-assigned id's, which appear to interfere with # foxy fixtures data[table_name][record_name].delete('id') data[table_name][record_name].each_key do |key| # look for foreign keys if( key =~ /\A(.*_id)\Z/ ) foreign_key = $1 reflection = foreign_key_to_reflection[table_name][foreign_key] # complain if an association seems to be missing if( reflection.nil? ) raise RuntimeError, "Foreign key '#{foreign_key}' for table " + "'#{table_name}' appears to be missing an association in the " + "model." else referenced_table = reflection.klass.table_name referenced_id = data[table_name][record_name][key] if( referenced_id != nil && table_names.include?(referenced_table) ) referenced_id = referenced_id.to_i # delete the existing id-based reference data[table_name][record_name].delete(key) # create the new name-based reference data[table_name][record_name][reflection.name.to_s] = id_to_name[referenced_table][referenced_id] end end end end end end # Move original fixtures to a backup folder FileUtils.mkdir_p("#{folder}/fixtures/backup") FileUtils.mv( Dir.glob("#{folder}/fixtures/*.yml"), "#{folder}/fixtures/backup") # Write out updated fixtures table_names.each do |table_name| File.open( "#{folder}/fixtures/#{table_name}.yml", 'w' ) do |out| #YAML.dump( data[table_name], out ) out << data[table_name].to_yaml(:SortKeys => true) end end end end private def map_id_to_fixture_name(folder) id_to_name = Hash.new # First gather all mappings between id's and fixture names Dir["#{folder}/fixtures/*.yml"].each do |file| table_name = File.basename(file, ".yml") if data = YAML::load_file(file) id_to_name[table_name] = Hash.new data.each do |record_name, record| if( record['id'] != nil ) id_to_name[table_name][ record['id'].to_i ] = record_name end end end end return id_to_name end def map_foreign_key_to_reflection(table_names) foreign_key_to_table = Hash.new table_names.each do |table_name| class_name = table_name.classify.constantize class_name.reflect_on_all_associations(:belongs_to).each do |reflection| originating_table = reflection.active_record.table_name if(foreign_key_to_table[originating_table].nil?) foreign_key_to_table[originating_table] = Hash.new end foreign_key = reflection.primary_key_name foreign_key_to_table[originating_table][foreign_key] = reflection end end return foreign_key_to_table end def load_data(folder) data = Hash.new # First gather all mappings between id's and fixture names Dir["#{folder}/fixtures/*.yml"].each do |file| table_name = File.basename(file, ".yml") data[table_name] = YAML::load_file(file) end return data end end end