Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Active Support のコア拡張機能について

479 views

Published on

Active Support のコア拡張機能について

Published in: Technology
  • Be the first to comment

Active Support のコア拡張機能について

  1. 1. Active Support の コア拡張機能について 2018/10/17 大手町.rb #10
  2. 2. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 1自己紹介 Tomoya Kawanishi a.k.a. @cuzic エネチェンジ株式会社 チーフエンジニア 電力会社、ガス会社を切り替えるなら、エネチェンジ経由で! 一般家庭も!法人も! Ruby関西の中の人 発表者として登壇くださる方、あとで声かけください。 第84回 Ruby関西勉強会 12月1日(土) 大手町.rb の中の人 毎月 大手町.rb の開催を予定 東京駅、各線大手町駅から直結! Ruby の初級者がメインターゲット
  3. 3. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Disclaimer 大手町.rb は祝!10回! 人数がとても増えてきました! 大手町.rb は(比較的)初級者向けの勉強会 継続するためには2択! 同じネタをリピート! 初級者向けというポジションを維持 少しずつ難易度を上げていく! いま、来てくれているオーディエンスとともに成長していく! 大手町.rb は今のオーディエンスを大切にし、 ともに成長していく道を選ぶことにしました! 2
  4. 4. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 今日のテーマ Active Support のコア拡張機能について Ruby on Rails を使っていて、 意識しているか、していないかに関わらず みんな使っています とはいえ、時間をとって学ぶことがあまりない というのも事実 今日は一緒に学んでいきましょう 3
  5. 5. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#blank? と Object#present? Ruby で 偽 なものは nil と false 以外のすべて ゼロ、空文字、空配列も true present? を使うと、空文字、空配列は偽になる 4 if 0 "OK" end #=> "OK" if "" "OK" end #=> "OK" if [] "OK" end #=> "OK" if {} "OK" end #=> "OK" if nil "OK" end #=> nil if 0.present? "OK" end #=> "OK" if "".present? "OK" end #=> nil if [].present? "OK" end #=> nil if {}.present? "OK" end #=> nil if nil.present? "OK" end #=> nil
  6. 6. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#presence x.presence は x.present? ? x : nil と同じ意味 x.presence&.split(",")&.last のように &. (ぼっち演算子)と 組合せたり || と組合わせて使うとベンリ 5 if Rails.env.development? email = `git config --get user.email`.chomp email = email.presence || 'example@example.com' name = `git config --get user.name`.chomp name = name.presence || 'Naohiro Sakuma' InternalUser::Admin.find_or_create_by(email: email) do |u| u.name = name end end
  7. 7. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#try 、Object#try! (1/3) nil の場合は何もしない nil でない場合、引数の名前のメソッドを呼び出す 6 # tryメソッドを使用しない例1 unless @number.nil? @number.next end # try メソッドを使用しない例2 !@number.nil? && @number.next @number && @number.next # tryメソッドを使用した場合 @number.try(:next) # &. 演算子を使用した場合 @number&.next
  8. 8. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#try 、Object#try! (2/3) メソッドがない場合、 nil を返す メソッドがない場合、例外にしたいときは try! を使う 7 def log_info(sql, name, ms) # @logger.debug? メソッドが存在して、かつ true を返す場合だけ以下を実行する # @logger.debug? がない場合もエラーにならない if @logger.try(:debug?) name = '%s (%.1fms)' % [name || 'SQL', ms] @logger.debug(format_log_entry(name, sql.squeeze(' '))) end end @number.try(:nest) # => nil @number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
  9. 9. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#try 、Object#try! (3/3) ブロックを渡すこともできる。この場合、 nil でない場合にのみブロックを実行する この場合、try の返り値は ブロックの評価結果 Ruby 2.5 以降の Object#yield_self 的な使い方もできる 8 @person.try { |p| "#{p.first_name} #{p.last_name}" } [["a", 1], ["b", 2]].try do |array| array.to_h end => {"a"=>1, "b"=>2} [["a", 1], ["b", 2]].yield_self do |array| array.to_h end => {"a"=>1, "b"=>2}
  10. 10. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#deep_dup 配列、ハッシュの要素も含めて複製する 配列やハッシュでない場合は、dup と同じ結果を返す 配列やハッシュなら、ディープコピーした結果を返す ハッシュの場合は、キーも値もディープコピーする 9 array = ['str'] duplicate = array.dup duplicate[0].gsub!('str', 'foo') # 要素は複製されていない。 # 一方を変更するとどちらも変更される array # => ['foo'] duplicate # => ['foo'] array = ['str'] duplicate = array.deep_dup duplicate[0].gsub!('str', 'foo') # 要素も複製されている。 # 元の配列は変更されない array # => ['str'] duplicate # => ['foo']
  11. 11. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#to_query to_query はクエリストリングを生成するのに使える 特に Hash#to_query はクエリストリングを生成する ときにベンリ 10 # このメソッドは、キーと値のいずれについても、必要な箇所をすべてエスケープします。 account.to_query('company[name]') # => "company%5Bname%5D=Johnson+%26+Johnson" # 配列にto_queryメソッドを適用した場合、to_queryを配列の各要素に適用して # key[]をキーとして追加し、それらを"&"で連結したものを返します。 [3.4, -45.6].to_query('sample') # => "sample%5B%5D=3.4&sample%5B%5D=-45.6" # ハッシュもto_queryに応答しますが、異なるシグネチャを使用します。 # メソッドに引数が渡されない場合、このメソッド呼び出しは、一連のキー/値ペアを # ソート済みの形で生成し、それぞれの値に対してto_query(key)を呼び出します。 # 続いて結果を"&"で連結します。 {c: 3, b: 2, a: 1}.to_query # => "a=1&b=2&c=3"
  12. 12. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Object#with_options 何度も同じ引数が出てきて、ウザいのが、、、 with_options を使うことでまとめられる 11 class Account < ApplicationRecord has_many :customers, dependent: :destroy has_many :products, dependent: :destroy has_many :invoices, dependent: :destroy has_many :expenses, dependent: :destroy end class Account < ApplicationRecord with_options dependent: :destroy do |assoc| assoc.has_many :customers assoc.has_many :products assoc.has_many :invoices assoc.has_many :expenses end end
  13. 13. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Module の拡張: delegate delegate を使うとメソッドを簡単に委譲できる。 複数の引数をとることで、複数の属性を一度に移譲できる 12 class User < ApplicationRecord has_one :profile def name profile.name end end class User < ApplicationRecord has_one :profile delegate :name, to: :profile end delegate :name, :age, :address, :twitter, to: :profile # 定数 Rails に委譲する delegate :logger, to: :Rails # レシーバのクラスに委譲する delegate :table_name, to: :class # profile が nil のとき、例外を raise せず nil を返す delegate :name, to: :profile, allow_nil: true
  14. 14. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Class の拡張: class_attribute 継承可能なクラス属性を定義 https://qiita.com/cuzic/items/ffd115f1e17458020b1b 13 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
  15. 15. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Class の拡張: cattr_reader、cattr_writer、cattr_accessor cattr_accessor でクラス変数へのアクセサを定義可能 cattr_reader でリーダだけを定義 cattr_writer でライタだけを定義 14 class MysqlAdapter < AbstractAdapter # @@emulate_booleansにアクセスできるクラスメソッドを生成する cattr_accessor :emulate_booleans, default: true end module A class B # first_name readerは生成されない cattr_accessor :first_name, instance_reader: false # last_name= writerは生成されない cattr_accessor :last_name, instance_writer: false # reader(surname)も writer(surname=) も生成されない cattr_accessor :surname, instance_accessor: false end end
  16. 16. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 String 拡張: 活用形 1/3 15 "table".pluralize # => "tables" "ruby".pluralize # => "rubies" "equipment".pluralize # => "equipment" "tables".singularize # => "table" "rubies".singularize # => "ruby" "equipment".singularize # => "equipment" "product".camelize # => "Product" "admin_user".camelize # => "AdminUser" "Product".underscore # => "product" "AdminUser".underscore # => "admin_user" "alice in wonderland".titleize # => "Alice In Wonderland" "fermat's enigma".titleize # => "Fermat's Enigma"
  17. 17. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 String 拡張: 活用形 2/3 16 "name".dasherize # => "name" "contact_data".dasherize # => "contact-data" "Product".demodulize # => "Product" "Backoffice::UsersController".demodulize # => "UsersController" "Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils" "::Inflections".demodulize # => "Inflections" "".demodulize # => "" "Product".deconstantize # => "" "Backoffice::UsersController".deconstantize # => "Backoffice" "Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel" "John Smith".parameterize # => "john-smith" "Kurt Gödel".parameterize # => "kurt-godel" "Person".tableize # => "people" "Invoice".tableize # => "invoices" "InvoiceLine".tableize # => "invoice_lines"
  18. 18. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 String拡張:活用形 3/3 17 "people".classify # => "Person" "invoices".classify # => "Invoice" "invoice_lines".classify # => "InvoiceLine" "Fixnum".constantize # => Fixnum "name".humanize # => "Name" "author_id".humanize # => "Author" "author_id".humanize(capitalize: false) # => "author" "comments_count".humanize # => "Comments count" "_id".humanize # => "Id" "User".foreign_key # => "user_id" "InvoiceLine".foreign_key # => "invoice_line_id" "Admin::Session".foreign_key # => "session_id"
  19. 19. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 String拡張 to_date, to_time, to_datetime to_date で Date 型 to_time :Time型 to_datetime : DateTime 型 Time.zone.parse よりも文字数が少なく便利 18 "2010-07-27".to_date # => Tue, 27 Jul 2010 "2010-07-27 23:37:00".to_time # => 2010-07-27 23:37:00 +0200 "2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000 "2010-07-27 23:42:00".to_time(:utc) # => 2010-07-27 23:42:00 UTC "2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200 # デフォルトは :local
  20. 20. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Numeric の拡張: フォーマッティング 1/3 19 Currency: 1234567890.50.to_s(:currency) # => "$1,234,567,890.50" 1234567890.506.to_s(:currency) # => "$1,234,567,890.51" 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506" -1234567890.50.to_s(:currency, negative_format: '(%u%n)') # => "($1,234,567,890.50)" 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '') # => "&pound;1234567890,50" 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u') # => "1234567890,50 &pound;" Percentage: 100.to_s(:percentage) # => "100.000%" 100.to_s(:percentage, precision: 0) # => "100%" 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%" 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%" 1000.to_s(:percentage, locale: :fr) # => "1 000,000%" 100.to_s(:percentage, format: '%n %') # => "100.000 %"
  21. 21. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Numeric の拡張: フォーマッティング 2/3 20 Delimited: 12345678.to_s(:delimited) # => "12,345,678" 12345678.05.to_s(:delimited) # => "12,345,678.05" 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678" 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678" 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05" 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05" 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',') # => "98 765 432,98" Rounded: 111.2345.to_s(:rounded) # => "111.235" 111.2345.to_s(:rounded, precision: 2) # => "111.23" 13.to_s(:rounded, precision: 5) # => "13.00000" 389.32314.to_s(:rounded, precision: 0) # => "389" 111.2345.to_s(:rounded, significant: true) # => "111" 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100" 13.to_s(:rounded, precision: 5, significant: true) # => "13.000" 111.234.to_s(:rounded, locale: :fr) # => "111,234" 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true) # => "13" 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3" 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.') # => "1.111,23"
  22. 22. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Numeric の拡張: フォーマッティング 3/3 21 Human-friendly size in Bytes: 123.to_s(:human_size) # => "123 Bytes" 1234.to_s(:human_size) # => "1.21 KB" 1234567.to_s(:human_size) # => "1.18 MB" 1234567890.to_s(:human_size) # => "1.15 GB" 1234567890123.to_s(:human_size) # => "1.12 TB" 1234567890123456.to_s(:human_size) # => "1.1 PB" 1234567890123456789.to_s(:human_size) # => "1.07 EB" 1234567.to_s(:human_size, precision: 2) # => "1.2 MB" 483989.to_s(:human_size, precision: 2) # => "470 KB" 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB" 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB" 524288000.to_s(:human_size, precision: 5) # => "500 MB" Human-friendly format: 123.to_s(:human) # => "123" 1234.to_s(:human) # => "1.23 Thousand" 1234567.to_s(:human) # => "1.23 Million" 1234567890.to_s(:human) # => "1.23 Billion" 1234567890123.to_s(:human) # => "1.23 Trillion" 1234567890123456.to_s(:human) # => "1.23 Quadrillion" 489939.to_s(:human, precision: 2) # => "490 Thousand" 489939.to_s(:human, precision: 4) # => "489.9 Thousand" 1234567.to_s(:human, precision: 4, significant: false) # => "1.2346 Million" 1234567.to_s(:human, precision: 1, separator: ',', significant: false) # => "1,2 Million"
  23. 23. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Enumerable の拡張: sum Enumerable#sum : 合計値を計算 空配列の場合のデフォルト値も指定できる。 数値でなくても、 + メソッドを使って sum を計算 22 [1, 2, 3].sum # => 6 (1..100).sum # => 5050 [[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4] %w(foo bar baz).sum # => "foobarbaz" {a: 1, b: 2, c: 3}.sum # => [:b, 2, :c, 3, :a, 1] [].sum(1) {|n| n**3} # => 1 (1..3).sum{|n| n**3} #=> 36 (1..3).sum(1){|n| n**3} #=> 36
  24. 24. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Enumerable の拡張: 抽出 exclude? で要素が含まれていないときの判定ができる without を使うと、指定した要素を除外できる pluck メソッドで、指定したキーに基づく配列を返す 23 # exclude? は、include? の逆の動作 to_visit << node if visited.exclude?(node) # without ["David", "Rafael", "Aaron", "Todd"].without("Aaron", "Todd") # => ["David", "Rafael"] # pluck [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
  25. 25. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Hash の拡張: 抽出 Hash#reverse_merge はデフォルト設定のときに便利 except で不要な要素の除外、slice で必要な要素の抽出ができる extract は、ハッシュから取り除いた上でそのキーのペアを返す compact を使うと値が nil の要素を除外できる 24 # デフォルト値が左側で、右側がカスタマイズする引数 # 同じキーがあれば、右側が優先される options = {length: 30, omission: "..."}.merge(options) # reverse_merge を使うとデフォルト値が右側になり、見やすい options = options.reverse_merge(length: 30, omission: "...") # 破壊的に変更する options.reverse_merge!(length: 30, omission: "...") {a: 1, b: 2}.except(:a) # => {:b=>2} {a: 1, b: 2, c: 3}.slice(:a, :c) # => {:a=>1, :c=>3} hash = {a: 1, b: 2} rest = hash.extract!(:a) # => {:a=>1} hash # => {:b=>2} {a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}
  26. 26. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 Hash の拡張: stringify、symbolize 文字列とシンボルの両方がキーになりえるとき、 stringify、symbolize で片方に寄せると扱いやすい with_indifference_access を使うと 文字列とシンボルのどちらでも使えるようにできる 25 {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"} {nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys # => {nil=>nil, 1=>1, nested:{a:3, 5=>5}} {a: 1}.with_indifferent_access["a"] # => 1 # 文字列とシンボルの両方がありえる場合に、ベンリ def rewrite_path(options) options = options.symbolize_keys options.update(options[:params].symbolize_keys) if options[:params] ... end
  27. 27. 大手町.rb #10 発表資料 「Active Support のコア拡張機能について」 まとめ 今日は Active Support のコア拡張機能について、 ピックアップして、紹介しました。 個人的には特に Object の拡張機能をよく使います blank?、present?、presence、with_options あと、 Hash の拡張もベンリです。 to_query、reverse_merge、except、slice、 with_indifferent_access ドキュメントを熟読しましょう! 気が付かなかった発見がたくさんあります。 私自身、たくさんありました 特に Rails Guide と Ruby on Rails API は必読です。 https://railsguides.jp/ https://api.rubyonrails.org/ 26
  28. 28. ご清聴ありがとう ございました

×