ActiveSupport一時調べ
2020/07/11 09:45
<h3>Active Supportとは</h3> <p>Active Supportは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby%20on%20Rails">Ruby on Rails</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>であり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>言語の拡張、ユーティリティ、その他横断的な作業を担っています。</p> <p>Active Supportは言語レベルで基本部分を底上げして豊かなものにし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>アプリケーションの開発と<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby%20on%20Rails">Ruby on Rails</a>それ自体の開発に役立てるべく作られています。 (引用:Active Support コア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%C8%C4%A5%B5%A1%C7%BD">拡張機能</a>) <a href="https://railsguides.jp/active_support_core_extensions.html">https://railsguides.jp/active_support_core_extensions.html</a></p> <p>StringやInteger、Date、Datetimeなどのコアなクラスのメソッドを拡張してくれるもの</p> <hr /> <h3>拡張対象</h3> <p>Active Supportが拡張する対象を ざっと挙げてみました(全部ではないです)</p> <ul> <li>Object</li> <li>Module</li> <li>Class</li> <li>String</li> <li>Numeric <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/BigDecimal">BigDecimal</a></li> <li>Integer</li> </ul> </li> <li>Enumerable</li> <li>Array</li> <li>Hash</li> <li>Range</li> <li>Date</li> <li>Time</li> <li>File</li> <li>NameError</li> <li>LoadError</li> </ul> <p>...</p> <hr /> <h3>全てのオブジェクトで使える拡張</h3> <ul> <li>Object#blank?</li> <li>Object#present?</li> </ul> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/ruby">ruby</a>にあるのは<code>nil?</code>と<code>empty?</code>のみ</p> <pre class="code" data-lang="" data-unlink>nil.nil? => true [].empty? => true ''.empty? => true</pre> <hr /> <ul> <li>Object#blank?</li> </ul> <p><code>nil?</code>と<code>empty?</code>の合わせ技</p> <pre class="code" data-lang="" data-unlink>nil.blank? => true [].blank? => true ''.blank? => true nil.blank? => true</pre> <ul> <li>Object#present?</li> </ul> <p><code>!blank?</code>と同じ意味</p> <pre class="code" data-lang="" data-unlink>nil.present? => false [].present? => false [1,2,3].present? => true</pre> <p>blank?とpresent?の定義は下のURL先 <a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/blank.rb">https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/blank.rb</a></p> <hr /> <ul> <li>Object#try</li> </ul> <p>おなじみtryメソッド。 レシーバが<a class="keyword" href="http://d.hatena.ne.jp/keyword/nil">nil</a>、もしくはメソッドが定義されていない時には<a class="keyword" href="http://d.hatena.ne.jp/keyword/nil">nil</a>を返す。<a class="keyword" href="http://d.hatena.ne.jp/keyword/nil">nil</a>でないなら引数で渡されたメソッドを実行。</p> <pre class="code" data-lang="" data-unlink>"test".try(:name) => nil "test".try(:size) => 4</pre> <ul> <li>Object#try!</li> </ul> <p><code>try</code>と違う点は、レシーバが<a class="keyword" href="http://d.hatena.ne.jp/keyword/nil">nil</a>なら<code>nil</code>でなく例外を返すところ。</p> <pre class="code" data-lang="" data-unlink>"test".try!(:name) NoMethodError: undefined method `name' for "test":String # NoMethodErrorクラスの例外が発生</pre> <hr /> <ul> <li>Object#with_options</li> </ul> <p>オプションを一つにまとめられる。 <code>validates</code>メソッドのオプションを一つにまとめたりとかもできると思うよ。</p> <pre class="code" data-lang="" data-unlink>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 </pre> <hr /> <ul> <li>tapの補足</li> </ul> <p>ブロックを渡した場合には、<a class="keyword" href="http://d.hatena.ne.jp/keyword/nil">nil</a>でない場合のみブロックの評価を行い、tryの返り値はブロックの評価結果となる。</p> <pre class="code" data-lang="" data-unlink>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]</pre> <hr /> <h3>Classの拡張</h3> <h4>class_attribute</h4> <p>継承可能なクラス属性を定義する。 以下のようなことができるようになる。</p> <ul> <li>子クラスで上書きが可能</li> <li>アクセサメソッドを自動で定義</li> <li>子クラスで定義されていなければ親クラスの値を参照</li> </ul> <p>親クラスの定数を汚染する恐れがない。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>メソッド内からでもアクセスできる。</p> <pre class="code" data-lang="" data-unlink>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 </pre> <hr /> <h3>Stringの拡張</h3> <ul> <li>活用系</li> </ul> <p>レシーバに対して何かしら操作を行う系</p> <pre class="code" data-lang="" data-unlink>"tomato".pluralize => "tomatoes" "katsui".pluralize => "katsuis"</pre> <hr /> <p>活用系の続き</p> <pre class="code" data-lang="" data-unlink>"admin_user".camelize => "AdminUser" "admin_user".camelize(:lower) => "adminUser" "account/financial_account".camelize => "Account::FinancialAccount" # camelizeと反対の操作を行うのがunderscore "Backoffice::UsersController".demodulize => "UsersController" "::UsersController".demodulize => "UsersController"</pre> <hr /> <h3>Numericの拡張</h3> <ul> <li>バイト</li> </ul> <p>全ての数値が以下のメソッドに対応している。</p> <pre class="code" data-lang="" data-unlink>bytes kilobytes megabytes gigabytes terabytes petabytes exabytes</pre> <pre class="code" data-lang="" data-unlink>2.bytes => 2 2.kilobytes => 2048 2.megabytes => 2097152</pre> <hr /> <ul> <li>書式設定</li> </ul> <p>指定した形式で数値をフォーマットできる</p> <pre class="code" data-lang="" data-unlink># 電話番号 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"</pre> <hr /> <h3>Enumerableの拡張</h3> <pre class="code" data-lang="" data-unlink># 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]</pre> <hr /> <h3>Hashの拡張</h3> <pre class="code" data-lang="" data-unlink># 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"} </pre> <hr /> <h3>まとめ</h3> <ul> <li><p>あなたが実装しようとしているメソッドは、既にActive Supportが提供しているかもしれないので、一旦落ち着いて探しましょう</p></li> <li><p>比較的Active Supportのドキュメントは読みやすく、発見も色々とあるので読んでみるといいよ!</p></li> </ul>