まっしろけっけ

めもてきなやーつ

初心者じゃなくても役に立つかもしれないRailsのroutingの記述方をまとめてみた

まとめようと思った経緯


と思って書き始めたのが1月の中頃
何を言っているのかわからねーと思うが
気づいたら違う記事をいくつか書いて公開していた。
(いつのまにかRuby2.2.1もリリースされていた・・・)

環境

Ruby - 2.2.1
Rails - 4.2.0

urlを直接指定する

get '/games',      to: 'games#index'
get '/games/:id',  to: 'games#show'
get '/games/test', to: 'games#test', as: :test

下記のようにも書ける

get '/games'      => 'games#index'
get '/games/:id'  => 'games#show'
get '/games/test' => 'games#test', as: :test

resource

scaffoldを使用した際にroutes.rbに追記される
複数のroutingを作成してくれやつ

resource :users

これだけで下記のようなroutingが生成される(簡単!)

Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
一部のroutingがいらない場合

resourceで作成されるroutingでindexとshowは必要だけどcreateいらないとかのパターンは
下記の用にonly,exceptで絞ることが可能

resource :sports, only: [:index] # indexのみ有効
resource :posts, except: [:index, :show] # index,show以外有効
向き先のcontrollerを変更

下記のようにcontrollerを指定することも可能
この場合「GET /hoge」へのリクエストが「users#index」に

resources :hoge, controller: :users
Prefix Verb URI Pattern Controller#Action
hoge POST /hoge(.:format) users#create
new_hoge GET /hoge/new(.:format) users#new
edit_hoge GET /hoge/edit(.:format) users#edit
GET /hoge(.:format) users#show
PATCH /hoge(.:format) users#update
PUT /hoge(.:format) users#update
DELETE /hoge(.:format) users#destroy
ネストしてみると・・・

resourceをネストしてみると下記のようなroutingが生成される
※結果が長くなってしまうのでonlyで絞っています。

resource :authors, only: [:index, :show, :create] do
  resource :books, only: [:index, :show, :create]
end
Prefix Verb URI Pattern Controller#Action
authors_books POST /authors/books(.:format) books#create
GET /authors/books(.:format) books#show
authors POST /authors(.:format) authors#create
GET /authors(.:format) authors#show
collection
resources :dogs, only: [:index, :show, :create] do
  get 'test1', on: :collection
  get 'test2', on: :collection
end

下記のようにも書ける

resources :dogs, only: [:index, :show, :create] do
  collection do
    get 'test1'
    get 'test2'
  end
end
Prefix Verb URI Pattern Controller#Action
test1_dogs GET /dogs/test1(.:format) dogs#test1
test2_dogs GET /dogs/test2(.:format) dogs#test2
dogs GET /dogs(.:format) dogs#index
POST /dogs(.:format) dogs#create
dog GET /dogs/:id(.:format) dogs#show
member
resources :cats do
  get 'test1', on: :member
  get 'test2', on: :member
end

下記のようにも書ける

resources :cats do
  member do
    get 'test1'
    get 'test2'
  end
end
Prefix Verb URI Pattern Controller#Action
test1_cat GET /cats/:id/test1(.:format) cats#test1
test2_cat GET /cats/:id/test2(.:format) cats#test2
cats GET /cats(.:format) cats#index
POST /cats(.:format) cats#create
new_cat GET /cats/new(.:format) cats#new
edit_cat GET /cats/:id/edit(.:format) cats#edit
cat GET /cats/:id(.:format) cats#show
PATCH /cats/:id(.:format) cats#update
PUT /cats/:id(.:format) cats#update
DELETE /cats/:id(.:format) cats#destroy
new

上記のcollection, memberの他にnewも指定可能

resources :companies, only: [:index, :show, :create] do
  get 'test1', on: :new
  get 'test2', on: :new
end

下記のようにも書ける

resources :companies, only: [:index, :show, :create] do
  new do
    get 'test1'
    get 'test2'
  end
end
Prefix Verb URI Pattern Controller#Action
test1_new_company GET /companies/new/test1(.:format) companies#test1
test2_new_company GET /companies/new/test2(.:format) companies#test2
companies GET /companies(.:format) companies#index
POST /companies(.:format) companies#create
company GET /companies/:id(.:format) companies#show
param
resources :hoge, only: [:index, :show], param: :user_id
Prefix Verb URI Pattern Controller#Action
hoge_index GET /hoge(.:format) hoge#index
hoge GET /hoge/:user_id(.:format) hoge#show

namespace

URI Patternとcontrollerにnamespace Admin::を付ける

namespace :admin do
  resources :users, only: [:index, :show]
end
Prefix Verb URI Pattern Controller#Action
admin_users GET /admin/users(.:format) admin/users#index
admin_user GET /admin/users/:id(.:format) admin/users#show

scope

controllerにのみnamespace Adminを付ける

scope module: 'admin' do
  resources :authors
end

下記のようにも書ける

resources :authors, module: 'admin'
Prefix Verb URI Pattern Controller#Action
authors GET /authors(.:format) admin/authors#index
author GET /authors/:id(.:format) admin/authors#show

concern

同じような記述をgroup化しておけるイメージ(?)
DRYに書けてすっきりする気がする

concern :categorize do
  resources :large_categories, only: [:index, :show] do
    get 'test'
  end
end

resources :dogs, only: [:index], concerns: :categorize
resources :dogs, only: [:index], concerns: :categorize
Prefix Verb URI Pattern Controller#Action
dog_large_category_test GET /dogs/:dog_id/large_categories/:large_category_id/test(.:format) large_categories#test
dog_large_categories GET /dogs/:dog_id/large_categories(.:format) large_categories#index
dog_large_category GET /dogs/:dog_id/large_categories/:id(.:format) large_categories#show
dogs GET /dogs(.:format) dogs#index
cat_large_category_test GET /cats/:cat_id/large_categories/:large_category_id/test(.:format) large_categories#test
cat_large_categories GET /cats/:cat_id/large_categories(.:format) large_categories#index
cat_large_category GET /cats/:cat_id/large_categories/:id(.:format) large_categories#show
cats GET /cats(.:format) cats#index

constraints

routingに色々な条件を付与できる
デフォルトでは{id: /[^\/]+/}このような指定になっているようです。

:idを正規表現でチェック

下記のように記述すると:idが数字の場合のみ対応するactionが呼ばれる

resources :small_categories, constraints: {id: /\d+/}

下記のような記述方法もある

constraints(id: /\d+/) do
  resources :small_categories
end
Prefix Verb URI Pattern Controller#Action
small_categories GET /small_categories(.:format) small_categories#index
POST /small_categories(.:format) small_categories#create
new_small_category GET /small_categories/new(.:format) small_categories#new
edit_small_category GET /small_categories/:id/edit(.:format) small_categories#edit {:id=>/\d+/}
small_category GET /small_categories/:id(.:format) small_categories#show {:id=>/\d+/}
PATCH /small_categories/:id(.:format) small_categories#update {:id=>/\d+/}
PUT /small_categories/:id(.:format) small_categories#update {:id=>/\d+/}
DELETE /small_categories/:id(.:format) small_categories#destroy {:id=>/\d+/}

下記のような場合に便利

resources :small_categories, constraints: {id: /\d+/}, only:[:show]
get '/small_categories/test', to: "small_categories#test"

もしこのようにroutingを書いていた場合に「/small_categories/test」にアクセスすれば「small_categories#test」が呼ばれるが、
constraintsがないと「params[:id]に"test"」が格納されている状態で「small_categories#show」が呼ばれる。

記述する順番を逆にすれば回避出来るが毎回順番を気にしなければいけないのでつらめだと個人的には思う。
また、controller側で:idの値を使用する際もどのようなデータが渡ってくるのかある程度予想出来るので
考えることが減る気がする。

subdomainでチェック

下記のように記述するとアクセスしたsubdomainがadminの場合のみ
該当のmethodが呼ばれるようです。(未確認)

resources :books, only: [:index, :show], constraints: {subdomain: 'admin'}
Prefix Verb URI Pattern Controller#Action
books GET /books(.:format) books#index {:subdomain=>"admin"}
book GET /books/:id(.:format) books#show {:subdomain=>"admin"}
matches?を使用してチェック

constraintsに渡したclassのmatches? methodがtrueを返す場合に
該当のactionが呼ばれる。

例)下記のような処理ではuaがpcの場合に該当のactionが呼ばれる。

class PcConstraint 
  def matches?(request)
    # ua check処理
    # uaがpcの場合にtrueを返す
  end
end
 
Rails.application.routes.draw do
  get '/', to: 'pc#index', constraints: PcConstraint.new
end
lambdaを使用してチェック

下記のようにlambdaでもチェック可能

get '/', to: 'iphone#index', constraints: lambda {|request| request.user_agent =~ /iPhone/}
Prefix Verb URI Pattern Controller#Action
GET / pc#index
GET / iphone#index

defaults

get '/users/:id/default' => 'users#default', defaults: { format: :json}                                                                                                        
get '/posts/:id/default' => 'posts#default', defaults: { default_value: :hoge } 
Prefix Verb URI Pattern Controller#Action
GET /users/:id/default(.:format) users#default {:format=>:json}
GET /posts/:id/default(.:format) posts#default {:default_value=>:hoge}

redirect

get "user"        => redirect("/users")
get "user/:id"    => redirect("/users/%{id}")
get 'user/*other' => redirect { |params| "/users/#{params[:other]}"}
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
user GET /users/:id(.:format) users#show
GET /user(.:format) redirect(301, /users)
GET /user/:id(.:format) redirect(301, /users/%{id})
GET /user/*other(.:format) redirect(301)

まとめ

今回使用したサンプルは下記で公開していますので
routing周りをいじって色々試してみてください。

shiro16/blog-samples · GitHub

この記事書いてる間にRailsGuidesのRouting周りの話の日本語版が公開されているので
特に初心者はそっちを見た方がいいとかいう話があるかもしれない・・・

Kibanaを3から4にしてみた

環境

環境は下記記事参照


fluentd + elasticsearch + kibanaを導入したので手順をメモ - まっしろけっけ

Kibana3でしたがkibana4がリリースされたのでKibanaを4にしてみた。
事前情報としては「白いらしい」「複数のindexのグラフを一つのdashboardに表示できるらしい」程度です。
Kibana4に関しては大谷さんが日本語に訳してくれているので下記を参照するといいかと思います。

Kibana 4(日本語訳) - @johtaniの日記 2nd

Elasticsearchのversion up

Kibana4はElasticsearch 1.4.4以上じゃないとダメなのでversion upします。
ちなみに構築済みの環境では1.3.5のElasticsearchを使ってました。
Elasticsearchのversionの確認はhttp://elasticsearch.host:9200/とかで確認するのが早いかも

yumリポジトリの変更(1.3.xから1.4.x)

$ sudo vi /etc/yum.repos.d/elasticsearch.repo
- [elasticsearch-1.3]
- name=Elasticsearch repository for 1.3.x packages
- baseurl=http://packages.elasticsearch.org/elasticsearch/1.3/centos
+ [elasticsearch-1.4]
+ name=Elasticsearch repository for 1.4.x packages
+ baseurl=http://packages.elasticsearch.org/elasticsearch/1.4/centos

Elasticsearchをupdate

$ sudo /etc/init.d/elasticsearch stop
$ yum update elasticsearch
$ sudo /etc/init.d/elasticsearch start

これでElasticsearchの更新は完了するかと思います。

Kibana4のセットアップ

Kibana4はweb serverとしての機能を持っているのでKibana3で起動していたnginxを止める。

$ sudo /etc/init.d/nginx stop
# nginxがもう必要ないならremove
$ sudo yum remove nginx
# Kibana3もいらないので削除する
$ sudo rm -rf /usr/share/nginx

Kibana4をインストールする

$ wget https://download.elasticsearch.org/kibana/kibana/kibana-4.0.0-linux-x64.tar.gz
$ tar xvzf kibana-4.0.0-linux-x64.tar.gz
$ sudo mv kibana-4.0.0-linux-x64 /etc/kibana # /etcにとりあえず置いてみる

これでとりあえずインストール完了
起動してみる

$ cd /etc/kibana
$ ./bin/kibana 

http://kibana.host:5601/
上記にアクセスすればKibana4にアクセス出来るかと思います。
こんな感じ(し、しろい・・・)
f:id:shiro-16:20150314231843p:plain

起動スクリプトを書いて見る

下記のようなスクリプト書いてみた。
どこかに他にやり方あれば教えてほしい・・・

$ sudo vi /etc/init.d/kibana4
+ #!/bin/bash
+
+ export NAME=kibana
+ export LOG_DIR=/var/log/${NAME}
+ export PID=/var/run/${NAME}.pid
+ export LOG=${LOG_DIR}/${NAME}.log
+
+ test -d $LOG_DIR || mkdir $LOG_DIR
+
+ case $1 in
+   'start' )
+     $0 status >/dev/null 2>&1 && echo "${NAME} is already running." && exit 1
+     nohup /etc/${NAME}/bin/${NAME} 0<&- &> $LOG &
+     echo $! > $PID
+     ;;
+   'stop' )
+     $0 status >/dev/null 2>&1 || echo "${NAME} is not running." || exit 1
+     test -f $PID && cat $PID | xargs kill -s SIGKILL && rm $PID
+     ;;
+   'restart' )
+     $0 stop
+     sleep 1
+     $0 start
+     ;;
+   'status' )
+     test -f $PID || echo "${NAME} not running." || exit 1
+     PID=`cat $PID`
+     kill -s 0 $PID >/dev/null 2>&1 && echo "${NAME} is running." && exit 0
+     echo "${NAME} not running."
+     exit 1
+     ;;
+   *)
+     echo "Usage: $0 start|stop|restart|status"
+     ;;
+ esac

環境変数は各々の環境に合わせて設定してください。

$ sudo chmod 755 /etc/init.d/kibana4
$ sudo /etc/init.d/kibana4 start # 起動
$ sudo /etc/init.d/kibana4 restart # 再起動
$ sudo /etc/init.d/kibana4 stop # 停止

まとめ

これでKibana3からKibana4への変更ができました。
事前情報通り全体的に黒い画面だったのが白くなりました。(個人的に白好きなのでうれしいw)
複数のindexのグラフを一つのdashboardに表示でき見やすい。
他にも色々出来るみたいですがまだ全然使いこなせていない感がやばい・・・

結構簡単にKibana4への移行が完了しました。
実際にはプロビジョニングツール(自分の場合はChef)で管理を行っているので
recipeを変更して反映するだけだったのですが。

Rubyはじめて1年間+αで読んだ書籍や参考にしたサイト等

よくあるタイトルの記事
Rubyを仕事で使いはじめてやっと1年たったのでまとめてみる
書こうと思って数ヶ月経ってしまったので現在は経験1年半くらい

Rubyをお仕事で使う以前のお話は下記参照

PHP書いてたけどRuby書くことになって1年間でやったことてきな - まっしろけっけ

参考にした書籍

最初に読んだ本

タイトル通りRubyは楽しかったです。

RailsによるアジャイルWebアプリケーション開発 第4版

RailsによるアジャイルWebアプリケーション開発 第4版

仕事で使うのがRails3.2だったのでRails3の本を探していて良さそうだったので
こちらを購入
Railsの基本的なことがわかった気がする

ちょっとRubyになれてきて読んだ本

Ruby書き始めて3ヶ月くらいの時に読んだのが下記の本

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

この本はRubyに慣れて来た自分にとっては復習的な意味やそんな書き方出来るのかRubyすげーってなってとても良かった。

入門Chef Solo - Infrastructure as Code

入門Chef Solo - Infrastructure as Code

この本はRubyというよりはRubyで書かれたchefというプロビジョニングツールの本
recipeをRubyで記述出来るのでchefは個人的に楽。
サクッと読めたし、この本の内容はとても役にたっている。

それなりに慣れてきて読んだ本

Ruby初めてもうすぐ10ヶ月くらいで読んだ本
ちょうどその時期に発売されたので即ポチった。

パーフェクトRuby on Rails

パーフェクトRuby on Rails

経験10ヶ月の自分としては復習になる内容と参考になる内容両方がありとても役にたった。
内容的には初心者にはちょっと辛いかもしれないが、ちょっと慣れてきた人であれば為になることが多いので読む価値はあると思う。
便利なgemをいくつも紹介してくれているので、それらを実際に導入してみた。

1年以降に読んだ本

1年以降に読んだのが下記

Ruby徹底攻略 (WEB+DB PRESS plus)

Ruby徹底攻略 (WEB+DB PRESS plus)

復習の様な気持ちで読んだ。

gemのfluentdを使いたかったのでこちらで勉強をして導入した。

直接的なRubyに関する本ではないがRubyを使ってちょっと深い層(プログラミング言語とか)の説明がされています。

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-

Ruby自体がどのように作られているかを解説してくれている一冊。
C言語が解らなくてもいいが解るととても面白いと思います。
個人的にはGC周りの話はとても面白かった。

Effective Ruby

Effective Ruby

中途半端に読んで止まっているが最初からだいぶ面白い

参考にしたサイト

とりあえず日本語のサイトをあげてみる

Ruby

Rubyコーディング規約

Rubyに慣れるまではだいぶお世話になった。

Rubyist Magazine - るびま

Rubyに関しての色々な情報がしれる。
過去のRubyKaigiのレポートもまとまってたりする。(RubyKaigi2014は今のところない)

Ruby Reference Manual
Rubyリファレンス

Rubyのリファレンス系は特にこの2つ

Rails

Railsドキュメント

Railsのドキュメント

Ruby on Rails Guides

最近翻訳のペースが上がっているので今からRails始めるなら日本語版でも不自由無いかも

その他

RubyRailsに限らずに参考にしているサイト

はてなブックマーク

みんな大好きはてブ
とりあえずテクノロジーカテゴリーは常にチェック

shiro16 (@shiro166) | Twitter

みんな知ってるtwitter
とりあえずRubyRailsのコアコミッターの方々やクッ社の方々をfollowして
その人達がシェアする内容をチェックしたり、
RTした人を更にfollowしていけばいいんじゃないかな。kamipoさんとか

Qiita - プログラマの技術情報共有サービス

アカウントは作ってないけど参考になる記事は多いので
見る専門ですが良く利用している。

まとめ

とりあえず思い出しつつ、今手元にある本と参考にしているサイトを記述してみた。
(貸し出してるものもあるので他にも読んだ本はあるかもしれない)
書いてて困ったらググれば情報なんていっぱい出てくるので
そんなに困ること無い気がする。

最近はこんなこと出来ないのかなー?と思ってちょっとググって出てこなかったら
そのgemのソースコード読んだり、Railsソースコード読んだりすることが多い。
Railsのソースとか読んでるとそこから学べることがかなり多い。

fluent-plugin-record-reformerを使ってhostnameを追加してみた

以前下記の記事でFluentd + Elasticsearch + kibanaを導入して
リアルタイムログ解析を行う方法をメモったその後。

実際に導入してみるとhostnameが無いと結局調査を行うには
どのhostに入って詳細な調査をすれば良いか分からないから片っ端から入ってみるみたいな感じになっちゃっていたので
hostnameも追加したいなとずっと思っていてしてなかった。。。


fluentd + elasticsearch + kibanaを導入したので手順をメモ - まっしろけっけ

つい先日hostname追加を行ったのでその内容をメモ
※今回の方法以外にも実は簡単に出来る方法があるかもしれない。

fluent-plugin-record-reformerをinstall

構成は上記の過去記事を参照してください。
今回はfluent-plugin-record-reformerというpluginを使います。
Forwarderにて下記を実行

$ gem install fluent-plugin-record-reformer

fluent.confを編集

# Nginx access logの設定
<source>
  type tail # 
  path /var/log/nginx/access.log # nginxのaccess logのpath
  format nginx # fileのフォーマット
  tag nginx.access.addHostname # tag名
  pos_file /var/log/fluent/nginx.access.pos # position fileを保存しておくpath
</source>

# Rails errorの設定
<source>
  type tail
  path RAILS_ROOT/log/rails.log # Railsのlogのpath指定
  format multiline # 複数行のログを送りたいときに指定
  format_firstline /.*[eE]rror.*/ # Errorもしくはerrorを含む行からformatNの条件を満たすログまでを送信する
  format1 /(?<error>.*[eE]rror.*)\n\n/ # 改行が連続している部分まで送信する
  tag rails.error.addHostname # tag名
  pos_file /var/log/fluent/rails.error.pos # position fileを保存しておくpath
</source>

<match **.addHostname>
  type record_reformer
  renew_record false
  enable_ruby true
  output_tag ${(tag_parts - ["addHostname"]).join(".")}

  <record>
    hostname ${hostname}
  </record>
</match>

<match *.**> # この条件にmatchするtagを下記の条件でごにょごにょする
  type forward
  buffer_type memory # Fluentdの内部バッファリングにmemoryを使用
  buffer_chunk_limit 256m # チャンクのサイズ
  buffer_queue_limit 128 # チャンクキューの長さ
  flush_interval 5s # データフラッシュの間隔
  <server>
    host aggregator_host # aggregatorのIP
    port aggregator_port # aggregatorのfluentの使用portを指定
  </server>
</match>
  • 各tag名に.addHostnameを追加
  • addHostnameが付いているtag用のmatchを追加
  • addHostnameのmatchでhostnameを追加

あとはForwarderのfluentdを再起動させるとhostnameが追加されます

感想

簡単にhostnameの追加が出来ました。
そしてhostnameあるとやはり便利!

gemのwheneverを導入してみた

wheneverとは?

javan/whenever · GitHub
簡単に説明するとRubyのコードでcrontabの管理が出来るライブラリです。

導入以前

whenever導入前はbatchを実行するserverに入って「crontab -e」とかで編集する
crontabメンテナンスおじさんが存在していた。

問題点

  • 設定がcrontabメンテナンスおじさんに依存してしまう
  • crontabメンテナンスおじさんとcrontabを設定しているserverがお亡くなりになった場合、再設定出来ない

解決方法

  1. chefで管理する
  2. ansibleで管理する
  3. どこかにまとめておく
  4. whenever導入

等々があった訳ですが
1,2はほぼ同じでプロビジョニングツールで管理するやり方です。

  • crontab変更する為だけにプロビジョニングツールを走らせるのめんどくさい
  • recipeやplaybook変更したけど、すぐには反映したくないけどcrontabだけ変更したいってなったら面倒

3に関して結局メンテナンスおじさんが発生してしまうのを回避出来ない・・・
wheneverではdeploy時にcrontabを更新することが可能なので問題点を解決出来るはず!ということで導入を決定。
経験上crontabを変更するのはdeployのタイミングがほとんどで緊急で編集したいということもほとんどない

gem install

環境は下記

導入自体は結構簡単

Gemfileに下記を追記でbundle install

+ gem 'whenever', require: false

もしくはgem install

$ gem install whenever

設定をcrontabの更新

$ cd rails_root
$ wheneverize . # もしくはbundle exec wheneverize .

これでconfig/schedule.rbというファイルが作成されます。
コメントアウトにてExampleが書いてあります。

schedule.rbを編集
※サンプルなので仮のrake taskを指定しています

# 出力先logの指定
set :output, 'log/crontab.log'
# 実行環境の指定
set :environment, :production

# 1時間毎に実行
every :hour do
  rake 'rake:task1'
end

# 1時間毎に実行
every 1.hour do
  rake 'rake:task2'
end

# 3分毎に実行(crontabと記述方法を合わせる)
every '*/3 * * * *' do
  rake 'rake:task3'
end

crontabを更新してみる

$ bundle exec whenever --update-crontab
# ここでinvalid byte sequence in US-ASCII (ArgumentError)が出たら
$ export LC_ALL=en_US.UTF-8
$ export LANG=en_US.UTF-8
$ bundle exec whenever --update-crontab
$ crontab -l
# Begin Whenever generated tasks for: rails_root/config/schedule.rb
0 * * * * /bin/bash -l -c 'cd rails_root && RAILS_ENV=production bundle exec rake rake:task1 --silent >> log/crontab.log 2>&1'

0 0,2,4,6,8,10,12,14,16,18,20,22 * * * /bin/bash -l -c 'cd rails_root && RAILS_ENV=production bundle exec rake rake:task2 --silent >> log/crontab.log 2>&1'

*/3 * * * * /bin/bash -l -c 'cd rails_root && RAILS_ENV=production bundle exec rake rake:task3 --silent >> log/crontab.log 2>&1'

# End Whenever generated tasks for: rails_root/config/schedule.rb

以上で簡単な設定と使い方はおしまい。
今回はrake taskを指定していますが、runner,commandといったものの指定も可能です。

deploy時に更新する

次にdeploy時にcrontabを更新したいので
whenever/capistranoの設定を行います。

Capfile,deploy.rb等に下記を追記

+ require 'whenever/capistrano'

その他のオプションを設定
追記場所は各自のcapistranoの設定に合わせて行ってください。

+ set :whenever_roles, :batch # 対象のロールを指定
+ set :whenever_environment, :production # 対象の環境を指定

これで対象のロールをdeployすればcrontabが更新されるはずです。
whenever/capistranoの詳細な設定周りは下記を参照してください。
whenever/lib/whenever/capistrano at master · javan/whenever · GitHub

環境毎にcrontabの設定を変更したり

実際に導入するにあたり環境毎に一部のrake taskを実行したくない環境とかもあったので
下記のようにしています。

schedule.rb

# 事故防止の為RAILS_ENVの指定が無い場合にはdevelopmentを使用する
rails_env = ENV['RAILS_ENV'] || :development

set :environment, rails_env

set :output, 'log/crontab.log'

# 全環境での設定


if rails_env.to_sym == :production
  # production環境のみで設定
end

capistranoの設定

+ set :whenever_roles, :batch
+ set :whenever_environment, :production
+ set :whenever_command, "RAILS_ENV=#{whenever_environment} bundle exec whenever  --update-crontab"
# もしくは
+ set :whenever_command, "RAILS_ENV=#{fetch(:whenever_environment)} bundle exec whenever --update-crontab"

こんな感じに設定しておけば環境毎にcrontabの内容を変更することも可能です。
※サンプルのcapistranoに関しては--dry-runオプションで確認しただけになります

まとめ

wheneverの導入によりcrontabメンテナンスおじさん(基本的に自分)の排除に成功しました。
wheneverでの時間指定の記述方法はいくつかありますが、
自分はcrontabと比較した時に分かりやすいのと○時間毎とか設定した場合に冗長になるのが嫌だったので
crontabと同じ記述方法を採用しています。
その他の細かい設定はwheneverのREADMEに載っていますのでご確認ください。

今回のサンプルは下記で公開してます。
shiro16/blog-samples · GitHub

Ruby2.2.0のインストールがlibffi.a: could not read symbols: Bad valueで失敗した件

Ruby 2.2.0 Released

素晴らしいクリスマスプレゼント!!
twitter見ている感じだと開発者の皆さんは大変だったようでお疲れ様でした+ありがとうございます。
Let's Install

install

Rubyのversion管理はrbenv使ってます。


rbenvでrubyのversion管理をするよ - まっしろけっけ

rubyのversion upをした時の作業内容メモ - まっしろけっけ


詳細は上記の記事を参照してください。
環境はvm上にCnetOS入れてます。

$ rbenv install 2.2.0
Downloading ruby-2.2.0.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/7671e394abfb5d262fbcd3b27a71bf78737c7e9347fa21c39e58b0bb9c4840fc
Installing ruby-2.2.0...

BUILD FAILED (CentOS release 6.5 (Final) using ruby-build 20141222-4-ge455975)

Inspect or clean up the working tree at /tmp/ruby-build.20141225151244.8260
Results logged to /tmp/ruby-build.20141225151244.8260.log

Last 10 log lines:
make[3]: Leaving directory `/tmp/ruby-build.20141225151244.8260/ruby-2.2.0/ext/fiddle/libffi-3.2.1'
linking shared-object fiddle.so
/usr/bin/ld: ./libffi-3.2.1/.libs/libffi.a(raw_api.o): relocation R_X86_64_32 against `.text' can not be used when making a shared object; recompile with -fPIC
./libffi-3.2.1/.libs/libffi.a: could not read symbols: Bad value
collect2: ld returned 1 exit status
make[2]: *** [../../.ext/x86_64-linux/fiddle.so] Error 1
make[2]: Leaving directory `/tmp/ruby-build.20141225151244.8260/ruby-2.2.0/ext/fiddle'
make[1]: *** [ext/fiddle/all] Error 2
make[1]: Leaving directory `/tmp/ruby-build.20141225151244.8260/ruby-2.2.0'
make: *** [build-ext] Error 2

エラー出た・・・

libffi-develを入れる

libffi.a: could not read symbols: Bad valueとかエラー出て困った。
twitterを漁っていると・・・


柴田さんありがとうございますmm

ということでlibffi-develを入れてリトライ

$ sudo yum install libffi-devel
$ rbenv install 2.2.0
Downloading ruby-2.2.0.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/7671e394abfb5d262fbcd3b27a71bf78737c7e9347fa21c39e58b0bb9c4840fc
Installing ruby-2.2.0...
Installed ruby-2.2.0 to /hoge/.rbenv/versions/2.2.0
$ rbenv global 2.2.0
$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]

入ったー

$ gem install bundler # とりあえずbundlerいれるよね

明日は会社の環境でもinstallして動作確認とかしてみよう

初心者は役立つかもしれないRuby(Rails)のmethod名に「?」がつくmethodのまとめ

まとめようと思った経緯

題名の通りRuby(Rails)にはmethod名に「?」がつくmethodが多く存在します。
「空文字の場合はtrue/falseどっち返すんだっけ?」ってなってconsole等で確認することが良くあるので、
(自分が)よく使うものを一覧でまとめておいてみる

環境とか

今回の検証環境は下記
下記のversionにしたのは最新使ってみたかったという理由のみ!

$ ruby -v
ruby 2.2.0preview2
$ rails -v
Rails 4.2.0.rc3

各methodで調べるデータは下記

  • nil
  • true
  • false
  • 文字(空文字と空文字以外)
  • 数値(0と1)
  • 配列(空配列と空配列以外)
  • Hash(空Hashと空Hash以外)

実行するコードは下記

nil?の部分を各methodに変更していく

params = [nil, true, false, "", "hoge", 0, 1, [], [1], {}, {key: 1}]

params.each do |param|
  begin
    result = param.nil?
  rescue => e
    result = e.message
  ensure
    puts "#{param}.nil? => #{result}"
  end
end

Ruby

nil?

nilかどうかを調べるnil?
当たり前かもしれませんがnil以外はfalse

nil.nil?      => true
true.nil?     => false
false.nil?    => false
"".nil?       => false
"hoge".nil?   => false
0.nil?        => false
1.nil?        => false
[].nil?       => false
[1].nil?      => false
{}.nil?       => false
{key: 1}.nil? => false
empty?

空かどうか調べるempty?
空文字、空配列、空hashのみtrueでした。
いくつかのclassでは定義されていないのでundefined methodの例外が発生しました。

nil.empty?      => undefined method `empty?' for nil:NilClass
true.empty?     => undefined method `empty?' for true:TrueClass
false.empty?    => undefined method `empty?' for false:FalseClass
"".empty?       => true
"hoge".empty?   => false
0.empty?        => undefined method `empty?' for 0:Fixnum
1.empty?        => undefined method `empty?' for 1:Fixnum
[].empty?       => true
[1].empty?      => false
{}.empty?       => true
{key: 1}.empty? => false
zero?

zeroかどうかを調べるzero?
Numeric classを継承しているclassにのみ定義されているようです。
最近使っただけでよくはつかわない

nil.zero?      => undefined method `zero?' for nil:NilClass
true.zero?     => undefined method `zero?' for true:TrueClass
false.zero?    => undefined method `zero?' for false:FalseClass
"".zero?       => undefined method `zero?' for "":String
"hoge".zero?   => undefined method `zero?' for "hoge":String
0.zero?        => true
1.zero?        => false
[].zero?       => undefined method `zero?' for []:Array
[1].zero?      => undefined method `zero?' for [1]:Array
{}.zero?       => undefined method `zero?' for {}:Hash
{key: 1}.zero? => undefined method `zero?' for {key: 1}:Hash
nonzero?

zeroじゃないかどうかを調べるnonzero?
zero? methodと同じでNumeric classを継承しているclassにのみ定義されているようです。

?付きmethodですが0の場合はnilを返し0じゃない場合はselfを返します。
最近使っただけでよくはつかわない

nil.nonzero?      => undefined method `nonzero?' for nil:NilClass
true.nonzero?     => undefined method `nonzero?' for true:TrueClass
false.nonzero?    => undefined method `nonzero?' for false:FalseClass
"".nonzero?       => undefined method `nonzero?' for "":String
"hoge".nonzero?   => undefined method `nonzero?' for "hoge":String
0.nonzero?        => nil
1.nonzero?        => 1
[].nonzero?       => undefined method `nonzero?' for []:Array
[1].nonzero?      => undefined method `nonzero?' for [1]:Array
{}.nonzero?       => undefined method `nonzero?' for {}:Hash
{key: 1}.nonzero? => undefined method `nonzero?' for {key: 1}:Hash

Rails

blank?

nil? + empty? のようなmethod
更にfalseでもtrueを返します

nil.blank?      => true
true.blank?     => false
false.blank?    => true
"".blank?       => true
"hoge".blank?   => false
0.blank?        => false
1.blank?        => false
[].blank?       => true
[1].blank?      => false
{}.blank?       => true
{key: 1}.blank? => false
present?

!blank?もしくはblank?.!のようなmethod
if 変数.present?とunless 変数.blank?は同じ意味になります。

nil.present?      => false
true.present?     => true
false.present?    => false
"".present?       => false
"hoge".present?   => true
0.present?        => true
1.present?        => true
[].present?       => false
[1].present?      => true
{}.present?       => false
{key: 1}.present? => true
blank?, present?のおまけ

空文字の際にtrueを返すblank?ですが下記の文字列でもtrueを返します

params = [" ", " ", "\t", "\n", "\r", "\r\n", "\f", "\s"]

" ".blank?    => true
" ".blank?   => true
"\t".blank?   => true
"\n".blank?   => true
"\r".blank?   => true
"\r\n".blank? => true
"\f".blank?   => true
"\s".blank?   => true

present?で同じ文字列を判定

" ".present?    => false
" ".present?   => false
"\t".present?   => false
"\n".present?   => false
"\r".present?   => false
"\r\n".present? => false
"\f".present?   => false
"\s".present?   => false

ちなみにempty?の全てfalseを返します

" ".empty?    => false
" ".empty?   => false
"\t".empty?   => false
"\n".empty?   => false
"\r".empty?   => false
"\r\n".empty? => false
"\f".empty?   => false
"\s".empty?   => false

blank?がこのような挙動になるのはこちらをご覧ください。

まとめ

下記のような結果になったよ。

nil? empty? zero? nonzero? blank? present?
nil true × × × true false
true false × × × false true
false false × × × true false
空文字 false true × × true false
文字列 false false × × false true
0 false × true nil false true
1 false × false self false true
空配列 false true × × true false
配列 false false × × false true
空Hash false true × × true false
Hash false false × × false true

×はundefined method
一部の出力は見やすいように加工してますよ