respond_to vs routes 指定の format
イントロ
例えばAPI サーバーを作るときに「この URL にアクセスする時は、常に json
でアクセスしてほしい」みたいな事を実現したい。
それを実現する具体的な方法としては namespace :api, format: 'json'
と routes を書けばいいと思ってました。
(デフォルトの format が json になるので)
ただ
- それ以外の format を渡した時にどう動くか
- 似たような事ができる実装とどう違うのか
がよくわかってなかったので、その時にした実験をもう一度やってみたメモ。
パターン1
class Api::AccountsController < ApplicationController def index @accounts = Account.all respond_to do |format| format.html { render } format.json { render } end end end
Rails.application.routes.draw do namespace :api, format: 'json' do resources :accounts end end
- controller 側では
respond_to
で複数 format を受けつける準備ができている - namespace では
json
を指定
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 01d34bba-60c5-4185-9e96-e343d179c45b X-Runtime: 0.014599 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 10 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 404 Not Found Content-Type: text/html; charset=utf-8 Content-Length: 41119 X-Web-Console-Session-Id: 939cd9fb2834ea8353d1332a62d97e15 X-Request-Id: a77ea27e-f5c0-4766-b357-09b964188e55 X-Runtime: 6.597595 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
- controller で受け取る
format
はjson
になる - response の
Content-Type
もapplication/json
json
以外の format の request が投げられたら、404 Not found
- controller 側では html をの時は html の render をする準備ができているのにも関わらず
- routes 側で 404 になってしまうので、controller に到達しない
- controller 側に到達した時には、常に
json
形式- (書いてしまってるけど) 実質
renspond_to
で複数 format 対応は 不要
- (書いてしまってるけど) 実質
パターン2
class Api::AccountsController < ApplicationController def index @accounts = Account.all respond_to do |format| format.html { render } format.json { render } end end end
Rails.application.routes.draw do namespace :api do resources :accounts end end
- format 指定を削除
- controller 側は変わらず
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 # text/html に注目! Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 4db51f09-53cc-42ca-a6a0-49e4080b5030 X-Runtime: 0.492789 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 1715 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 7d446561-2768-4f20-8832-4d6476115d45 X-Runtime: 0.050431 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 1715 Connection: Keep-Alive
json
が通るか確かめる
% curl -I -X GET http://localhost:3000/api/accounts.json HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: c3c4a0fb-847c-4240-9470-606695c6b935 X-Runtime: 0.023534 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 10 Connection: Keep-Alive
respond_to
での指定以外のformat
% curl -I http://localhost:3000/api/accounts.xml HTTP/1.1 406 Not Acceptable Content-Type: text/html; charset=utf-8 Content-Length: 115323 X-Web-Console-Session-Id: bfb96347bd3ab4058a468ebb5762595f X-Request-Id: 74ee3306-aa22-4615-a72d-c1a7602ef434 X-Runtime: 0.640820 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
respond_to do ~ end
で指定する format の内、 最初に書いてある方が default になるrespond_to
ブロックで指定している format 以外の request が投げられたら406 Not acceptable
responders
class Api::AccountsController < ApplicationController respond_to :json def index @accounts = Account.all respond_with @account end end
Rails.application.routes.draw do namespace :api do resources :accounts end end
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: f7a06375-320d-4d37-a93c-12d9d3c17cf0 X-Runtime: 0.299781 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 0 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 406 Not Acceptable Content-Type: text/html; charset=utf-8 Content-Length: 115331 X-Web-Console-Session-Id: 780f1e5e4050cb0b7e72d36f60841f4c X-Request-Id: b0f92ee8-c6fa-4dc4-872e-f60adc942104 X-Runtime: 0.379881 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
- パターン2 とほぼおなじ
respond_to
も複数個 format が渡せるが、format
指定無しでリクエストされた場合、最初に指定されてある format が利用されるrespond_to :html, :json
なら、html
がデフォルト値
- 指定以外の
format
でのリクエストは、406 Not acceptable
responders
の利点は、controller 毎に format の指定をしなくて済むことなのかな?- 一箇所にまとめられていれば、controller 毎にデフォルトの
format
が異なるという間違いを避けられる。 - (一瞬触っただけなので、よくわかってない)
- 一箇所にまとめられていれば、controller 毎にデフォルトの
結論
サーバーが指定外の format
で request を受けた時、どう返したいかで実装が変わる
406 Not acceptable
を返す場合はrespond_to
(あるいはresponders
)- 複数 format を受け付ける URL なら、こちらのほうが自然
404 Not Found
を返す場合はnamespace :prefix, format: 'format名'
- 文字列 で指定すること。(
symbol
じゃダメ) - 例えば「
json
しか絶対受け付けない」なら、こういうのもありかも。
- 文字列 で指定すること。(
参考
emacs in terminal に移行しようと思ったけど、GUI の emacs に戻った話
イントロ
今まで GUI の emacs を使ってたけど、terminal の中で emacs を開くようにして、暫く使ってみた。
要するに emacs -nw
です。
けど色々難しいことがあって、元に戻ったという話。
苦労したところと、経緯を残します
環境
emacs 24.4
OSX 10.10.4
iterm2 2.1.1
なんで terminal の中で開きたいの?
カッコイイからです!
理由はなんとなく。
やったこと
特定のキーバインドが emacs に渡らない
ターミナル上のEmacsで本来使えない"Ctrlキー+何か"を使う方法 - すぎゃーんメモ を見て解決。
これで C-:
, C-;
, C-y
等よく使うキーバインドを機能させることができた。
コピペができない
emacs の kill-ring
と clipboard
の共有ができてなくて困った。
emacs でコピーした時に、pbcopy
を呼ぶことで、無理やり同期させるような elisp を書いた。
Emacs と Mac のクリップボードを共有する と似たような感じ。
-nw
にした時に同期できてないことに気づいたけど、特に GUI では意識してなかったポイント。
tmux prefix key と emacs のキーバインドが競合する
私は tmux の prefix key として C-q
を使っていたけど、emacs の quoted-insert
と競合する。
悩んだ末 quoted-insert
の prefix を変更した。
emacsclient の起動
# emacs -nw function emacs () { EMACS_CLIENT='/Applications/Emacs.app/Contents/MacOS/bin/emacsclient' EMACS='/Applications/Emacs.app/Contents/MacOS/Emacs' if ! pgrep Emacs; then $EMACS --daemon fi $EMACS_CLIENT -nw -q } function ekill () { pkill Emacs }
この辺りはまだ泥臭くやってます。
emacs が立ち上がってなかったら、 --daemon
付きで起動し、その後 emacsclient を立ち上げる、というスクリプトを使って凌いでいる。
terminal の中で起動してよかった事
太字は特に良かった(悪かった)ところ
- カッコ良くなった
emacsclient
の起動がかなり早くなった- パッと開いて、パッと閉じる事が可能になって気持ちいい
- いい感じに emacs で完結するようになった
- 操作性うp
shell-command
を以前より多用するようになった- だけどまだまだ shell 周りは課題が多い
よくなかったこと
- 設定で無茶をしている
- 特に「ターミナルから特定のキーバインドが渡らない」現象を解決するのに色々苦労した
- この環境を再現するために、iterm2 の設定をいじらないといけなくて微妙な感じがする
- コマンド + TAB で emacs に移れない
- emacs で完結している限りは困らないけど、時々困る(かも)
color-theme
が好みなやつがない- 自分の中でコレ!っていうのが無かった
- GUI の時と、terminal の時で、色合いが結構違う
elscreen-server
が地味に機能してない気がする件- 別の
emacsclient
を立ち上げても tab が共有されてない - 色々なところで emacsclient 立ち上げると頭使うから、こういう使い方をしたくなかった
- 別の
結局どうなったか
GUI に戻りました...
自分は zsh と emacs を両方よく使っていたのと、zsh in emacs
をイメージ通りに使えなかったのが大きかったです。
なので iterm2
(zsh 使う) と emacs
スイッチを コマンド + TAB
で行えないのが辛かった。
これがうまく機能しないと、効率が落ちてしまいます。
けどいつかは使いこなしたいこの環境、またいつか挑戦したいと思います。
delayed_job まとめ
イントロ
なんとなく使ってきて、なんとなくわかってるつもりの delayed_job
について、改めてまとめてみたもの。
休日暇だったのでソースコード読んでみたり、step 実行したりして、何処に何が書いてあるかをざっくりとまとめたものです。
環境
rails (4.2.1)
delayed_job (4.0.6)
delayed_job_active_record (4.0.3)
daemons (1.2.3)
導入
- Gemfile
gem "delayed_job_active_record"
- ActiveJob
ActiveJob のバックエンドとして、delayed_job
を指定する
config.active_job.queue_adapter = :delayed_job
- command
rails generate delayed_job:active_record rake db:migrate
基本的な仕組み
Queue
- queue は database に保存される
- テーブル名は
delayed_jobs
- scheme は gory-details に書かれている
rails generate delayed_job:active_record
で自動生成される
serialize
- ruby の object を yaml 形式で dump し、DB に保存する
handler
カラムに保存する
- 取り出す時に dump された yaml を load し、ruby のオブジェクトに戻す
worker
- worker が
delayed_jobs
テーブルを数秒おきにポーリングする - レコードがあったら、yaml をロードして ruby のオブジェクトに戻し job を実行する
- job を実行し終わったらレコードは削除される
- worker はポーリングを続ける、の繰り返し
worker の詳細
要の worker について詳しく見てみる。
woker の起動方法
rake jobs:work
bin/delayed_job start
の二通り
共通してやっていること
- worker プロセスの起動
rake jobs:work
bin/delayed_job start
- https://github.com/collectiveidea/delayed_job/blob/v.4.0.6/lib/delayed/command.rb
- 起動した worker プロセス自体の管理は
daemons
gem の仕事
両者の違いは?
rake jobs:work
は worker を起動するだけ- worker プロセスは一つだけ
bin/delayed_job
は 起動オプションを受け取り、柔軟な worker プロセスの設定ができるdaemons
gem がないと 起動不可能
なので
- 開発環境で軽く使う場合は
rake jobs:work
(もしくはrake jobs:workoff
) - 本番環境で運用する場合は、オプションを指定して
bin/delayed_job
のようなイメージ
worker の option
Delayed::Worker
クラスのアクセサとして定義する。
README に書いてあるとおり、rails なら config/initializers
の下に適当に書いておけばいい。
default_queue_name
queue
の名前。queue
カラムに入る。
destroy_failed_jobs
- true だと、失敗した job を DB から削除する(デフォルト true)
max_run_time
- job の timeout を設定(デフォルトは4時間)
- この時間より長いジョブは
Timeout::Error
を継承したDelayedJob::WorkerTimeout
をraise する
raise_signal_exceptions
- シグナル
INT
,TERM
を受け取った時の挙動を制御する - https://github.com/collectiveidea/delayed_job/blob/v4.0.6/lib/delayed/worker.rb#L53-L58 にコメントで書かれている
- シグナル
bin/delayed_job のオプション
https://github.com/collectiveidea/delayed_job/blob/v4.0.6/lib/delayed/command.rb#L26-L71
書いてあるとおり。重要そうなものをピックアップ
--number_of_workers
- 起動する worker 数
- ただし
--identifier
option との併用は不可(identifier 指定とプロセスの自動連番が衝突するから) - stop と start の worker 数が等しくないと、
bin/delayed_job stop
で stop できない
--pid-dir
- pid ファイルのディレクトリを指定
--log-dir
- log ファイルのディレクトリを指定
--identifier
- プロセス名を指定できる。指定すると
delayed_job.#{@options[:identifier]}
というプロセス名になる
- プロセス名を指定できる。指定すると
--prefix
- プロセス名を指定できる。指定すると
File.join(options[:prefix], process_name)
というプロセス名になる
- プロセス名を指定できる。指定すると
bin/delayed_job stop で何が起こるか
起動中の delayed_job
プロセスには TERM
が送られる。(daemons
gem の仕事)
https://github.com/thuehlinger/daemons/blob/v1.2.3/lib/daemons/application.rb#L374
発生する例外
起動中のプロセスが TERM, INT を受け取った時、job の挙動はDelayed::Worker.raise_signal_exceptions
の値で変わる
false
TERM
を受け取った場合- 例外は出ない
INT
を受け取った場合- 例外は出ない
:term
TERM
を受け取った場合SignalException
を raise
INT
を受け取った場合- 例外は出ない
true
TERM
を受け取った場合SignalException
を raise
INT
を受け取った場合SignalException
を raise
コードを見たほうが腑に落ちるかも https://github.com/collectiveidea/delayed_job/blob/v4.0.6/lib/delayed/worker.rb#L132-L142
例外が出た後に何が起こるか
:error
の callback が登録されていれば、それを実行する- Job を DB に戻して、再度実行し直す(
handle_failed_job
)
callback について
- error のフックとしては
:before
,:after
が空席handle_failed_job
の前と後に実行されるcallback
- callback の定義方法はコードは
Lifecycle
というクラスに書かれてある
書き方は以下の様な感じ
Delayed::Worker.lifecycle.before(:error) do |worker, job| # puts worker # puts job end
ブロック引数に
run_callbacks(:error, self, job)
の第二引数以降、即ちDelayed::Worker
,Delayed::Job
インスタンスを受け取れる- ただし、
callback
を定義する場所に注意- 実質
config/initializer
しかない? - アプリ側で動的に callback を定義しても、既に起動中の worker には伝わらない
- ワーカーに依存しない処理が書ける場所
- 実質
Capistrano
https://github.com/collectiveidea/delayed_job/blob/v4.0.6/lib/delayed/recipes.rb
- 各 host で
bin/delayed_job
コマンド + オプションを実行する
だけ。
つまり bin/delayed_job
の章で述べたとおり、capistrano で deploy した時、実行中の job は中断し DB に戻される。
そして新しいコードを load した状態で、再度 job が実行される、という流れになる。
細かすぎて伝わらない delayed_job のネタ
実は Delayed::Job
というクラスはない
正確には collectiveidea/delayed_job に定義されていない、ということ。
実体は collectiveidea/delayed_job_active_record になっていて、このクラスが include Delayed::Backend::Base
している
>> Delayed::Job => Delayed::Backend::ActiveRecord::Job
なので Delayed::Job
インスタンスのメソッドを調べる時は delayed_job_active_record か Delayed::Backend::Base を見ると良い。
find_available メソッドの謎
Delayed::Backend::Base.reserve
に定義されている find_available
メソッド、よく調べてみたけど、実は何処にも定義されていない。なので NoMethodError
が発生するはず。
https://github.com/collectiveidea/delayed_job/blob/v4.0.6/lib/delayed/backend/base.rb#L43
けど上記で述べたとおり、実体は collectiveidea/delayed_job_active_record のオブジェクトになっていて、https://github.com/collectiveidea/delayed_job_active_record/blob/v4.0.6/lib/delayed/backend/active_record.rb#L43 で reserve
を上書きしているから、実質問題は起きていない。
delayed_job
バックエンドによっては find_available
を上書きしているものもあるらしい
まとめ
- delayed_job の仕組みについて調べました
- DB に queue を溜めて、worker がそれを取り出して実行する
- worker の起動方法は2種類ある
rake
とbin/delayed_job
の2つ
- worker の設定は
Delayed::Worker
クラスのアクセサとして定義- worker の設定は沢山あるんやで
- worker を stop させると、worker が既に動いていても DB に一旦戻されて再実行を行う仕組みになっている
- デフォルト
authlogic で、ユーザーの password を編集した直後に強制ログアウトされる件
イントロ
authlogic
でハマったネタ第二弾。
まとめると以下の様な感じ。
- authlogic でログインユーザーを作成
- よくある 名前と password で認証するユーザー
- ログインユーザーを編集する画面を作る
- password を編集可能にする
- ログインユーザーの password を更新すると、何故か強制的にログアウト状態になる
- ログインを要求する url に強制 redirect される
解決法
class User < ActiveRecord::Base acts_as_authentic do |c| c.session_class = UserSession end end
この解決方法はあくまで一例。
原因
authlogic
のログイン状態は、DB に保存されているpersistence_token
とsession[:user_credentials]
の一致により判定される。user_credentials
という session の key はログイン用のモデルによって変更の可能性あり
authlogic
で password を更新すると、強制的にpersistence_token
も再生成される- この時 session の値も再生成された文字列に更新しなければならない
- 設定が甘くて、password を更新した時に session の値が更新されなかったため、ログイン状態が維持されなかった
- authlogic がログイン画面にリダイレクト
のような感じ。
肝心の session の値も再生性された文字列で上書きしなければならない
設定は以下のメソッドで行っている。
恐らくこの値は true でなければいけないが、false になっているとこんなおかしな状況になる。
def update_sessions? !skip_session_maintenance && session_class && session_class.activated? && self.class.maintain_sessions == true && !session_ids.blank? && persistence_token_changed? end
今回私が遭遇したケースでは session_class
が何故か nil を返すようになっていたので、session が update されず、ログアウト状態になってしまっていた。
session_class について
session_class
とは
Authlogic::Session::Base
を継承した session クラスを返すメソッド- 普通は自動で判定される
User
がacts_as_authentic
ならUserSession
という風に
ので、普段は意識しなくていいはず。
例えば Admin
モデルを作って、Admin
専用の管理画面を作ろう、となった時に Session クラスを AdministratorSession
としてしまうと、session_class
を指定する必要がある。
設定を忘れるとこのような目にある。
まとめ
Authlogic::ActsAsAuthentic::SessionMaintenance::Methods#update_session?
メソッドを調べればよろし
authlogic で blank の password で update 出来てしまう問題
イントロ
情報が古いけど
改めてまとめると、以下の様な状態。
authlogic
でログインユーザーを作成- ログインユーザーは
password
を form に入力する
- ログインユーザーは
- ログインユーザーを編集する画面を作る
- 新規作成ではないので注意
password
を編集可能にする
- ログインユーザーを編集するとき、パスワードを空にして保存できてしまう
validates_length_of
は何も設定しないなら 4 文字が minimum なので、空で保存できるのはおかしい- https://github.com/binarylogic/authlogic/blob/23751e3eaba4c2b09751505079d7264fd1cc8b72/lib/authlogic/acts_as_authentic/password.rb#L95
解決法
class User < ActiveRecord::Base acts_as_authentic do |c| c.ignore_password_blank = false end end
とする。
よく見ると
のコメントに書いてある。
前提条件も含めて説明すると...
- 編集画面において、
password
を最初から入力済みにすることはできない- DB に保存されている
password
を復号することはできないため(暗号化方式によるが) - つまり編集画面を開いた直後はどうしても空になってしまう
- DB に保存されている
- 状況として「
password
は変えたくないけど、ページを遷移してしまう」ケースが存在するだろう- どういうケースかは明記されてないけど、例えば間違って「更新」ボタンを押してしまったりするとか?
- そうすると、空のパスワードが保存されてしまい、セキュリティ的に甘くなる
- そういうケースは避けたい
- オプションで変更できる
ということらしい。(多分)
Capybara::Poltergeist で remote debug が動かない件
イントロ
タイトルの通り。Capybara::Poltergest
の目玉機能である remote debugging
がうまく動作しなくて、色々試行錯誤した話。
https://github.com/teampoltergeist/poltergeist#remote-debugging-experimental
環境
OSX 10.10.3
phantomjs 2.2.0
poltergeist 1.6.0
chromium 45.0.2411.0
remote debugging とは
poltergeist
の README に書いてあるとおり
Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, js_errors: false, inspector: true) end @session = Capybara::Session.new(:poltergeist) @session.visit url @session.driver.debug
のような感じで、driver.debug のタイミングでブラウザを起動し、debug できる機能らしい。
使い方は簡単で
Capybara::Poltergeist::Driver.new
にinspector: true
オプションを渡すdriver.debug
を呼ぶ
だけで良い。
苦戦した所
1. そもそもブラウザ起動しない
# @session.driver.debug を使った spec を実行させる bundle exec rspec ... Poltergeist execution paused. Press enter to continue. The file //localhost:9664 does not exist.
てっきり chrome
か何かが自動で起動するんだろうなぁと思ってたが、file //localhost:9664
がないと言われて怒られてしまう。
解決法
微妙なアプローチだけど、chromium
を install して回避してみた。
brew install Caskroom/cask/chromium
ln -s /opt/homebrew-cask/Caskroom/chromium/latest/chrome-mac/Chromium.app/Contents/MacOS/Chromium /usr/local/bin/chromium
として、chromium コマンドをむりやり PATH
に追加。
これだけで良い。
というのも、ブラウザ起動の仕組みは単純で、
chromium chromium-browser google-chrome
のいずれかのコマンドが PATH に通ってれば実行される、というものだから。
chromium http://www.google.co.jp
と叩いて chromium
が起動して google
トップページが表示されるならOKなはず。
google-chrome
コマンドがどうやら linux 環境でのコマンドというのと、mac で google-chrome
コマンドに相当するものを見つけられなかったので、こんな感じになりました。
Check failed: feature. と言われて起動できない。
ところが上記をやっても解決しない。
chromium
が動こうとするのだが、以下の様なエラーが出て、chromium
が起動できない。
Poltergeist execution paused. Press enter to continue. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest. [86324:1299:0524/032749:FATAL:feature_provider.cc(75)] Check failed: feature. FeatureProvider 'behavior' does not contain Feature 'whitelisted_for_incognito'
色々調べてみたがよくわからない。
chromium
のソースコードにそれっぽいものを発見して、右往左往したけど挫折。
解決法
これは 予め chromium を起動した状態で、@session.driver.debug を実行する と回避できる。
要するに chromium
起動させて rspec 叩くわけです。
すると
のように file:///localhost:9664
を開こうとしているから、file:///
を消してエンター。
URL があらわれた。URL をクリック。
無事コンソール画面へ
まとめ
溢れんばかりのバッドノウハウ感。
poltergiest
の experimental な feature なので、参考になれば。
google-chrome
使うことができれば、妙なエラーはでない?のかもしれない。
みんなどうやってるの...
bootstrap-generators で bootstrap 導入後、一部の flash が使えなくなる現象
イントロ
bootstrap 導入すると、flash がちゃんと動かなくなって戸惑うやつ。
decioferreira/bootstrap-generators で生成する flash 部分にミスがあるのが原因(多分)
具体的には flash[:alert]
時でも、緑枠の flash メッセージになってしまう現象。
ちゃんと考えると直せるんだけど、思い出すのが面倒なので残す。
環境
rails 4.2.1
sass-rails 5.0.3
bootstrap-sass 3.3.4
bootstrap-generators 3.3.1
各 gem の install 方法なども省略。 README.md
の通りです。
bootstrap-generators について
意外とスター数が少ないので補足。
rails で twitter-bootstrap 導入しようとすると、有名なのは以下の2つ。
- seyhunak/twitter-bootstrap-rails
LESS
- generator 付き
- install 面倒 (随分前の記憶だけど)
- 有志が作ってる?
- twbs/bootstrap-sass
sass(scss)
- generator 付いてない
- install 簡単
- 公式(
Official Sass port of Bootstrap
)
bootstrap-sass
のほうが簡単に導入できるが、generator が付いていないので、view 等が手作りになってしまう。
一方で twitter-bootstrap-rails
は導入が面倒だが、generator が便利で、一回インストールできてしまえば楽になってくる、という感じ。
正に一長一短な2つの gem なわけですが、実は boostrap の generator は gem になって切りだされていて、bootstrap-sass
と合わせることで「install 簡単」と「generator」のイイトコロ取りをできるわけです。
その generator が decioferreira/bootstrap-generators です。
rails generate bootstrap:install
これで bootstrap のあの黒いヘッダー(やそれ以外も)が生成されます。
haml/slim/erb
に対応している。
直し方
app/views/layouts/application.html.erb
(bootstrap 導入後)
の class を作っている部分を直す。
<% flash.each do |name, msg| %> - <%= content_tag :div, :class => "alert alert-#{ name == :error ? "danger" : "success" } alert-dismissable", :role => "alert" do + <%= content_tag :div, :class => "alert alert-#{ name == "alert" ? "danger" : "success" } alert-dismissable", :role => "alert" do <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> <%= msg %> <% end %> <% end %>
後はいつもどおり flash[:alert]
してあげればよい。
if @user_session.save redirect_to root_url, notice: "Login successful" else redirect_to login_url, alert: 'Login failed.' end
何も考えず redirect_to login_url, error: 'Login failed.'
みたいにやっても効かないので注意。
(view で :error
とあるから引っかかった。)
参考
おまけ
記述量を最小限にして直すと、上記のようになるけど、いやちゃんと考えたい、という場合には以下が参考になる。
以下の 3 つのうち、どれかを使えばひとまずは大丈夫そう。
(要は flash のキーが bootstrap のクラスに対応していればいいだけなので)
heroku で Postgres 9.4 を使う
イントロ
タイトルの通り。jsonb 型のカラムを取り扱おうとしたら、激しく怒られた。
rake aborted! StandardError: An error has occurred, this and all later migrations canceled: ... PG::UndefinedObject: ERROR: type "jsonb" does not exist LINE 1: SELECT 'jsonb'::regtype::oid ... : SELECT 'jsonb'::regtype::oid/app/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:155:in `async_exec' ...
原因
Postgres の default version が 9.3
だから。jsonb
型は 9.4
からサポートされているので、そんな型しらねーよと言われている。
https://devcenter.heroku.com/articles/heroku-postgresql#version-support-and-legacy-infrastructure
解決法
https://blog.heroku.com/archives/2015/1/8/postgres-9-4-on-heroku
9.4 に upgrade して、DATABASE_URL
を切り替えるだけでよい。
% heroku addons:add heroku-postgresql --version=9.4 --app test Adding heroku-postgresql on test... done, v14 (free) Attached as HEROKU_POSTGRESQL_YELLOW_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation.
config:unset
% heroku config:unset DATABASE_URL --app test Unsetting DATABASE_URL and restarting test... done, v15
config:set
% heroku config --app test === test Config Vars .... HEROKU_POSTGRESQL_ROSE_URL: postgres://xxxxx HEROKU_POSTGRESQL_YELLOW_URL: postgres://yyyyy ....
upgrade した時のログにも書いてあった通りで、HEROKU_POSTGRESQL_YELLOW_URL
という環境変数が attach されているので、この value を DATABASE_URL に set する。
heroku config:set DATABASE_URL=postgres://yyyy
再起動
migration して再起動して反映させる。
heroku run rake db:migrate --app test heroku run rake db:seed --app test heroku restart --app test
rails で id を BIGINT 型 + primary key + AUTO_INCREMENT する
イントロ
rails のモデルは自動で id というカラムを作ってくれますが、これは常に INT 型。
BIGINT
型にしようとしたら、結構ハマったのでメモする。
使っている database はmysql
です。
(postgresql
だとこの罠は回避できるのだろうか?)
tl;dr
- 普通に頑張ると...
db:migrate
とdb:setup
の実行結果が異なる- migration ファイルの内容が、
schema.rb
に(一部)反映されないため
- migration ファイルの内容が、
db:migrate
とdb:setup
の実行結果が異なると、db:rollback
ができなくなるので、辛くなりそう。db:migrate
かdb:setup
どちらかを使わない、という運用方式もある- だが、面倒くさいので、そういうことを意識したくない
- 解決方法は 2 つ
format_schema = :sql
- kamipo/activerecord-mysql-awesome を使う
普通にやってみる
Rails探訪 ~ create_table 編 ~ | マネーフォワード エンジニアブログ の素晴らしい参考記事によると、こんな感じ。
(ところで参考記事を見ると、色々地雷を踏み抜いてこの結論なので、この時点で既に普通でない気もする)
class CreateUsers < ActiveRecord::Migration def change create_table :users, id: false do |t| t.column :id, 'BIGINT PRIMARY KEY AUTO_INCREMENT' t.string :name t.timestamps null: false end end end
rake db:migrate
% rake db:drop db:create db:migrate
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
無事成功。
rake db:setup
一見うまくいってそうだが、実はこれは問題がある。
db/scheme.rb
に BIGINT
型にしたよ、という内容が反映されていないために、db:setup
をやると db:migrate
の結果と異なったものになってしまう。
% rake db:setup
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.01 sec)
INT型
に戻ってしまった。
じゃあどうするの?と言うと、schema_format = :sql
方式か activerecord-mysql-awesome
方式の二通りがある。
schema_format = :sql
よく見かける解決法。
デフォルトでは schema_format
は ruby なので、これを sql にする。
:ruby
では db/schema.rb
を生成するが、:sql
にすると db/structure.sql
を生成してくれる。
config/application.rb
等に予め設定しておく。
config.active_record.schema_format = :sql
rake db:migrate
migration ファイルはここと同じものを使う。
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.01 sec)
bigint 型になっている。ここまでは良い
rake db:setup
% rake db:setup
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
今度は migrate
と setup
の結果が同じになりました。
activerecord-mysql-awesome
kamipo/activerecord-mysql-awesome
を使う。
この場合 schema_format
は :ruby
のままで良い。
class CreateUsers < ActiveRecord::Migration def change create_table :users, id: :bigint do |t| t.string :name t.timestamps null: false end end end
のような migration ファイルを作る
rake db:migrate
% rake db:drop db:create db:migrate
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
rake db:setup
% rake db:setup
mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
migrate
と setup
の結果が同じになった。
change_column 編
create_table
ではなく、change_column
でやったらどうなるか。
create_table
の時点で INT
を BIGINT
にしようと思うことが少ないので、実は change_column
で対応できるかどうかが重要。
create_table
の時と違って、今度は db:rollback
が成功するかどうかもチェックしなければならない。
activerecord-mysql-awesome / schema_format = :sql
sql の確認コマンドは鬱陶しいので省略。
class ChangeIdColumnToUsers < ActiveRecord::Migration def change reversible do |dir| dir.up do change_column :users, :id, :bigint, auto_increment: true end dir.down do change_column :users, :id, :int, auto_increment: true end end end end
この migration ファイルで
rake db:drop db:create db:migrate
rake db:setup
rake db:rollback
が成功する。ばっちり。
activerecord-mysql-awesome
でも schema_format = :sql
でも同じ結果になったので、change_column
の時はこうすれば良さそう。
普通にやる
勿論これも activerecord-mysql-awesome
か schema_format = :sql
をせずに migration しても、 db:setup
で id が INT型
になってしまう。
おまけ
https://github.com/rails/rails/pull/18220 がマージされているので、現時点での rails の master では対応済みのよう。
なので、 第三の選択「もうしばらく待つ」 も有りかも。
少なくとも Rails 4.2.1
では修正は含まれていなかったです。
ubuntu で install 済みのパッケージリストを表示する
イントロ
yum list installed
的なやつ。
apt-get list
のようなコマンドがあるだろうと思っていたが無かった。
コマンド
dpkg --get-selections
参考
http://www.howtogeek.com/howto/linux/show-the-list-of-installed-packages-on-ubuntu-or-debian/