一分一秒真剣勝負!

Ruby, Railsなど。Web系の技術ネタを充実させたい・・が、そうなるかは分からない。

Railsのエラーハンドリング

Rails3.2から結構いい感じになったエラーハンドリング

Railsは1.xからやっているけど、気に喰わないのがエラーハンドリング周りだった。 特にRoutingエラーを補足する為に各バージョンごとに対応が微妙に違ったりして、毎回調べたりRailsの実装を追っていた記憶がある。 だけどRails3.2から実装された機能で、個人的にはこれで落ち着いたかなと思えた。 既に4が出ているので今更感があるけど、備忘録として書いておく。 Rails4.0.2,Ruby2.0.0で確認しています。

Release Notesでは以下のように書かれている。

Added config.exceptions_app to set the exceptions application invoked
by the ShowException middleware when an exception happens.
Defaults to ActionDispatch::PublicExceptions.new(Rails.public_path).

つまり、例外発生時に呼ばれる例外処理アプリケーションを config.exceptions_app で設定できるようになった。 デフォルトではActionDispatch::PublicExceptions.new(Rails.public_path)が設定される。

どうやって使うか

色々と調べた結果、ActionDispatch::Routing::RouteSetをセットしてやるのが一番シンプルかなーと思った。 まずはself.routesをセット。

# config/application.rb
config.exceptions_app = self.routes #=>ActionDispatch::Routing::RouteSet

で、例外処理用のコントローラを作成しておく。

# app/controllers/erros_controller.rb
class ErrorsController < ApplicationController
  def not_found
    render status: 404
  end

  def internal_server_error
    render status: 500
  end
end

最後にroutes.rbで各httpステータスコードを判定し、ErrorsControllerの各メソッドを指定してやる。

# config/routes.rb
  get '/404', to: 'errors#not_found'
  get '/500', to: 'errors#internal_error'

他のやり方

self.routes以外を指定する事も当然出来る。 というかRackアプリを指定できるのでRailsで無くてもOKです。 例えば以下の様な感じ。

# config/application.rb
config.exceptions_app = ->(env) { ErrorsController.action(:error_page).call(env) }

明確にエラーコードを表示したいウェブアプリだと使うかも。

思うこと

エラーハンドリングに関してはRails newした時に雛形を作るなどしてしまってもいいのではと思った。フリーランスで働いていた時に、途中から入ったプロジェクトの場合、エラーハンドリングの実装方法がバラバラで、その内容によっては無駄に工数がかかるケースもあったので。 ま、Rails is omakaseなので嫌なら使うなって話ですが。

Capistrano3が良さげ

CapistranoのVersion3がリリースされてますね。変更点は以下のサイトに大量に書かれています。
Capistrano Version 3 ReleaseAnnounceme

変更点が多過ぎて全部まとめるのは諦めたので、個人的にこれはいいなーと思った点を上げます。

良かった変更点

  • マルチステージをデフォルトでサポート

もうこれでcapistrano-extはいらない。まあ、これは標準サポートされてもいいですよね。

  • デプロイの高速化

確か以前はコマンド1回実行毎にsshのセッションを貼り直していたので遅かったはず。SSHのライブラリを変更したのと同時にココらへんも改善されたのかも。ただ、やっぱりRailsをデプロイする時はassetsのコンパイルでちょっと止まりますね。

  • 実装が見やすくなった

capistrano2はネットにドキュメントが少なくて結局実装を読む事になる場合が多かったのですが、2と比較すると3の方が見やすくなってました。これは助かります。

  • linked_files, linked_dirs

これ凄い便利です。シンボリックリンクを貼りたいファイルがあった時に短く書ける。確かcapistrano2の時は確か無かったと思いますが・・どうなんだろ。
例えばdatabase.ymlのようにgitなどで管理したくないファイルを渡してやると、

set :linked_files, %w{config/database.yml}

/shared/config/database.ymlから/deploy先のバス/current/config/database.ymlにシンボリックリンクを貼ってくれる!2まではrunとかで普通にコマンドを書いていたので、これは楽です。やっぱりこういうファイルってshared配下に置く人が多いんだろうか。linked_dirsもlinked_filesと同じ感じです。

デプロイスクリプトのサンプル

とりあえずRailsアプリをデプロイするスクリプトを書いてみた。ただファイルを置くだけなんで、Webサーバの再起動とかプロセスのリスタートとかは書いてません。

Gemfile

  gem 'capistrano', '3.0.0', github: 'capistrano/capistrano'                                                                                                                                                           
  gem 'capistrano-rails'                                                                                                                                                                                               
  gem 'capistrano-rbenv', github: 'capistrano/rbenv'                                                                                                                                                                   
  gem 'capistrano-bundler', github: 'capistrano/bundler' 

Capfiile

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/console'
require 'capistrano/rbenv'
set :rbenv_type, :user
set :rbenv_ruby, '2.0.0-p247'
require 'capistrano/bundler'
require 'capistrano/rails'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }

config/deploy.rb

set :application, :sample
set :repo_url, 'git://hogehogehoge.com/hoge.git'
ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }

set :deploy_to, '/deploy/to'
set :scm, :git

set :pty, true

set :linked_files, %w{config/database.yml config/resque.yml}
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

set :keep_releases, 10

config/deploy/production.rb

set :stage, :production

# Simple Role Syntax
# ==================
# Supports bulk-adding hosts to roles, the primary
# server in each group is considered to be the first
# unless any hosts have the primary property set.
role :app, %w{app@hoge1 app@hoge2}
role :web, %w{app@hoge1 app@hoge2}
role :db,  %w{app@hoge1}, primary: true

# you can set custom ssh options
# it's possible to pass any option but you need to keep in mind that net/ssh understand limited list of options
# you can see them in [net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start)
# set it globally
set :ssh_options, {
  keys: %w(/home/app/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}
# and/or per server
# server 'example.com',
#   user: 'user_name',
#   roles: %w{web app},
#   ssh_options: {
#     user: 'user_name', # overrides user setting above
#     keys: %w(/home/user_name/.ssh/id_rsa),
#     forward_agent: false,
#     auth_methods: %w(publickey password)
#     # password: 'please use keys'
#   }
# setting per server overrides global ssh_options

# fetch(:default_env).merge!(:rails_env, :production)

サーバの再起動とか全くやってないのであまり参考にはならない。。
ま、ドキュメント読んだりちょっといじってみた結果、実行速度を理由にminaを使うならCapistrano3を使った方がいいかなと感じました。十分早いし、ちょと複雑な事をminaでやろうとすると時間がかかったりするんで。

追記

いいまとめエントリーきました!
Capistrano 3への手引き - 今日のごはんは素麺です http://takkkun.hatenablog.com/entry/2013/10/12/Capistrano_3%E3%81%B8%E3%81%AE%E6%89%8B%E5%BC%95%E3%81%8D

Rails4にアップデートした

Rails4にアップデートする情報は十分にあるので、あえて書くことは無いんですが、今やってるプロジェクトでRails3.2.14からRails4に上げた時のメモ。

基本的にRails3のうちにstrong_parametersさえ対応しておけば大したことはないというのが感想。
問題は使っているgemがRails4に対応しているかどうかですね。
ほぼ全てのgemが対応済みだったり、対策方法があってスムーズに移行が進んだのですが、ちょっとはまったのがglobalize3でした。rails4というブランチがあるので、それをGemfileで指定してbundle installしたら以下のエラーが発生。

Bundler could not find compatible versions for gem "activerecord":
  In Gemfile:
    globalize3 (>= 0) ruby depends on
      activerecord (~> 3.0) ruby

    rails (= 4.0.0) ruby depends on
      activerecord (4.0.0)

Bundler could not find compatible versions for gem "rails":
  In Gemfile:
    globalize3 (>= 0) ruby depends on
      rails (~> 3) ruby

    rails (4.0.0)

調べてみたらpaper_trailとの依存関係が問題で、paper_trailを使っていなくても同じくrails4ブランチを指定してGemfileに追記すればおkでした。
Rubyも2.0になってるし、とりあえずは安心。

SUMMER SONIC 2013

サマソニ2日目だけ行きました。

http://instagram.com/p/c4XiQZEKV9/
今年のリストバンドはリボンみたいでかわいかった

ももいろクローバーZ

MountainStageに入った瞬間入場規制が始まった。午前中に入場規制が入ったアーティストは初めて見た。レッドがジャンプするって事しか知らなかったので、ジャンプ見ようかなあと思ったけど、汗が異常に流れてきたので途中で抜けて物販でタオルとTシャツを購入。
ちょっと見たらもういいやって抜ける人も結構いたけど、入場規制で順番待ちしている人の行列を見たら、人気は半端ないなあと思いました。

未定(初回限定盤)

未定(初回限定盤)

CAPITAL CITIES

ポップです。ダンスミュージックかつポップ。休日の昼間に部屋に流したい感じでした。

Capital Cities Ep [Analog]

Capital Cities Ep [Analog]

THE ROYAL CONCEPT

スウェーデン出身のポップなマンドゥ・ディアオかなと思ってたら、想像以上にポップでした。なんていうか次のアルバムを聞きたい感じですね。まだ伸びしろがあるというか。次はもっとロックな感じでアルバム作って欲しいかな。

The Royal Concept

The Royal Concept

CYNDI LAUPER

ハイスタンダードがインディーズ時代にカバーしていたので、原曲をライブで聞きたくて見ました。最初から目当てのMoney Changes Everythingをやってくれたので満足。後から知ったけど、還暦みたいですね。それであの歌唱力はすげえ。

トゥルー・カラーズ

トゥルー・カラーズ

THE VENTURES

ビーチステージとベンチャーズって最強の組み合わせだと思った。ユーミンのカバーを聞きながらビールとソーセージは最高でした。毎年来て欲しい。

ベンチャーズ・プレイ・ユーミン

ベンチャーズ・プレイ・ユーミン

MUSE

SUMMER SONIC初参戦の時はヘッドライナーだったぽいですね。出世したなあ。ライブの内容はトリを務めるには十分のクオリティで、スタンドまで立ち見の満員だった。最後の花火凄かったですね。100発ぐらい打ち上げてたかも。

The 2nd Law

The 2nd Law



今年はフラっと見に行った感じだったので、そこまで疲れなかった。しかしSUMMER SONICは毎年色々な面がブラッシュアップされていて良い。帰りの電車もここ数年で増やされたし、かなりオススメのフェスになったと思う。

Git勉強会@Krayに行ってきた

KrayさんのGit勉強会に行って来ました。以下感想。

Gitはなぜ難しいのか(irohiroki)

コマンドを実行した時に.git/ 配下で何が起こっているのか?という部分にフォーカスを当てていた部分が印象に残りました。初心者向けという前提っぽかったので、最初にそれ説明しちゃうの・・・?と思っていたけど、前半で.git/配下の動きを説明しておいた方が理解しやすくなるなと感じました。これは新発見。(この時、メタプログラミングRubyを連想してしまった。タイトルに「メタプログラミング」って入っているので勘違いしていたのだけど、他言語経験者にとってはいい入門書として読める良書です。)

Gitをなんとなく使ってる人に丁度いい感じの内容でした。

まとめ: pullは恐ろしいコマンド

Gitがどう動いているかについて(f96q)

Gitのソースコードの解説。初音ミクのカツラが気になってあまり記憶に残っていない。

まとめ: GitはCで実装されてるけど、構造体使ってOOPな感じに作ってるよ!おもしろーい!リーナス!リーナス!

次回予告

次もあるらしいのでダニーさんに脅されて行くと思います。

HerokuでBambooStackからCedarStackに移行する

Herokuでずっと更新していないアプリをruby2.0に上げようとしたのだが、Gemfileに「ruby "2.0"」と書いてもpushするとbundle installでエラーが出る。調べてみると過去にHeorkuで作ったアプリはBambooStack上で動いていて、使用出来るRubyのバージョンが古いなどなどが原因だった。
なので、Ruby2.0を使えるCedarStackに移行しようと思ったのだがBambooStackからCedarStackへの移行ツールなどは用意されておらず、CedarStackにするには新しくアプリを作るしかなかった。旧アプリ→新アプリへのデータ移行作業をすればいいかと考えたのだけど、BambooStackで作ったアプリのURLは「http://appname.heroku.com/」だったものがCedarStackだと「http://appname.herokuapp.com/」で作られるらしい。独自ドメインを設定していないので、URLが変わってしまう。これでは移行できない・・・と思ったが、アプリ名さえ同じであればheroku.comもherokuapp.comと同じIPを指してくれるので問題は無し。

というわけでまずDBのバックアップ。

bundle exec heroku db:pull sqlite://db/production.sqlite3 --app appname

ここで自分の環境ではエラーが出た。このエントリーを書いている時点ではRuby1.9.3以降でsqlite形式にデータを変換する時にバグがあるらしく、Ruby1.9.2以前のバージンでエクスポートしなくてはならない。(Macで開発している場合は「brew install apple-gcc42」でRuby1.9.2をコンパイルできるようになります。)

そして現在使っているHerokuドメイン"appname"をブラウザから別名にリネーム*1し、今まで使っていた"appname"でアプリを再作成します。

heroku create appname --stack cedar # アプリ削除からこのコマンド実行までに"appname"を取られたら作れなくなるので急いで作る

そしてバックアップしたDBを新アプリにインポート。

bundle exec appname db:push sqlite://db/production.sqlite3 --app appname

これでアクセスしてみて動作確認がとれれば旧アプリは削除してしまってOK。

ちょっと面倒ですね。ブラウザからクリックひとつでStackを移行できるツールがあったらいいのに。

*1:コマンドでもできます