使用頻度は高くないので忘れちゃいそうだけど、ここぞという時に知っていたら便利そうなメソッドを備忘のためにまとめておきます。
(便利かつ高頻度で使うものは忘れないと思うのでここには書きません)
今後も便利そうだと思うメソッドがあったら随時追加していく予定です。
clamp
数値の上限・下限を制限する
523.clamp(0, 100)
# => 100
配列の共通の値を見つける
irb(main):001:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):002:0> b = [2, 4]
=> [2, 4]
irb(main):006:0> a.intersection b
=> [2]
irb(main):007:0> b.intersection a
=> [2]
email_address_with_name
action_mailerのtoに<>や"が含まれていた場合、エスケープしてくれる
# <>や"がnameやemailに入っていてもエスケープしてくれる。
mail(to: email_address_with_name(@user.email, @user.name))
改行コードを置換(\R)
# \Rを使うと\r\n, \r, \n全てを置換対象にしてくれる
"あい\r\nう\rえ\nお".gsub(/\R/, '')
全角スペースも含めてトリム
> ' あうい '.strip
=> " あうい "
> ' あうい '.gsub(/\A[[:space:]]*|[[:space:]]*\Z/, '')
=> "あうい"
squish
スペースやタブ、改行をいい感じに除去してくれる
" \n foo\n\r \t bar \n".squish
# => "foo bar"
キャメルケース、スネークケース
'ham_method'.camelize
=> "HamMethod"
'ham_method'.camelize(:lower)
=> "hamMethod"
'HamMethod'.underscore
=> "ham_method"
文字列からClassに変換
'Admin'.constantize
=> Admin(id: integer, name: string, email: string,created_at: datetime, updated_at: datetime)
tally (>= ruby2.7)
配列の値をグルーピングしてカウントしてくれる
['a', 'b', 'b', 'c', 'b'].tally
# => { 'a'=>1, 'b'=>3, 'c'=>1 }
cover?: 時刻がRange内に入っているか確認する
((Time.now - 1.day)..Time.now).cover?(Time.now - 1.5.day)
=> false
((Time.now - 1.day)..Time.now).cover?(Time.now - 0.5.day)
=> true
対象日の1日〜月末のrange
Time.now.all_month
=> 2022-08-01 00:00:00 +0900..2022-08-31 23:59:59.999999999 +0900
# all_day, all_week, all_year, all_quarterもあり
2つの日付の日数を取得する
(today..tomorrow).count
=> 2
transform_{keys|values}
hashのkey or valueだけ変換する
h = { a: 1, b: 2, c: 3 }
h.transform_keys {|k| k == :a ? :A : k } #=> {:A=>1, :b=>2, :c=>3}
h.transform_values {|v| v * v + 1 } #=> { a: 2, b: 5, c: 10 }
Ruby3.0からtransform_keysにhashが渡せるようになったようです。
h.transform_keys { { |k| "#{k}#{k}" } #=> {"aa"=>1, "bb"=>2, "cc"=>3}
h.transform_keys({ a: :A }) #=> {:A=>1, :b=>2, :c=>3}
ファイルの文字コードを判定
irb(main):006:0> require 'nkf'
=> true
irb(main):007:0> NKF.guess("にほんご")
=> #<Encoding:UTF-8>
irb(main):008:0> NKF.guess("にほんご").name
=> "UTF-8"
irb(main):009:0> NKF.guess(File.read('utf8.txt')).name
=> "UTF-8"
irb(main):010:0> NKF.guess(File.read('sjis.txt')).name
=> "Shift_JIS"
delegate_missing_to
UserオブジェクトにないものをProfileにあるものにすべて委譲したいとしましょう。
delegate_missing_toマクロを使えばこれを簡単に実装できます。
class User < ApplicationRecord
has_one :profile
delegate_missing_to :profile
end
Delayed Job
# 任意のジョブを実行する。削除されないので不要になったら手動で削除すること
Delayed::Job.find(1).invoke_job
# 削除
Delayed::Job.find(1).destroy
# オブジェクト取得
job = Delayed::Job.find(1)
job.payload_object
Enumerable
Enumerableは痒いところに手が届くメソッドがたくさん
https://docs.ruby-lang.org/ja/latest/class/Enumerable.html
ヒアドキュメント
記号忘れがち
https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html#here
Active Support
Railsガイド
https://railsguides.jp/active_support_core_extensions.html
missing, associated(>=Rails 6.1)
関連先が存在しないレコードを取得したいとき
Post.where.missing(:author)
# 下記と同じ意味
Post.left_joins(:author).where(authors: { id: nil })
has_many :users, -> { where.associated(:contact) }
# 下記と同じ意味
has_many :users, -> { joins(:contact).where.not(contact_id: nil) }
ActiveRecord::Relation.none
データが存在しないけどActiveRecord::Relationは返却したいとき
Post.none
SQL構築でSQLインジェクションの警告が出た場合
Arel.sqlを通すと警告を回避できる。
ただし、Arel.sqlで囲むとエスケープされるだけではなく、危険な文字列が混入したらSQLインジェクションになる可能性がある。
下記のように確実に危険な文字列が入らない場合のみ使用する。
Arel.sql('max(`index`)')
SQLのサニタイズメソッド色々
joinsで条件を指定する方法
# sanitize_sqlを使う必要がある
joins(sanitize_sql(['LEFT OUTER JOIN hoges ON hoges.id = fugas.hoge_id AND hoges.status = ?', status])
mergeでorが使える
1つのmergeで生成したorは()でくくられ、1つのグループとして扱われる。
# users.activated = true OR users.confirmed = true
merge(User.activated.or(User.confirmed))
ActiveRecordで更新前の値を取る方法
save前
hoge.will_save_change_to_attribute?
hoge.attribute_in_database
save後
hoge.saved_change_to_attribute?
hoge.attribute_before_last_save
sort, sort_by
# col1の降順, col2の昇順で並び替え
# sortの場合はaとbの順序で制御する
array.sort do |a, b|
(b[:col1] <=> a[:col1]).nonzero? || (a[:col2] <=> b[:col2])
end
# sort_byの場合はマイナスにするといいらしいけど、数値じゃないとできない
# 日付などでもto_iすれば使える
array.sort_by do |a|
[-a[:col1], a[:col2]]
end
ドキュメントにソートが一位にならないときのやり方が書いてあった。
https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/sort_by.html
Enumerable#sort_by は安定ではありません (unstable sort)。ただし、sort_by を以下のように使うと安定なソートを実装できます。
i = 0
ary.sort_by {|v| [v, i += 1] }
in_order_of
配列やActiveRecordで使える
# idが2, 3, 1の順番に並び替えられる
[Post.find(1), Post.find(2), Post.find(3)].in_order_of(:id, [2, 3, 1])
# MySQLだとFIELDが使われる。
> Post.in_order_of(:id, [3, 5, 1])
SELECT "posts".* FROM "posts" ORDER BY FIELD("posts"."id", 1, 5, 3) DESC
spec
mock
時間を止める
travel_to
を使う
travel_to(Time.now.prev_month) do
# 時間が止まる
end
テスト中はずっと止める場合はtravel 1.minute
のようにブロックにしなくても良い。テストが終わったら解除されるようだ。