Ruby 2.1 / Rails4.1 での意外に忘れらている便利なメソッドを集めました。
RubyやRailsでは有用なメソッドがたくさん実装されており、車輪の再発明にならないように、それらを覚えて適切な箇所で使うだけで生産性はあがります。
(大雑把に書いたので、まだまとまってないので、随時修正します)
動作確認
- Ruby 2.1.2
- Rails 4.1
- ActiveSupport 4.1.7
目次
代入、範囲、正規表現、%記法
文字列からクラスを作成 constantize
動的にメソッドを定義 class_eval
nilや空を判定する(present?, blank?)、nilや空以外の値を取得(present)、nilを扱う(try)
複数行の文字列(ヒアドキュメント)、文字列を含む(include?, index)、パターンにマッチする(match, =~)
パターンで置換する(gsub)、文字列で分割し配列にする(split)
1行ずつ取り出す(each_line)、1文字ずつ取り出す(each_char)、部分文字列を取り出す(slice)
文字列を削除(delete)、空白を除く(strip)、改行文字除く(chomp)
長さ(length, size), 空かどうか(empty?), 値が含まれるか(include?)
繰り返し(each, each_with_index, reverse_each)
連結(+)、追加(<<)
1要素の検索(find), 複数要素の検索(select, reject), 最大値、最小値(max, min)、各要素を処理(collect, map)
すべて◯◯か(all?), どれか◯◯か(any?)
各要素を結合(join)、合計を計算(sum)
ソート(sort)、重複除去(uniq)、ランダムに要素を取得(sample)
その他のArrayクラスのメソッド
要素の取り出しと設定(, =)、キーを配列で取得(keys)、値を配列で取得(values)、値の数(length)
空かどうか(empty?)、キーが含まれるか(has_key?)、値が含まれるか(has_value?)
繰り返し(each)、値の取得(select, collect, map)
ハッシュを統合(merge)、差を取得する(diff)
その他のHashクラスのメソッド
指定した要素を配列で取得(pluck)
変更を確認する(changes, changed?)
カラム値の存在有無を確認(カラム名+?)
DBに保存されていないか(new_record?)、DBに保存されているか(persisted?)
DBに存在しない場合作成(find_or_create_by)
コントローラーのメソッドをビューで使う(helper_method)
Rubyの基本
- 代入
# 多重代入 a, b = 1, 2 a # => 1 b # => 2 a, b = [1, 2, 3] a # => 1 b # => 2 a, *b = [1, 2, 3] a # => 1 b # => [2, 3] a, b, c = [1, 2] a # => 1 b # => 2 c # => nil # 入れ替え a, b = b, a # 自己代入 a += 1 # a = a + 1 b ||= 2 # b = b || 2
- 範囲
>ruby|
(1..5).include?(5) # => true (5も含む)
(1...5).include?(5) # => false (5は含まない)
- %記法
%w(Alice Bob Chrlie) # => ["Alice", "Bob", "Chrlie"]
Rubyの基本
- 文字列からクラスを作成(constantize)
"User".constantize #=> Userクラスが作成される "User".constantize.new #=> #<User id: nil, name: nil, created_at: nil, updated_at: nil> # 応用: URLから動的にモデルのレコードを読み出す # # URL例(request.path): /events/:id/comments, /articles/:id/comments resource, id = request.path.split('/')[1, 2] # resource = events か articles @commentable = resource.singularize.classify.constantize.find(id)
メタプログラミング
- メソッドを動的に定義する(class_eval, module_eval)
# Cクラスを定義 class C; end # class_evalメソッドで、Cクラスにmメソッドを定義 C.class_eval %Q{ def m puts "hello" end } # mメソッドを実行 C.new.m # => hello
Object
nilや空文字など空でないか確認(present?)'', ' ', nil, [], {}など空っぽいもの以外の場合、trueを返す
['', ' ', "", " ", nil, [], {}].each { |a| p a.present? } # => false # ... (全て false)
nilや空文字など空であるか確認(blank?)
'', ' ', "", " ", nil, [], {}などの空ぽいものの場合、trueを返す
['', ' ', nil, [], {}].each { |a| p a.blank? } # => true # ... (全て true)
nilや空文字など空でない値を取得(present)
'', ' ', nil, [], {}など空っぽいもの以外の場合、値を返す。
'a'.presence #=> "a" ''.presence #=> nil
tryメソッド(nilの場合、nilを返す)
@person ? @pserson.name : nil # をtryで次のように書き換えれる @poerson.try(:name) # 引数やブロックも渡せる "abc".try(:includes?, "a")
String
複数行に渡る文字列(ヒアドキュメント)str = <<TEXT 1行目 2行目 3行目 TEXT str #=> "1行目\n2行目\n3行目\n"
指定した文字列を含んでいるか(include?)
"abcde".include?("bc") #=> true "abcde".include?("zy") #=> false
指定した文字列を含んでいれば、その開始位置を整数で返す(index)
"abcdefg".index("cd") # => 2 "abcdefg".index("zy") # => nil
パターンにマッチする(=~, match)
html = "<p></p>" html.match(/<(\w)>/) do |m| p match[0] p match[1] end # => "<p>" # => "p"
パターンで置換(gsub)
html = "hello,\nworld\r\n" # 改行コードを<br />に置き換える html.gsub(/(\r\n|\r|\n)/, "<br />") # => "hello,<br />world<br />"
指定した文字列で分割し、配列にする(split)
"Alice,Bob,Charlie".split(",") # => ["Alice", "Bob", "Charlie"]
1行ずつ取り出す(each_line)
"1行目\n2行目\n3行目\n".each_line { |line| p line } # => "1行目\n" # => "2行目\n" # => "3行目\n"
1文字ずつ取り出す(each_char)
"abcde".each_char { |ch| p ch } # => "a" # => "b" # ...
部分文字列を取り出す(slice)
"abcdefg".slice(1, 3) # 2番目から3文字分 # => "bcd" "abcdefg".slice(1..3) # 1文字目から3文字目まで # => "bcd" "abcdefg".slice(/\w+/) # 正規表現 # => "abcdefg"
指定した文字列を削除(delete)
"abcde".delete("cd") # => "abe" "abcde".delete("xy") # => "abcde"
先頭と末尾の空白を取り除く(strip)
" hi \t “.strip # => “hi”
文字列の末尾の改行文字を除く(chomp)
"hi\r\n".chomp # => “hi” "hi\n".chomp # => "hi" "hi\r".chomp # => "hi" "1行目\n2行目\n3行目\n".chomp #=> "1行目\n2行目\n3行目"
Array
長さ(length, size)[].length #=> 0 [1, 2, 3].length #=> 3 [1, 2, 3].size #=> 3
空かどうか(empty?)
[].empty? #=> true # Railsの場合、ActiveSupportで拡張されたblank?の方が都合が良い場合が多い [].blank? #=> true
値が含まれるか(include?)
[1, 2, 3].include?(1) #=> true [1, 2, 3].include?(10) #=> false
繰り返し(each, each_with_index, reverse_each)
[1, 2, 3].each { |a| p a } # => 1 # => 2 # => 3 [1, 2, 3].each_with_index { |a, i| p "#{i}番目:#{a}" } # => "0番目:1" # => "1番目:2" # => "2番目:3" [1, 2, 3].reverse_each { |a| p a } # => 3 # => 2 # => 1
配列の連結(+)
[1, 2, 3] + [4, 5, 6] #=> [1, 2, 3, 4, 5, 6] [1, 2, 3] + [] #=> [1, 2, 3]
要素の追加(<<)
[1, 2, 3] << 4 #=> [1, 2, 3, 4] [1, 2, 3] << [4, 5] #=> [1, 2, 3, [4, 5]]
1要素の検索(find)、複数要素の検索(select, reject),
[1, 2, 3, 4, 5, 6].find { |n| n % 3 == 0 } # => 3 [1, 2, 3, 4, 5, 6].find { |n| n % 3 == 100 } # => nil (条件に当てはまらないとnilを返す) # selectはブロック内の条件に当てはまる要素を返す [1, 2, 3, 4, 5, 6].select { |n| n % 3 == 0 } => [3, 6] [1, 2, 3, 4, 5, 6].select { |n| n % 3 == 100 } => [] (条件に当てはまらないと[]を返す) # rejectはブロック内の条件に当てはまらない要素を返す [1, 2, 3, 4, 5, 6].reject { |n| n % 3 == 0 } => [1, 2, 4, 5]
最大値、最小値(max, min)
[2, 3, 1, 10, 8].min #=> 1 [2, 3, 1, 10, 8].max #=> 10
各要素を処理する(collect, map)
collectもmapも別名で同じ処理をする。ブロックに与えた処理を行ったあとに、それらを配列で返す
['ruby', 'rails'].map { |str| str.capitalize } # => ["Ruby", "Rails"] # 別の書き方もできる ['ruby', 'rails'].map(&:capitalize) # => ["Ruby", "Rails"]
すべて◯◯か(all?), どれか◯◯か(any?)
[true, true, true].all? # => true [true, true, false].all? # => false [true, true, false].any? # => true [false, false, false].any? # => false [true, false, false].one? # => true [true, true, false].one? # => false [false, false, false].none? # => true # ブロックを渡して条件判定ができる ["abc tomato", "xyz tomato", "123 tomato"].all? { |a| a.include?("tomato") } # => true
各要素を指定した文字列で結合して文字列で返す(join)
["ABC", "DEF", "GHI"].join(",") => "ABC,DEF,GHI" [].join(",") => ""
合計を計算(sum)
[1, 2, 3].sum # => 6 ["abc", "def", "ghi"].sum #=> "abcdefghi"
ソート(sort)
[8, 3, 2, 9, 1].sort #=> [1, 2, 3, 8, 9]
重複する要素を除去する(uniq)
[3, 1, 2, 3, 1, 4, 2, 1].uniq # => [3, 1, 2, 4]
ランダムに要素を取得する(sample)
[1, 2, 3, 4, 5].sample # => 5 [1, 2, 3, 4, 5].sample # => 2
Hash
要素の取り出しと設定(, =)hash = { "1" => { "username" => "taro", "age" => 20 }, "2" => { "username" => "chika", "age" => 18 } } # 要素の取り出し hash["1"] # => {"username"=>"taro", "age"=>20 } # 要素の設定 hash["3"] = { "username" => "tom", "age" => 25 } hash # => {"1"=>{"username"=>"taro", "age"=>20}, "2"=>{"username"=>"chika", "age"=>18}, "3"=>{"username"=>"tom", "age"=>25}}
キーを配列で取得(keys)、値を配列で取得(values)、値の数(length)
hash = { "1" => { "username" => "taro", "age" => 20 }, "2" => { "username" => "chika", "age" => 18 } } hash.keys # => ["1", "2"] hash.values # => [{"username"=>"taro", "age"=>20}, {"username"=>"chika", "age"=>18}] hash.length # => 2
空かどうか(empty?)、キーが含まれるか(has_key?)、値が含まれるか(has_value?)
hash = { name: :tom } hash.empty? # => false hash.has_key?(:name) # => true hash.has_value?(:tom) # => true hash = {} hash.empty? # => true hash.has_key?(:name) # => false hash.has_value?(:tom) # => false
繰り返し(each)
hash = { "1" => { "username" => "taro", "age" => 20 }, "2" => { "username" => "chika", "age" => 18 } } hash.each { |k, v| p "#{k}:#{v}" } # => "1:{\"username\"=>\"taro\", \"age\"=>20}" # => "2:{\"username\"=>\"chika\", \"age\"=>18}"
値の取得(select, collect, map)
- select ... ブロックの条件に合致するハッシュを返す
- collect(map) ... 各要素に対して、処理を行った結果を配列で返す
hash = { "1" => { "username" => "taro", "age" => 20 }, "2" => { "username" => "chika", "age" => 18 } } # ageが20以上のハッシュを取得する hash.select { |_, v| v["age"].to_i >= 20 } # => {"1"=>{"username"=>"taro", "age"=>20}} # ageを配列で取得する hash.collect { |_, v| v["age"].to_i } # => [20, 18] # mapとcollectのエイリアスであるため挙動は同じ hash.map { |_, v| v["age"].to_i } # => [20, 18]
ハッシュを統合(merge)
h1 = { price: 100 } h2 = { published: false } h1.merge(h2) # => {:price=>100, :published=>false} # 同じ値の場合は、後のハッシュが優先になる h1 = { published: true } h2 = { published: false } h1.merge(h2) # => {:published=>false}
ActiveRecord
- 指定した要素を配列で取得(pluck)
User.all.pluck(:id, :name) #(0.2ms) SELECT "users"."id", "users"."name" FROM "users" # => [[1, nil], [2, "田中"]]
- 変更を確認する(changes, changed?)
u = User.first # 変更された要素を取得する u.changes # => {} # すべての要素が変更されたかbooleanで取得 u.changed? # => false # nameカラムが変更されたかbooleanで取得 u.name_changed? #=> false u.name = "test" u.changes #=> {"name"=>[nil, "test"]} u.changed? #=> true u.name_changed? #=> true
- カラムの存在有無を確認する(カラム名+?)
boolean値のカラム
u = User.first # nil, "", {}, [], falseなどの場合、falseを返す u.name = nil u.name? # => false u.name = "" u.name? # => false u.name = {} u.name? # => false u.name = [] u.name? # => false u.name = false u.name? # => false u.name = "test" u.name? # => true
- DBに保存されていないか(new_record?)、DBに保存されているか(persisted?)
# DBに保存されていない値 u = User.new u.new_record? # => true u.persisted? # => false # DBに保存されている値 u = User.first u.new_record? #=> false u.persisted? #=> true
- DBに存在しない場合作成(find_or_create_by)
# DBに存在する場合、レコードを取得 User.find_or_create_by(name: "test") # => #<User id: 1, name: "test", created_at: "2014-11-29 09:02:15", updated_at: "2014-11-30 10:49:04", active: nil> # DBに存在しない場合、レコードを作成 User.find_or_create_by(name: "test3") do |u| # 引数で指定した値は自動的に設定される # ブロック内でその他の値を設定する u.active = false end # => #<User id: 4, name: "test3", created_at: "2014-11-30 10:51:45", updated_at: "2014-11-30 10:51:45", active: false>
コントローラー
- コントローラーのメソッドをビューで使う(helper_method)
# Controller # ビューでcurrent_userメソッドが利用できる helper_method :current_user private def current_user @_current_user ||= User.find(session[:user_id]) end end # View ユーザー名: <%= current_user.name %>
参考文献
- Rails3 レシピブック 190の技
- Ruby on Rails API
- Rubyリファレンス