現在のシステムでも、開発環境のデータは運用環境のデータを流用している。これは、運用会社がサポートをするにあたって、ユーザと同じ環境にないと確認ができないためである。ただし、学内の機密データに関するものは、個人情報をマスキングしたり、データ自体を削除したりしている。今回もこのマスキングされたデータを、開発に使っていく予定である。
システムは PostgreSQL で作成してしまっているので、PostgreSQL 固有のテーブルダンプ・リストアコマンドである \copy コマンドを使っている。今回もテーブルごとに吐き出されたデータを db:seed で読み込むことにする。ただし、間違えて本番環境のデータを破壊しないように、テーブルに一つでもデータが入っている場合には読み込みをしない。
\copy で吐き出したダンプデータは dump に保持することとする。これはリポジトリに入れないので、.gitignore に無視するファイルとして設定する。同時にマスクしたデータをここに入れておく。
# dump files /dump
ダンプデータは psql 上の \copy コマンドで実行する。以下のようなスクリプトを bin/store_database.sh として用意する。テーブルのデータを全削除してから、\COPY コマンドで流し込みを行う。
#!/bin/sh HOST=$1 USER=$2 DB=$3 TABLE=$4 COLUMNS=$5 FNAME=$6 PASSWORD=$7 echo "### load ${TABLE}" psql -U $USER -h $HOST $DB << EOF DELETE from $TABLE; \COPY $TABLE ($COLUMNS, created_at, updated_at) FROM '$FNAME' EOF
rake タスクの db:seed からこのスクリプトを叩くようにする。db/seeds.rb を以下のように記述した。旧システムではデータベース名などは決め打ちしていたが、今回は、正しく DRY にするために、Rails.application.config.database_configuration からちゃんと取得している。db:seed ではデータがまったく登録されていない場合のみ、store_database.sh を呼ぶようにしている。\COPY コマンド終了後には、各テーブルの主キーの sequence をリセットしている。
require 'active_support' class CopyFromDump < ActiveRecord::Migration def self.store_database_from_dump(table, dir = "dump") storeClass = table.classify.constantize fname = File.join(dir, "#{table}.sql") env = Rails.env database_configuration = Rails.application.config.database_configuration[env] host = database_configuration["host"] username = database_configuration["username"] database = database_configuration["database"] password = database_configuration["password"] if storeClass.first == nil system("bin/store_database.sh #{host} #{username} #{database} #{table} \"#{storeClass::LoadKey}\" #{fname} #{password}") print "#{ActiveRecord::Base.connection.reset_pk_sequence!(table)}\n" end end def self.up Rails.application.config.CopyTables.each { |key| self.store_database_from_dump key } end end CopyFromDump.up
コピーすべきテーブル名は Rails.application.config.CopyTables から取得する。これは、config/application.rb で以下のように設定する。テーブルが増えれば %w の中身が増殖していく。
config.CopyTables = %w( years users )
\COPY コマンドの列名は各モデルクラスの LoadKey という変数から取得する。app/models/year.rb の該当部はこうなる。
class Year < ActiveRecord::Base LoadKey = 'id, year, default_year'
同様に、app/model/user.rb の該当部はこうなる。
class User < ActiveRecord::Base LoadKey = 'id, encrypted_password, password_salt, reset_password_token, remember_token, remember_created_at, sign_in_count, current_sign_in_at, last_sign_in_at, current_sign_in_ip, last_sign_in_ip, kyouin_id, gakusei_id, shokuin_id, name, email'
これだけの設定をすると、データベースに流し込みができるはずである。実行した結果がこちら。各テーブルごとに削除した数、コピーした数、次に設定される主キーが表示される。users の方で COPY の数値がかなり少ないのは削除されたデータも存在するからである。
$ bin/rake db:seed ### load years DELETE 0 COPY 17 18 ### load users DELETE 0 COPY 3737 4134
認証のテスト
まず、welcome#index の描画に認証が必要となるように設定してみる。app/controller/welcome_controller.rb に以下の行を追加する。
before_action :authenticate_user!
この状態で、ブラウザでアクセスするとこんな画面になる。
気になるのは以下の部分。
- 警告文が英語であること
- キーは name に変更したのに Email を入力するようになっていること
- 画面の左端にくっついていること
まず、最初の言語の部分を修正する。config/application.rb に i18n の default_locale を設定する部分があるので、そこを :ja に変更する。また、サブフォルダの locale も読み出すようにパスを修正する。
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] config.i18n.default_locale = :ja
locale ファイルは前日の設定で配置済であるため、警告メッセージが日本語に変わっていることを確認できる。メッセージの内容が若干システムと合わないので、locale ファイルを後で修正しておく。
また、GitHub から日本語辞書ファイルをダウンロードしておく(参考: Railsの多言語化対応 I18nのやり方を整理してみた!【国際化/英語化】)。
$ mkdir config/locales/{defaults,models,views} $ wget https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml -P config/locales/defaults
次に user モデルの locale ファイルを作成する。先ほど作った models フォルダの下にさらに users フォルダを作成し、そこに ja.yml ファイルを作成する。config/locales/models/users/ja.yml は以下のようになる。
ja: activerecord: attributes: user: name: "アカウント名" password: "パスワード" remember_me: "次回からパスワード入力を省く"
二番目以降を修正するために、カスタム view を作成する(前回ペンディングにした部分である)。今回は、users と admin の二つのモデルを作成する予定だが、ログイン画面は共通でよいので、scoped_view は true にはしない。
$ bin/rails g devise:views invoke Devise::Generators::SharedViewsGenerator create app/views/devise/shared create app/views/devise/shared/_links.html.erb invoke form_for create app/views/devise/confirmations create app/views/devise/confirmations/new.html.erb create app/views/devise/passwords create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions create app/views/devise/sessions/new.html.erb create app/views/devise/unlocks create app/views/devise/unlocks/new.html.erb invoke erb create app/views/devise/mailer create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erb
devise 1.2 以前と違って、haml ジェネレータは削除されてしまったので、erb を haml に変換する。
$ for file in app/views/devise/**/*.erb; do bundle exec html2haml -e $file ${file%erb}haml && rm $file; done /Users/hkob/rails/webcit3/vendor/bundle/ruby/2.1.0/gems/html2haml-1.0.1/lib/html2haml/html.rb:346: warning: wrong element type nil at 0 (expected array) /Users/hkob/rails/webcit3/vendor/bundle/ruby/2.1.0/gems/html2haml-1.0.1/lib/html2haml/html.rb:346: warning: ignoring wrong elements is deprecated, remove them explicitly /Users/hkob/rails/webcit3/vendor/bundle/ruby/2.1.0/gems/html2haml-1.0.1/lib/html2haml/html.rb:346: warning: this causes ArgumentError in the next release (以下、これの繰り返し)
今回必要なものはログイン処理のみなので、app/views/devise/sessions/new.html.haml を修正する。locale を設定したので、f.label などはシンボル名だけで自動的に日本語が表示される。
.container-fluid %h2 ログイン = form_for(resource, as:resource_name, url:session_path(resource_name), html:{class:'form-horizontal'}) do |f| %fieldset .form-group = f.label :name, class:'control-label' = f.text_field :name, autofocus: true, class:'form-control' .form-group = f.label :password, class:'control-label' = f.password_field :password, autocomplete: "off", class:'form-control' - if devise_mapping.rememberable? .form-group .checkbox-inline = f.check_box :remember_me = f.label :remember_me .actions = f.submit "ログイン", class:"btn btn-primary" = render "devise/shared/links"
描画される画面はこんな感じ。
試しにログインに失敗してみる。エラーメッセージがメールアドレスになっているので、文章は後で修正する。
ログインに成功すると、正しく welcome#index が描画される。
一方、Guard の方は以下のようなエラーが出てしまっている。
WelcomeController GET index returns http success (FAILED - 1)
welcome#index を描画するには認証が必要となったためである。
早速、Devise の本家に書いてあるように spec/rspec_helper.rb に以下を記述する。
config.include Devise::TestHelpers, type: :controller
また、特定の FactoryGirl を使ってログインするメソッド login_user_as を同じく spec/rspec_helper.rb に追記しておく(このメソッドは後日修正が入る)。
def login_user_as(fg_key) sign_in :user, FactoryGirl.create(fg_key) end
次に、spec/factories/users.rb の FactoryGirl の設定をする。今後、いくつかの権限を持ったユーザを増やすので、継承ファクトリを作成する。
FactoryGirl.define do factory :user do factory :user_hkob do name: 'hkob' end end end
welcome#index を描画する前に login_user_as を実行することで、エラーを回避する。
require 'rails_helper' RSpec.describe WelcomeController, :type => :controller do describe "GET index" do it "returns http success" do login_user_as :user_hkob get :index expect(response).to have_http_status(:success) end end end
この結果、WelcomeController の GET index のテストも無事通過するようになった。User のテストがなくてペンディングになっているのを除き、テストが無事に動作した。
WelcomeController GET index returns http success
今日はここまで。
written by iHatenaSync