Hatena::ブログ(Diary)

elm200 の日記 このページをアンテナに追加 RSSフィード Twitter

2007-09-28

[] データベースからテストフィクスチャを抽出する(to_yaml 不使用)


ar_fixtures の出力はなんだかダサくない?


データベースから yaml 形式のフィクスチャを抽出するプラグインとしては、ar_fixtures がある。さっきインストールして使ってみたけど、どうも yaml の出力が気に入らない。たとえば次のような感じになってしまう。

---
- !ruby/object:Entry
  attributes:
    title: MyString1
    body: MyText1
    id: "1"
- !ruby/object:Entry
  attributes:
    title: MyString2
    body: MyText2
    id: "2"

- !ruby/object:Entry ってなんだかよくわからないのがついてるし、カラムの表示順がランダムになってしまっていて、見づらい。次のような出力がほしいのだ。

entry1:
  id: 1
  title: MyString1
  body: MyText1

entry2:
  id: 2
  title: MyString2
  body: MyText2

あと、ar_fixtures は内部的に to_yaml というメソッドを使っていて、これが UTF-8 の文字列をうまく扱えない。そこで、

日本語をto_yamlするとエンコードされてしまう問題を安直な方法で解決する
to_yamlでUTF-8な日本語がbinaryになってしまう問題を回避するRailsプラグイン

みたいな hack が必要になってくる。

そんなわけで、Chad Flower「Rails レシピ」のレシピ41「生データからのテストフィクスチャの抽出」(p155) のソースコードをベースに次のような Rake タスクを作ってみた。to_yaml は使ってないので、UTF-8 文字列にまつわる頭痛とも無縁だ。興味があれば、使ってみてほしい。

使い方


下のコードを丸ごとコピーして、extract_fixtures.rake というファイルを作り、$RAILS_ROOT/lib/tasks の直下に置く。すると rake db:fixtures:extract という新しい Rake タスクが使えるようになる。

% rake db:fixtures:extract 
# => すべてテーブルの内容が tmp/fixtures/*.yml に抽出される
% rake db:fixtures:extract FIXTURES=entries,comments
# => テーブル entries, comments の内容のみが tmp/fixtures/*.yml に抽出される

という感じで使えるはずだ。

ソースコード


def fixture_entry(table_name, obj)
  res = []
  klass = table_name.singularize.camelize.constantize
  res << "#{table_name.singularize}#{obj['id']}:"
  klass.columns.each do |column|
    res << "  #{column.name}: #{obj[column.name]}"
  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. Otherwise, all the table data will be extracted."
    task :extract => :environment do
      sql = "SELECT * FROM %s ORDER BY id"
      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|
          objects  = ActiveRecord::Base.connection.select_all(sql % table_name)
          objects.each do |obj|
            file.write  fixture_entry(table_name, obj) + "\n\n"
          end
        end
      end
    end
  end
end

RommyRommy 2007/09/29 00:51 はじめまして、トラックバックありがとうございます。私が使っていたときはスマートな形式で生成されていたので変だなあと思って調べてみたら、rake db:data:dump MODEL=Entry で生成したときだけよくわからないのがついて出力されるようです。rake db:fixtures:dump MODEL=Entry とした場合はスマートな形式になりました。
ただスマートな形式といっても、レコードやカラムの並びが滅茶苦茶なので、elm200さんの作られたのを使った方が遥にメンテナンスしやすそうです。

elm200elm200 2007/09/29 08:02 なるほど rake db:fixtures:dump MODEL=Entry で出力がすこしましになるのですね。勉強になりました。ありがとうございます。

abikounsoabikounso 2008/10/15 18:27 はじめまして。
elm200さんのソースコードをカスタマイズしてpluginとして公開したいのですが、使わせていただいてもよろしいでしょうか?

elm200elm200 2008/10/15 22:49 abikounso さん、こんにちは。
どうぞどうぞ。お使いくださいませ。

abikounsoabikounso 2008/10/16 12:32 ありがとうございます。elm200さんのお言葉に甘えて、さっそくプラグインを公開させていただきました。
http://github.com/abikounso/ar_extractor/tree/master

YOHYOH 2008/12/08 17:22 はじめまして、YOHと言います。
ar_fixturesを使っていて、確かに「YAMLファイルの出力バラバラやん」と思っていました。
で、elm200さんのを使ってみたのですが、日付カラム(ORACLE)が「Fri Nov 21 11:00:40 +0900 2008」のような形で出力されてしまいます。

a2ikma2ikm 2009/10/07 11:40 初めまして。db:fixtures:extract利用させてもらってます。
カラムが文字列の場合には引用符でくくった方が安全なので、次のように修正して使ってます。
参考にしていただけたら幸いです。

6行目の

>|
res << " #{column.name}: #{obj[column.name]}"
|<



>|
x = obj[column.name]
if column.text? then x = "\"#{x}\"" end
res << " #{column.name}: #{x}"
|<

に置き換えています。

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

Connection: close