ActiveAdminを使用する場合はassetの汚染に注意
ActiveAdmin を使用すると簡単にデータの管理画面が作れますがデフォルトだとasset周りで問題が発生しやすいのでメモしておきます。
使用したバージョン
- rails 4.0.2
- activeadmin 1.0.0.pre
発生した問題
- 管理画面以外の画面にもActiveAdminのcssが読み込まれ、ボタン等が管理画面と同じものになる
- 管理画面以外の画面にもActiveAdminが保持するjQueryが読み込まれ、jQuery Pluginが動作しなくなる
どちらもActiveAdminを使用しないページにまでActiveAdminのassetが読み込まれてしまうのが原因です。
rails generate active_admin:install
を実行した際にapp/assets/javascripts/active_admin.js
とapp/assets/stylesheets/active_admin.css
が作成されますが、これらがapplication.js
、application.css
によって読み込まれてしまっています。
解決方法
管理画面でのみActiveAdminのassetが読み込まれるようにします。
以下のページを参考にしました。
Excluding active_admin JS and CSS from your Rails app - Dan Adams
まず、app/assets/javascripts/admin
ディレクトリを作成し、そこにactive_admin.js
を移動します。
次に、app/assets/javascripts/main
ディレクトリを作成し、管理画面以外の画面のjsファイルを移動します。
そして、application.js
の//= require_tree .
を//= require_tree ./main
に変更します。
cssについても同様です。
それらが済んだらconfig/initializer/active_admin.rb
に以下の内容を追加します。
config.clear_stylesheets!
config.register_stylesheet 'admin/active_admin.css'
config.clear_javascripts!
config.register_javascript 'admin/active_admin.js'
Herokuを使用している場合はさらにconfig/application.rb
に以下の内容を追加します。
config.assets.precompile += %w[admin/active_admin.css admin/active_admin.js]
rails serverを再起動して意図した通りにassetが読み込まれていればOKです。
ActiveAdminに限らずjQueryをバンドルしているgemはたまにあるので同様の問題に注意する必要があります。
speed_gun gemはconfigでjQueryの読み込み設定が可能でした。
なお、解決方法を探してるとActiveAdminのassetをvendor/assets
に配置するという情報もありましたが手元で試した限りでは解決しませんでした。
GitHubの方に今回の問題に関するIssueがあったのでそのうち公式に解決されるかもしれません。
rspec-rails3.0でのControllerテストの書き方
Controllerテストの書き方のメモ。
通常のページ
require 'spec_helper'
describe HogesController do
describe "GET show" do
let(:request) { get :show, id: 1 }
subject do
request
end
it { should be_ok }
it { should render_template "show" }
describe "@hoge" do
subject do
request
assigns(:hoge)
end
it { should be_present }
end
end
describe "DELETE destroy" do
let(:request) { delete :destroy, id: 1 }
before do
@hoge = stub_model(Hoge, destroy: nil)
allow(Hoge).to receive(:find).and_return(@hoge)
end
subject do
request
end
it { should be_success }
it { should redirect_to hoges_path)
describe "@hoge" do
subject do
request
assigns(:hoge)
end
it { should be_present }
it "#destory called once" do
expect(@hoge).to receive(:destroy).once
subject
end
end
end
end
get
やpost
はResponse
オブジェクトを返すのでそれをテストする。
ロジックの呼び出しに関してはモックでテスト。
ステータスコードは、
its(:status) { should eq 200 }
# または
its(:status) { should eq Rack::Utils.status_code(:ok) }
でも確認できるが可読性やタイプ数を考えるとbe_*
で済むものは済ませたい。
使いそうなのは、
- be_ok # 200
- be_success # 200番台
- be_successful # 200番台
- be_redirect # 301, 302, 303, 307のどれか
- be_forbidden # 403
- be_not_found # 404
- be_missing # 404
あたりか。
アサインした変数と意図したテンプレートが使われたかはチェックするがそのレンダリング内容についてはViewのテストで行うのでここではテストしない。
@hoge
はFactoryGirl.create(:hoge)
でも良いのだがstub_model
の方がテストの実行時間が短くなるので積極的に利用する(そもそもコントローラのテストは本質的にはDBは不要)。
※2014-03-07 stubを使う部分のコードがmochaのものになっていたのでrspecのものに修正
mochaをインストールしていたせいかspec_helper.rb
でmochaを使う設定にしていないのにstubs
やreturns
を使っていても表面上はエラーにならなかった。
しかし、specファイルを指定して実行してもエラーにならないがall testだとエラーになる現象が発生するようになった(100%ではなくall testが通る場合もある)。調べるとあるspecファイルで使ったstubs
が他のspecファイルでも有効になっていた(そのためテストの実行順によってはall testが通った)。
Deviseを使う場合
spec/support/devise.rb
を以下の内容で作成。
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
これでsign_in
, sign_out
が使える。
また、DeviseController
を継承したコントローラのテストではリクエスト前に
@request.env["devise.mapping"] = Devise.mappings[:user]
が必要になる。
Ajaxを使う場合
Viewのlink_to
等でremote: true
とした場合、.js
がリクエストされるのでテストでは次のようにリクエストする。
delete :destroy, id: 1, format: :js
これで406 Not Acceptable
になるようならアクションのrespond_to
にformat.js
が書かれていないと思われる。
また、401
になる場合はログイン状態、403
になる場合はCanCan等承認周りを確認する。
foremanでターミナルウィンドウを減らす
railsでWebアプリを作っていると実行させっぱなしにしておく必要があるコマンドがいくつかある。
例えば以下のようなもの。
- rails server
- guard
作りがマズい(粗い)とポートを変えてWebサーバを複数立ち上げたりすることもある。
今まではその数だけターミナルを開いてそれぞれ実行していたが、パラメータを含めてコマンドを毎回打つのは面倒だし、ターミナルの切り替えも面倒だった。
foremanがそれを解決してくれた。
前提
- rails serverが実行可能
- bundle exec guardが実行可能
- foremanがインストールされている
Procfileの作成
アプリのrootディレクトリに以下の内容のProcfileを作成する。
web: rails server
guard: bundle exec guard -i
foremanを実行
foremanを実行する。
bundle exec foreman start
2つのプロセスが実行状態になり、出力が色分けして表示される。
foremanを停止
foremanを実行中のターミナルでctrl-cを押すと実行していたすべてのプロセス停止する。
Herokuを使用する場合
Herokuもforemanを使用しているためProcfileを含めたソースをアップロードするとHerokuのforemanがそれを使用してしまう。
その場合はProcfileをProcfile.dev等にリネームし、
bundle exec foreman start -f Procfile.dev
のように開発環境ではファイルを指定すれば良い。
感想
まだ使い始めたばかりだけれど便利そう。
SSL証明書の更新
RapidSSLからSSL証明書期限切れ90日前の通知が来たのでギリギリになって焦らないように早めに更新作業を行うことにした。
Netowl上での作業
新規取得手順については以下の記事に書いたが、「新規」という文言が「更新」に変わり入力項目が減ったくらいで更新手順はほとんど同じだった。
CSRの作成とSSL証明書の取得 - yamotonalds's blog
なお、CSRは以前のファイルを使い回した。
すんなり終わるかと思っていたら問題が発生した。
更新のはずが新規扱いで処理された
証明書詳細画面を確認すると申請区分は更新になっているのに有効期限が今日から1年間になっていた。今日は有効期限90日前だったので90日分損していることになる。
そもそもこれでは更新ではなく新規と何も変わらない。
SSLボックスの問い合わせフォームから問い合わせると(問い合わせを受け付けた旨のメール等は無かったが)しばらくしてカスタマーサポートからメールが来た。一部抜粋すると、
発行元とこちらの時差が原因で「90日前」に達する前に
手続が行われてしまい、新規の取り扱いとなってしまったようです。
とのこと。
発行元がRapidSSLだとするとそこからのメールで今回の作業を始めたはずだがどういうことだろう。
詳しい原因はさておき、新規扱いとなった更新をキャンセルして更新申請をし直すことになった。
念のためキャンセルの流れをメモしておく(可能性は低いが誰かの参考になれば良い)。
まず、キャンセル申請はネットオウル側行ったらしく、数日後に
RapidSSL Request for Order Cancellation
というメールが届いた。I confirm the cancellationと返信しろと書いてあったのでそれだけ書いて返信した。
ちなみに、
Order Information Request for <ドメイン名>
というメールもほぼ同時に届き、こちらはWebページ上でキャンセル確認ができそうだったがサポートセンターの指示通り前述のメールにだけ返信し、こちらのメールは無視した。
翌日、RapidSSLから以下のメールが届いた。
AUTO-REPLY: Re: RapidSSL Request for Order Cancellation (#<番号>)
キャンセルを受け付けた旨と作業に2営業日程度かかる旨が書かれていた。
3日後、RapidSSLから以下のメールが届いた。
RE: Re: RapidSSL Request for Order Cancellation (#<番号>)
キャンセルが完了した旨が書かれていたのでサポートセンターに転送。サポートセンターからキャンセル作業を進める旨のメールが届く。
そして数時間後、キャンセル完了のメールがサポートセンターから届いたので最初にやったように管理画面から更新申請し、今度はちゃんと更新された有効期限になっていることを確認した。
AWS上での作業
話は戻って更新作業の続き。
以前設定した時の手順をブログに書いてるつもりだったが書いてなかったようなので改めて書く。
まったくの新規からの作業ではないので抜けがあるかもしれないがあしからず。
前提
手順
- ブラウザでAWS ConsoleにログインしてEC2の管理画面へ移動
- サイドバーのNETWORK & SECURITYからLoad Balancersをクリック
- 表示されたelbの一覧からSSL証明書を適用したいものを選択し、下の詳細パネルのListenersタブをクリック
- 自分の場合、httpとhttpsを両方使うので、elbの80番ポートからinstanceの80番ポートへのSSL無しの行と、elbの443番ポートからinstanceの80番ポートへのSSL有りの2行が表示されている。
- SSL有りの行のSSL Certificate列のChangeをクリックする。
- SSL証明書選択画面がポップアップするのでUpload a new SSL Certificateを選択。
- Certificate Nameに今から登録する内容に適切な名前を入力する。対象ドメイン名と有効期限が分かるものが良いと思う。(例: example_com__2014-04-01_2015-03-31)
- Private Keyにkeyファイルの中身(-----BEGIN RSA PRIVATE KEY-----から-----END RSA PRIVATE KEY-----まで)を入力。今回は以前のCSRを使い回しているのでkeyファイルも前と同じもの。
- Public Key Certificateに証明書詳細画面のCERT(SSL証明書)からダウンロードしたファイルの中身(-----BEGIN CERTIFICATE-----から-----END CERTIFICATE-----まで)を入力。
- Certificate Chainには証明書詳細画面の中間証明書の内容を入力しても良いが、Android 2.2より古いスマートフォン等の新しい証明書に対応していないものに対応するなら、 RapidSSL - Knowledge Center - SSL Certificates Support のDownload the RapidSSL CA Bundleからダウンロードしたファイルの中身(-----BEGIN CERTIFICATE-----から-----END CERTIFICATE-----までが2つ)を入力する。
- SaveボタンをクリックするとChoose from your existing SSL Certificatesが選択され、先ほど作成した名前がプルダウンで選ばれた状態になるがここから先はBeanstalkの設定で行うためCancelをクリックする。
- Beanstalkの管理画面に移動し、SSLに対応したい環境名をクリック、サイドバーのConfigurationをクリックする。
- 表示された画面の下部、Network TierのLoad Balancingの歯車をクリックする。
- Load BalancerのSSL certificate IDプルダウンから先ほど作成したものの名前を選択し画面右下のSaveをクリックする。
- 環境の更新が始まるので正常に終わったらサイトにアクセスしてSSL証明書が更新されていることを確認する(更新されていなければスーパーリロードも試す)。
EC2の画面でSSL証明書のアップロードをしておいて適用はBeanstalkの画面から行うというのが少しわかりにくいかもしれない。
アップロードされたSSL証明書はIAMに保存されるようなのでIAMの画面で操作できれば良いのだが見つからなかった。
今後IAMの画面で操作できるようになるかもしれない。
rspec-rails3.0でのViewテストの書き方
rspec-rails3.0でのViewテストの書き方のメモ。
まずは公式に目を通しとくべき。
Upgrade - RSpec Rails - RSpec - Relish
view spec - View specs - RSpec Rails - RSpec - Relish
重なる内容もあるけど個人的に気になった点を以下に記述する。
Capybaraのmatchersを使う
have_tag
は無くなったようなのでCapybaraをインストールしてそのmatchersを使う。
Module: Capybara::Node::Matchers — Documentation for jnicklas/capybara (master)
spec_helper.rb
に以下の行を追加。
require 'capybara/rspec'
テストコードは以下のような感じになる。
describe "layouts/_navbar.html.haml" do
subject do
render partial: "layouts/navbar"
rendered
end
describe "session info" do
context "user not signed in" do
it { should have_text I18n.t('layouts.navbar.sign_in') }
end
end
end
have_cssではオプションのキーを指定する
have_css
(have_xpath
, have_selector
)のオプションには以下のようにtext:
等のキーを付ける。
it { should have_css(".username", text: "アリス さん") }
付けなくてもテストは動くが、表示されるメッセージに違いがある。
# text: を付けた場合
should have css ".username" with text "アリス さん"
# text: を付けずに文字列のみを渡した場合
should have css ".username"
Library not loaded
発生条件まで追っていないが、自分の環境ではCapybaraをインストールするとrails起動時に以下のエラーが発生するようになった。
/Users/yamotonalds/Work/project_dir/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:251:in `require': dlopen(/Users/yamotonalds/Work/project_dir/vendor/bundle/ruby/1.9.1/gems/nokogiri-1.6.0/lib/nokogiri/nokogiri.bundle, 9): Library not loaded: /Users/yamotonalds/.rvm/rubies/ruby-1.9.3-p327/lib/libruby.1.9.1.dylib
Referenced from: /Users/yamotonalds/Work/project_dir/vendor/bundle/ruby/1.9.1/gems/nokogiri-1.6.0/lib/nokogiri/nokogiri.bundle
Reason: image not found - /Users/yamotonalds/Work/project_dir/vendor/bundle/ruby/1.9.1/gems/nokogiri-1.6.0/lib/nokogiri/nokogiri.bundle (LoadError)
nokogiri
を1.6.0から1.6.1にバージョンアップするとエラーは発生しなくなった。
bundle update nokogiri
Deviseを使っている場合
ReadmeにRSpecについての記述がある。
spec/support/devise.rb
を作成して以下を記述する。
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.include Devise::TestHelpers, type: :view
end
これで以下のようにsign_in
等が使える。
before do
sign_in FactoryGirl.create(:user)
end
view.stub(:current_user, stub_model(User))
とする方法もあるようだが、user_signed_in?
等の他のメソッドもあるためUser
クラスが使えるなら上記の方法で良いと思う。
helperのstub
before do
view.stub(:admin?).and_return(true)
end
stub_modelとmock_modelの使い分け
大体以下のような感じだと思う。
stub_model
呼び出すごとに固有のid
(正確にはto_param
)を持ったインスタンスを作成する。指定するクラスは実在しなければならない。
つまり、実際にDBにデータを保存せずに永続化されたオブジェクトのdoubleを作るメソッド。
stub_model(Widget).as_new_record
とすることで永続化していないオブジェクトのdoubleも作れる。
mock_model
基本的にはstub_model
と同じで、ActiveModel
のメソッドがstub化されたdoubleを作るメソッド。指定するクラスはActiveModel::Naming
を継承している必要がある。
stub_model
との大きな違いは実在しないクラスでも文字列で渡せばOKなところ。自動的にActiveModel::Naming
を継承したクラスを作ってくれる。
分業しててViewのテストを作る時にまだ必要なクラスが作成されていない場合等に使える。
注意: Ruby 1.9.3では"Hoge::Foo"
のようにモジュール内のクラスを指定するとNameError
が発生する。Ruby 2.1.0では修正されていた。