LoginSignup
36
29

More than 5 years have passed since last update.

Ruby単体でActiveRecordを使うベストプラクティス

Last updated at Posted at 2016-09-22

を模索した結果落ち着いた構成をメモしておきます。

特に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

Gemfile
source 'https://rubygems.org'

gem 'activerecord'
gem 'rake'

config/database.yml

config/database.yml
development:
  adapter: mysql2
  encoding: utf8
  database: your_database
  pool: 5
  username: root
  host: localhost

..

一般的な構造にしました。以下のconfig/initializers/db.rbrakefile.rbではこの構造を前提としています。

config/initializers/db.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としています。起動時に読み込みます。
TimeWithZoneTime.zoneを参照して時刻表記を変えるので、JST表記になるよう、ここで設定しています。

これが読み込まれるより前に、あるいはこのファイル内でactive_support/core_ext/time/zonesのrequireが必要ですが、ActiveSupportはActiveRecordの依存関係にあるので、Bundler.requireで十分です。自分はapplication.rb内でBundler.requireとその他必要なライブラリ(yaml, erb等)のrequireを行っています。

Rakefile.rb

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式を展開するようにしたくらいです。

調べると、migraterollbackを自前で書く例も多く見つかるのですが、createschema:loadを実装している例は見つかりませんでした。そこで此方の全タスク持ってきちゃったRakefileに落ち着きました。

後はdb/migrateにmigrationファイルを置けばmigrateができます。また、migrateをする度にdb/schema.rbを生成してくれます。

使い方

あとはActiveRecord::Baseを継承したクラスを作るなりしていつも通り使うだけです。

36
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
29