を模索した結果落ち着いた構成をメモしておきます。
特にTimeZone周りで苦労しました。
ファイル構成案
├── Gemfile
├── Gemfile.lock
├── Rakefile.rb
├── config
│ ├── application.rb
│ ├── database.yml
│ ├── database.yml.sample
│ └── initializers
│ └── db.rb
├── db
│ ├── migrate
│ │ └── 001_create_your_table.rb
│ └── schema.rb
└── app
├── ..
..
└── ..
主要なファイルの中身
Gemfile
source 'https://rubygems.org'
gem 'activerecord'
gem 'rake'
config/database.yml
development:
adapter: mysql2
encoding: utf8
database: your_database
pool: 5
username: root
host: localhost
..
一般的な構造にしました。以下のconfig/initializers/db.rb
やrakefile.rb
ではこの構造を前提としています。
config/initializers/db.rb
config = YAML::load(ERB.new(IO.read(File.expand_path('../../database.yml', __FILE__))).result)
env = ENV.fetch('APP_ENV', 'development')
ActiveRecord::Base.establish_connection(config[env])
ActiveRecord::Base.time_zone_aware_attributes = true
一度読み込めばデータベースと接続されるようにしました。このファイルは起動時に読み込むようにしています。
個人的に重要かつなかなか辿り着けなかったのは
ActiveRecord::Base.time_zone_aware_attributes = true
で、これによりActiveRecordで取得されるdatetime型のカラムのクラスがTimeWithZone
になり、Time.zone
で指定されているタイムゾーンに合わせて表記を調整してくれます。これを設定しないとTime
になり、そのままstrftime等で扱うとレコード上の時刻が表示されてしまいます。
これはレコード上の時刻が用いたいタイムゾーンとズレているために起こる問題を解決しているということですが、レコードに記録される時刻を用いたい時刻(e.g. JST)にすれば良いという見方もあります。その場合、以下を追加すれば良いみたいです。
ActiveRecord::Base.default_time_zone = :local
こうすることで、レコード保存時の時刻を、systemのタイムゾーンとしてくれます。
しかしこれはsystemのタイムゾーンが用いたいタイムゾーンであることを前提としていて嫌だなと思い、自分はtime_zone_aware_attributes = true
として、DB上の時刻はデフォルトのutcにするようにしました。
config/application.rb
..
Time.zone = 'Tokyo'
アプリケーションの設定全般的な意味合いでapplication.rb
としています。起動時に読み込みます。
TimeWithZone
はTime.zone
を参照して時刻表記を変えるので、JST表記になるよう、ここで設定しています。
これが読み込まれるより前に、あるいはこのファイル内でactive_support/core_ext/time/zones
のrequireが必要ですが、ActiveSupportはActiveRecordの依存関係にあるので、Bundler.require
で十分です。自分はapplication.rb内でBundler.require
とその他必要なライブラリ(yaml
, erb
等)のrequireを行っています。
Rakefile.rb
require 'bundler/setup'
require 'active_record'
include ActiveRecord::Tasks
db_dir = File.expand_path('../db', __FILE__)
config_dir = File.expand_path('../config', __FILE__)
DatabaseTasks.env = ENV['APP_ENV'] || 'development'
DatabaseTasks.db_dir = db_dir
DatabaseTasks.database_configuration = YAML::load(ERB.new(IO.read(File.join(config_dir, 'database.yml'))).result)
DatabaseTasks.migrations_paths = File.join(db_dir, 'migrate')
task :environment do
ActiveRecord::Base.configurations = DatabaseTasks.database_configuration
ActiveRecord::Base.establish_connection DatabaseTasks.env.to_sym
end
load 'active_record/railties/databases.rake'
ほぼ此方そのまま。変更点はyamlロード時にRuby式を展開するようにしたくらいです。
調べると、migrate
やrollback
を自前で書く例も多く見つかるのですが、create
やschema:load
を実装している例は見つかりませんでした。そこで此方の全タスク持ってきちゃったRakefileに落ち着きました。
後はdb/migrate
にmigrationファイルを置けばmigrateができます。また、migrateをする度にdb/schema.rb
を生成してくれます。
使い方
あとはActiveRecord::Base
を継承したクラスを作るなりしていつも通り使うだけです。