読者です 読者をやめる 読者になる 読者になる

本当は怖い情報科学

情報系大学院生の趣味&実益ブログ。

id:elm200さんの「データベースからテストフィクスチャを抽出する(to_yaml 不使用)」をさらに改良した

http://d.hatena.ne.jp/elm200/20070928/1190947947

生データからfixtureを作りたいけど、to_yamlとか使うと変なのが出ていやだ、という話。
ありがたく使わせていただいたんだけど、2つほど気になったことがあるので、修正してトラバを送らせていただく。

修正したのは次の2点。

  • データ内に改行が入る場合、エラーになってしまう。
  • idカラムを含まないテーブルの場合、エラーになってしまう。(多対多テーブルで :id => false にしているテーブルがいくつかあるんだ)

というわけで、次のように修正:

  • 改行が入る場合は、YAMLの複数行リテラルの記法を用いるようにした
  • idは並び替えに使っているだけなので、無ければ順番については妥協する

出力はこんな感じになる。

entry1:
  id: 1
  title: |
    これは改行を
    含むデータだよ
  body: MyText1

entry2:
  id: 2
  title: MyString2
  body: MyText2

コード。

# lib/tasks/fixture_dump.rake
def fixture_entry(table_name, obj)
  res = []
  klass = table_name.singularize.camelize.constantize
  res << "#{ table_name.singularize}#{obj['id']}:"
  klass.columns.each do |column|
    name = column.name
    value = obj[column.name]

    if value.is_a? String and value =~ /\n/
      res << "  #{name}: |\n    " + value.split("\n").join("\n    ")
    else
      res << "  #{name}: #{value}"
    end
  end
  res.join("\n")
end

namespace :db do
  fixtures_dir = "#{RAILS_ROOT}/tmp/fixtures/"
  namespace :fixtures do
    desc "Extract database data to the tmp/fixtures/ directory. Use FIXTURES=table_name[,table_name...] to specify table names to extract. "
    task :dump => :environment do
      sql = "SELECT * FROM %s ORDER BY id"
      sql_no_id = "SELECT * FROM %s"
      skip_tables = ["schema_info"]
      ActiveRecord::Base.establish_connection
      FileUtils.mkdir_p(fixtures_dir)

      if ENV['FIXTURES']
        table_names = ENV['FIXTURES'].split(/,/)
      else
        table_names = (ActiveRecord::Base.connection.tables - skip_tables)
      end

      table_names.each do |table_name|
        File.open("#{fixtures_dir}#{table_name}.yml", "w") do |file|
          begin
            objects  = ActiveRecord::Base.connection.select_all(sql % table_name)
          rescue
            objects  = ActiveRecord::Base.connection.select_all(sql_no_id % table_name)
          end
          objects.each do |obj|
            file.write  fixture_entry(table_name, obj) + "\n\n"
          end
        end
      end
    end
  end
end

使い方は、データが保存されている状況で、

$ rake db:fixtuers:dump

これによって、tmp/fixturesにデータが保存される。これらをspec/fixtuers(もちろんRSpec on Railsは使ってるよね:-)に移動して、

$ rake spec:db:fixtures:load

で何事もなく終了すればOK.

Enjoy Testing!

【広告】