6
7

More than 1 year has passed since last update.

RubyやRailsで使用頻度は高くないが知っていたら便利そうな機能

Last updated at Posted at 2019-11-12

使用頻度は高くないので忘れちゃいそうだけど、ここぞという時に知っていたら便利そうなメソッドを備忘のためにまとめておきます。
(便利かつ高頻度で使うものは忘れないと思うのでここには書きません)

今後も便利そうだと思うメソッドがあったら随時追加していく予定です。

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のようにブロックにしなくても良い。テストが終わったら解除されるようだ。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7