- Ruby / Rails関連
[Rails5] Active Support Core ExtensionsのStringの小粒なメソッドたち: 文字列定数は#freezeしよう
こんにちは、hachi8833です。Active Support探訪シリーズ、今回はStringクラスの小物をいくつかまとめて見てみます。
今回のメソッド
- メソッド:
String#strip_heredoc
、String#exclude?
、String#starts_with?
、String#ends_with?
- ディレクトリ配置: https://github.com/rails/rails/blob/5-0-stable/activesupport/lib/active_support/core_ext/string/のstrip.rb、exclude.rb、starts_ends_with.rb
条件
- Railsバージョン: 5-0-stable
- Rubyバージョン: 2.4.0
String#strip_heredoc
例によって元のコメントは除去してあります。
String#strip_heredocは、ヒアドキュメントから余分なインデントスペースを除去するだけのメソッドです。
class String
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
end
end
#gsub
による置き換え一発のみのシンプルなコードです。
String#freeze
による高速化
ところで、置き換え文字列の''.freeze
って何でしょう?
チームに相談してみたところ、RubyのString#freeze
の最適化を反映したものと考えられるとのことでした。
Ruby 2.3.0のリリースノートを見ると、Ruby 2.1と2.3.0でString#freeze
が最適化されたとあります。
Frozen String Literal プラグマ が導入されました。 Ruby 2.1 では既に、オブジェクトアロケーション削減のために
"str".freeze
が最適化されていましたが、Ruby 2.3 では 1 ファイル中の全 String リテラルを freeze する新しいマジックコメントとコマンドラインオプションが導入されました。
Ruby 2.3.0リリースより
それではということでstrip.rbの履歴を追ってみると、2015年9月のコミット: 503d334で```.freeze`が導入されています。
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
gsub(/^[ \t]{#{indent}}/, '')
# ↓
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
コミットメッセージにも「Saves about 6 MB, about 40% faster.」とあるので、やはり高速化が目的ですね。
今後、文字列定数はString#freeze
で積極的に固定するようにしたいと思います。いいこと知った♡
参考
String#exclude?
String#exclude?は#include?
の論理値を反転するだけのシンプルなメソッドです。
class String
def exclude?(string)
!include?(string)
end
なお、CoreExtensionにEnumerable#excludeがあることにも気づきました。
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 61
def exclude?(object)
!include?(object)
end
String#starts_with?
とString#ends_with?
エイリアスを設定するだけというシンプル極まりないメソッドです。
class String
alias_method :starts_with?, :start_with?
alias_method :ends_with?, :end_with?
end
元のString#start_withとString#end_withはどちらもRubyのメソッドです。
startとendをstartsとendsにした理由を知りたかったので履歴を追ってみたところ、元々DHHが独自に実装したものが数回のリファクタリングを経て「なんだ、Rubyに同じものがあるじゃん」ということでエイリアスされたようです。
なお、三人称単数になってない#start_with?
や#end_with?
は英語圏では割りと残念に感じられると思います。