普段仕事で使っているRuby on Railsですが、ソースコードを読む機会もなかなかないので、試しにやってみることにしました
読めるようにするまで
1. Githubのrails/railsリポジトリから任意のフォルダにクローンする
- ブラウザで
https://github.com/rails/rails
を開く Clone or Downloadをクリック
クリップボードアイコンをクリック
任意のディレクトリ配下で
$ git clone https://github.com/rails/rails.git rails
2. 任意のエディタで閲覧
僕の場合はRubyMineを使ってます
※以下はRubyMineの場合の設定方法です
Rubymineを開き、openを選択
先程cloneした任意のフォルダを選択する
- 下記のように取り込めている
読んだ箇所
Rails書いているとよく使う blank?
を今日は読んでみようと思います
どんな使い方だっけ?
読んで見る前に使い方を調べてみます Railsの日本語ドキュメントを見てみると
ruby 変数.blank?
nil? + empty? のようなメソッド。nilまたは空のオブジェクトをチェックできる。
nil, "", " "(半角スペースのみ), , {}(空のハッシュ) のときにfalseを返します。
Railsで拡張されたメソッドで、Rubyのみでは使えないのでご注意ください。
引用:Railsドキュメント
ざっくり値がないものを確認したいときとかに便利ですね。ruby標準でも欲しいなと思ったりします
ソースコードを読んでみる
1. railsプロジェクトのactivesupportにある機能なので、activesupportディレクトリのlib配下で def blank?
を探してみます
2. 該当箇所が10個ほどあったので、それぞれみてみます
1. activesupport > lib > active_support > core_ext > date > blank.rb
# frozen_string_literal: true require "date" class Date #:nodoc: # No Date is blank: # # Date.today.blank? # => false # # @return [false] def blank? false end end
Dateクラスに対してblank?をすると必ずfalseが返り値のようです
オープンクラスで実装しているようです
※ オープンクラスとは?
既存するクラスを好きな場所で再オープンし、メソッド修正・追加など任意の変更を加えられる機能のこと。
引用: [Ruby] メタプログラミングの入り口、オープンクラスを理解する
2. activesupport > lib > active_support > time_with_zone.rb
中略 # An instance of ActiveSupport::TimeWithZone is never blank def blank? false end 中略
ActiveSupport::TimeWithZoneの場合も同じように必ずfalseが返り値のようです
3. activesupport > lib > active_support > core_ext > date_time > blank.rb
# frozen_string_literal: true require "date" class DateTime #:nodoc: # No DateTime is ever blank: # # DateTime.now.blank? # => false # # @return [false] def blank? false end end
DateTimeクラスに対してblank?をすると必ずfalseが返り値のようです
4. activesupport > lib > active_support > core_ext > object > blank.rb
Object.blank?
# An object is blank if it's false, empty, or a whitespace string. # For example, +false+, '', ' ', +nil+, [], and {} are all blank. # # This simplifies # # !address || address.empty? # # to # # address.blank? # # @return [true, false] def blank? respond_to?(:empty?) ? !!empty? : !self end
respond_to?の使い方を調べてみると
respond_to?メソッドは、レシーバのオブジェクトに対してメソッドを呼び出せるかどうかを調べます。引数nameにはメソッド名をシンボルか文字列で指定します。メソッドnameを持っていればtrue、なければfalseが返ります。
レシーバのクラスのメソッドだけでなく、親クラスやインクルードしているモジュールのメソッドも対象になります。デフォルトではpublicなメソッドとprotectedなメソッドを調べますが、第2引数にtrueを指定するとprivateなメソッドも含めて調べます。
引用:Rubyリファレンス:respond_to? (Object)
empty?が実装されているかチェックしているようです
そしてtrueの場合は!!empty?
で falseの場合は !selfが返ります
なぜempty?の前に!!が付いているのかわからなかったので、更に調べてみます
該当箇所のコミットログを見てみると
126dc47で変更しているようです
Githubで見てみると
コントリビューターから質問されているところを見つけました
rubyという言語仕様上empty?が書き換えられて必ず TrueClass
/ FalseClass
のシングルトンが返り値となるわけではない為、こういう書き方をしているようです
!self
はObjectクラスのインスタンスに対して行っているので、基本的にfalseが返り値となります
※ trueになるケースが思いつかなかったので、誰か知っている方いらっしゃいましたら教えてください
NilClass
class NilClass # +nil+ is blank: # # nil.blank? # => true # # @return [true] def blank? true end end
Dateクラスのときと同じようにオープンクラスしてblank?メソッドを追加しているようです
下記クラスも同様ですね
FalseClass
class FalseClass # +false+ is blank: # # false.blank? # => true # # @return [true] def blank? true end end
TrueClass
class TrueClass # +true+ is not blank: # # true.blank? # => false # # @return [false] def blank? false end end
String
BLANK_RE = /\A[[:space:]]*\z/ # A string is blank if it's empty or contains whitespaces only: # # ''.blank? # => true # ' '.blank? # => true # "\t\n\r".blank? # => true # ' blah '.blank? # => false # # Unicode whitespace is supported: # # "\u00a0".blank? # => true # # @return [true, false] def blank? # The regexp that matches blank strings is expensive. For the case of empty # strings we can speed up this method (~3.5x) with an empty? call. The # penalty for the rest of strings is marginal. empty? || BLANK_RE.match?(self) end
レシーバがempty?またはBLANK_REにマッチする場合はtrueのようです
BLANK_REをみてみると BLANK_RE = /\A[[:space:]]*\z/
[:space:]はPOSIX文字クラスというものです。スペースとタブと改ページにマッチします
上記2パターンのどちらかにマッチした場合にtrueが返り値になるようです
Numeric
class Numeric #:nodoc: # No number is blank: # # 1.blank? # => false # 0.blank? # => false # # @return [false] def blank? false end end
Numericの場合も必ずfalseが返り値のようです
0だともしかしたらtrueが返り値かもとか思ってしまいそうなので、確認するのは大事ですね
Time
class Time #:nodoc: # No Time is blank: # # Time.now.blank? # => false # # @return [false] def blank? false end end
読んでみて
ソースコードを読むのは結構億劫だったりしますが、よく使う処理などは意外な発見があったりして楽しめそうです