はじめに
- このメソッドが存在したら
- この変数が配列で何か入っていたら
- Item.firstで何か見つかったら
というのは結構出てくるパターンで、簡潔に書ける場合も多いのでまとめました。
使うとコードが分かりにくくなる場合もあるので、容量用法を守って使いましょう。
基本の確認
ローカル変数が定義されているか
前提
定義されていないローカル変数は使おうとすると例外を起こす
hoge #=> NameError: undefined local variable or method `hoge' for main:Object
defined?
定義されているか確認できる
defined? hoge #=> false
オブジェクトのメソッド存在チェック
前提
存在しないメソッドを呼び出すと例外
hoge = nil
hoge.fuga #=> NoMethodError: undefined method `fuga' for nil:NilClass
respond_to?
メソッドが存在しているか確認できる
hoge.respond_to? :fuga #=> false
事例集
ここからRails(Active Support)の導入を前提とします。
便利なメソッド
present?
偽でなくsizeが0でなければtrue
Stringクラスは/\A[[:space:]]*\z/へのマッチもfalseになる
nil.present? #=> false
[Item.first].present? #=> true
compact
nilの要素を消してくれる
[nil].present? #=> true
[nil].compact.present? #=> false
presence
#present?
を実行しtrueだったらselfを返す
<% if items = current_user.items.order(id: :asc).limit(5).presence %>
<h2>アイテム一覧</h2>
<ul>
<% items.each do |item| %>
<li><%= item.name %></li>
<% end %>
</ul>
<% end %>
tap
tapはブロック内でselfが使え、selfが返ります。ただ、breakするとその値が返ります。あんまりいい例が思いつかなかった…。
items = [{ id: 1, title: 'item1' }, { id: 2 }]
items.map{|item| "タイトルは" + item.tap{|i| break { title: 'no title' } unless i[:title]}[:title] }
try
メソッドが無ければnilを返す
titles = ['1', nil, '123']
title_lengths = titles.map{|title| title.try(:length) }
#=> [1, nil, 3]
便利な演算子 (Ruby)
and
左辺が真だと右辺が評価される(演算子の優先順位低)
<% if item = current_user.items.first and item.prirority == 100 %>
<h2><%= item.name %></h2>
<% end %>
&&
左辺が真だと右辺が評価される(演算子の優先順位高)
<% if item.status == :open or item.user == current_user && item.status != :delete %>
<h2><%= item.name %></h2>
<% end %>
||
左辺が偽だと右辺が評価される(演算子の優先順位高)
next_item = Item.order(id: :asc).where("items.id > ?", @item.id).first || Item.first
&. (Ruby 2.3↑), try! (Active Support)
オブジェクトがnilでない場合は呼び出し、nilの時はnilを返す
hoge = nil
hoge&.fuga #=> nil
hoge.try!(:fuga) #=> nil
こんなことも可能
Item.first&.user&.themes&.first
||=
定義されていなかったら、デフォルト値を代入
<%= partial 'item' %>
<%= partial 'item', locals: { hidden_header: true } %>
||=を使って未定義時にfalseを入れておくことで、oldからの呼び出しにも耐えられる。
<% hidden_header ||= false %>
<% unless hidden_header %>
hidden_header is true
<% end %>
ただし変数に偽のものが入っていると考えられる時は、使えない
hoge = nil
hoge ||= "test"
hoge #=> "test"
? :
三項演算子は演算子の優先順位的に連続で使える
a ? 'a is true' : b ? 'b is true' : c ? 'c is true' : 'all false'
caseやelsifを使った方が良いこともままある
後置if
erbでも便利
<div class="<%= :tab_on if action_name == 'index' %>">
後置rescue
require 'open-uri'
res = open('http://hoge.huga').read #=> SocketError: Failed to open TCP connection to hoge.huga:80 (getaddrinfo: nodename nor servname provided, or not known)
res = open('http://hoge.huga').read rescue nil #=> nil
おわりに
「これ書かなくても情報量的には充分だよね?」と思った時、ぐぐってみると結構解決策があったりして便利。
世の中一般には汎用的ではないが、自分のところでは役立つ場合は、ヘルパーなどで定義してしまうのも手。
よいRuby & Railsライフを!