fujjima’s blog

主に備忘録

Active Support一時調べ

Active Supportとは

Active SupportはRuby on Railsコンポーネントであり、Ruby言語の拡張、ユーティリティ、その他横断的な作業を担っています。

Active Supportは言語レベルで基本部分を底上げして豊かなものにし、Railsアプリケーションの開発とRuby on Railsそれ自体の開発に役立てるべく作られています。 (引用:Active Support コア拡張機能https://railsguides.jp/active_support_core_extensions.html

StringやInteger、Date、Datetimeなどのコアなクラスのメソッドを拡張してくれるもの


拡張対象

Active Supportが拡張する対象を ざっと挙げてみました(全部ではないです)

  • Object
  • Module
  • Class
  • String
  • Numeric
  • Enumerable
  • Array
  • Hash
  • Range
  • Date
  • Time
  • File
  • NameError
  • LoadError

...


全てのオブジェクトで使える拡張

  • Object#blank?
  • Object#present?

rubyにあるのはnil?empty?のみ

nil.nil?
=> true

[].empty?
=> true

''.empty?
=> true

  • Object#blank?

nil?empty?の合わせ技

nil.blank?
=> true

[].blank?
=> true

''.blank?
=> true

nil.blank?
=> true
  • Object#present?

!blank?と同じ意味

nil.present?
=> false

[].present?
=> false

[1,2,3].present?
=> true

blank?とpresent?の定義は下のURL先 https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/blank.rb


  • Object#try

おなじみtryメソッド。 レシーバがnil、もしくはメソッドが定義されていない時にはnilを返す。nilでないなら引数で渡されたメソッドを実行。

"test".try(:name)
=> nil

"test".try(:size)
=> 4
  • Object#try!

tryと違う点は、レシーバがnilならnilでなく例外を返すところ。

"test".try!(:name)
NoMethodError: undefined method `name' for "test":String

# NoMethodErrorクラスの例外が発生

  • Object#with_options

オプションを一つにまとめられる。 validatesメソッドのオプションを一つにまとめたりとかもできると思うよ。

class Company < ApplicationRecord
...
  has_many :contracts, dependent: :destroy
  has_one :billing_address, dependent: :destroy
  has_many :billings, dependent: :destroy
...
end

↓

class Company < ApplicationRecord
...
  with_options dependent: :destroy to |comp|
  comp.has_many :contracts
  comp.has_one :billing_address
  comp.has_many :billings
...
end

  • tapの補足

ブロックを渡した場合には、nilでない場合のみブロックの評価を行い、tryの返り値はブロックの評価結果となる。

array
=> [["test1", 1], ["test2", 2]]

array.try do |a|
  a.to_h
end  
=> {"test1"=>1, "test2"=>2}

array.try do |a|
  a.to_h
end.values  
=> [1, 2]


# Object#yield_selfも同様の挙動
array.yield_self do |a|
  a.to_h  
end.values  
=> [1, 2]

Classの拡張

class_attribute

継承可能なクラス属性を定義する。 以下のようなことができるようになる。

  • 子クラスで上書きが可能
  • アクセサメソッドを自動で定義
  • 子クラスで定義されていなければ親クラスの値を参照

親クラスの定数を汚染する恐れがない。インスタンスメソッド内からでもアクセスできる。

class A
  class_attribute :x
end
 
class B < A; end
 
class C < B; end
 
A.x = :a
B.x # => :a
C.x # => :a
 
B.x = :b
A.x # => :a
C.x # => :b
 
C.x = :c
A.x # => :a
B.x # => :b

# インスタンスからもアクセス・書き換え可能
# インスタンスからクラスのclass_attributeを書き換えることはない
test = B.new
test.x = :t

test.x # => :t
B.x = :b

Stringの拡張

  • 活用系

レシーバに対して何かしら操作を行う系

"tomato".pluralize
=> "tomatoes"

"katsui".pluralize
=> "katsuis"

活用系の続き

"admin_user".camelize
=> "AdminUser"

"admin_user".camelize(:lower)
=> "adminUser"

"account/financial_account".camelize
=> "Account::FinancialAccount"

# camelizeと反対の操作を行うのがunderscore


"Backoffice::UsersController".demodulize
=> "UsersController"

"::UsersController".demodulize
=> "UsersController"

Numericの拡張

  • バイト

全ての数値が以下のメソッドに対応している。

bytes
kilobytes
megabytes
gigabytes
terabytes
petabytes
exabytes
2.bytes
=> 2

2.kilobytes
=> 2048

2.megabytes
=> 2097152

  • 書式設定

指定した形式で数値をフォーマットできる

# 電話番号

1234567.to_s(:phone)
=> "123-4567"

1234567891.to_s(:phone)
=> "123-456-7891"

1234567891.to_s(:phone, area_code: true)
=> "(123) 456-7891"

// 桁区切り

12345678.to_s(:delimited)
=> "12,345,678"

pry(main)> 12345678.to_s(:delimited, delimiter: ".")
=> "12.345.678"

// 丸める
111.2345.to_s(:rounded)
=> "111.235"

111.2345.to_s(:rounded, precision: 2)
=> "111.23"

1112903.to_s(:rounded, precision: 3)
=> "1112903.000"

Enumerableの拡張

# sum
# 要素の合計値を返す
# 数値でない場合は+を使ってsumを計算する
[2,3,4].sum
=> 9

(1..10).sum
=> 55

%w(rb yml haml).sum
=> "rbymlhaml"

# many?
# collection.size > 1 の短縮系

[1,2,3,4].many?
=> true

# many?ではブロックの評価も行える
# ブロックの場合、評価結果のcollection > 1かどうか判定する
[1,2,3,4].many? { |a| a % 2 == 0 }
=> true

[1,2,3,4].many? { |a| a % 3 == 0 }
=> false

# pluck
# 指定したkeyに基づく配列を返す
[ { id: 1 }, { id: 2 }, { id: 3 } ].pluck(:id)
=> [1, 2, 3]

Hashの拡張

# deep_merge
# レシーバと引数に同じkeyが存在し、かつ値がHashの時に下位のHashをmergeする

{a: {b: 1}}.deep_merge({a: {c: 2}})
=> {:a=>{:b=>1, :c=>2}}

# exceptとexcept!
# 引数で指定したkeyがあればレシーバのHashから取り除く
{a: 1, b: 2}.except(:a)
=> {:b=>2}


# stringify_keysとsymbolize_keys
# レシーバのHashのkeyをシンボル→文字列に変換(stringify_keys)
# レシーバのHashのkeyを文字列→シンボルに変換(symbolize_keys)

{nil => nil, 1 => 1, a: :a}.stringify_keys
=> {""=>nil, "1"=>1, "a"=>:a}

{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
=> {nil=>nil, 1=>1, :a=>"a"}

まとめ

  • あなたが実装しようとしているメソッドは、既にActive Supportが提供しているかもしれないので、一旦落ち着いて探しましょう

  • 比較的Active Supportのドキュメントは読みやすく、発見も色々とあるので読んでみるといいよ!