データ構造が確定したので、古いデータをデータベースに流し込みます。今回のシステムは閲覧専用なので、データの補充はこの作業のみとなります。
旧システムは key-value 形式で書かれたテキストを読み込み、更新された情報に関わるページを自動的に作成するようになっていました。この時、データブロックごとに最終更新日のデータを保持し、最後にページセットを構築した日付と比較する形で更新確認を行っていました。
今回、データを移行するにあたり、元のデータを大幅に作り直すのは面倒です。そこで、テーブルへの流し込みについては同様に処理を行いました。Rails では、初期データの流し込みについて、seed という仕組みがあるので、それを利用します。また、テーブルごとに処理をしたいので、db/seeds の下に各テーブルごとのスクリプトを記述して行きます。これができるのは、db/seeds.rb に以下のように書かれているからです。
Dir.glob(File.join(Rails.root, 'db', 'seeds', '*.rb')) do |file| load(file) end
アルバムを例にすると以下のようなスクリプト(db/seeds/albums.rb)が書いています。例のごとく適度にコメントを入れてみます。
# vim:set fileencoding=utf-8 filetype=ruby: # key-value テキストは文字列として str に入れておく # key: value の形式。データの区切りは空行 str = <<"EOF" keyword: aNewSeason.html type: Album j_title: NEW SEASON singer: iChisatoMoritaka.html number: 1st date: 1987/7/25 minutes: 42 seconds: 25 code: CD WPCL-502 second company: Warner Music Japan price: 2,400 code2: LP K-12533 first x company2: Warner-Pioneer price2: ???? code3: KT LKF-8176 first x company3: Warner-Pioneer price3: ???? code4: CD 32XL-216 first x company4: Warner-Pioneer price4: 3,200 3,008 jcomment: デビューアルバム ecomment: Debut Album songlist: COMMON MODIFY: 1995/12/13 中略 keyword: aTheSingles.html type: Album j_title: ザ・シングルス e_title: The Singles singer: iChisatoMoritaka.html number: 20th date: 2012/8/8 minutes: 195 seconds: 00 code: CD WPCL-11128/30 first company: WARNER MUSIC JAPAN price: 4,280 songlist: Disk1 Disk2 Disk3 MODIFY: 2012/8/8 - EOF include MiscMethod # 一まとまりの key value データを入れておく hash hash = {} sort_order = 0; # 改行で分割し、一行ずつ処理 str.split("\n").each do |line| # 空行だったら、一つ分のデータがすべて hash にまとまっているはず if line == "" キーワードを取得し、キーワードがある時だけ処理 if k = hash[:keyword] sort_order += 1 # 既存の keyword が k のアルバムを取得、なければ作成 album = Album.keyword_is(k).first || Album.new(keyword:k) # device_type はスペース区切りで複数選択可能。 album.device_type = 0 hash[:type].split(/ /).each do |k| album.device_type |= Album::AlbumStr2Num[k.intern] end # 各値はハッシュの対応する key のものを適切に設定 album.j_title = hash[:j_title] album.e_title = hash[:e_title] album.number = hash[:number] album.date = hash[:date] && Date.parse(hash[:date]) album.minutes = hash[:minutes] album.seconds = hash[:seconds] # すでに Individual テーブルに singer が登録されていれば関連を登録 album.singer = hash[:singer] && Individual.keyword_is(hash[:singer]).first # year のキーか、アルバムの日付から年の数値を取得 year_num = hash[:year] || (album.date && album.date.year) if year_num # 年の数値が取得できていれば、数値から年のオブジェクトを取得もしくは作成 year = Year.year_from_year_or_create(year_num) # アルバムに年の関連を設定 album.year = year end album.sort_order = sort_order # アルバムの media(わかりにくいけど medium の複数形)に取得もしくは作成したメディアを登録 album.media << Medium.medium_from_data(hash, :code, :company, :price, 0) if hash[:code] album.media << Medium.medium_from_data(hash, :code2, :company2, :price2, 1) if hash[:code2] album.media << Medium.medium_from_data(hash, :code3, :company3, :price3, 2) if hash[:code3] album.media << Medium.medium_from_data(hash, :code4, :company4, :price4, 3) if hash[:code4] album.media << Medium.medium_from_data(hash, :code5, :company5, :price5, 4) if hash[:code5] album.jcomment = hash[:jcomment] album.ecomment = hash[:ecomment] # アルバムを保存する if album.save # 保存に成功し、データが更新されていればメッセージを表示 # Rails では save されても、オブジェクトの内容が変わってなければ updated_at は更新されない print "update album: #{k}\n" if Time.now - album.updated_at < 10 else # データベースへの登録が失敗した時にはエラーメッセージを表示する p album print "save error!\n" end # アルバムに関連づけられた song_lists を取得し、keyword を key にした hash に登録 sl_hash = array_to_hash(album.song_lists) { |sl| [ sl.keyword, sl ] } if hash[:songlist] # songlist key にはスペース区切りで複数のキーワードが記述されている hash[:songlist].split(' ').each do |sl| # sl_hash に登録されていない場合には、アルバムに関連づけられた song_list を新規に作成する unless sl_hash[sl] song_list = album.song_lists.create(keyword:sl, sort_order:sl_hash.keys.count + 1) sl_hash[sl] = song_list print "create song_list: #{sl}\n" end end end hash = {} end else # 空行でない場合には、登録されたキーの値のみを hash に登録する k, v = line.split(": ") ks = k.intern case ks when :keyword, :type, :j_title, :e_title, :number, :date, :year, :singer, :minutes, :seconds, :code, :code2, :code3, :code4, :code5, :company, :company2, :company3, :company4, :company5, :price, :price2, :price3, :price4, :price5, :jcomment, :ecomment, :songlist hash[ks] = v end end end
基本的にはstr の中身を追記し、rake db:seed をすれば追加分だけがデータベースに流し込まれることになります。ただし、依存関係があるので数回行わなければならないことがあります(更新メッセージが表示されなくなるまで)。更新はそう頻繁ではないので許されるかと思っています。