Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

[Rails5] Active Support Core ExtensionsのStringの小粒なメソッドたち: 文字列定数は#freezeしよう

こんにちは、hachi8833です。Active Support探訪シリーズ、今回はStringクラスの小物をいくつかまとめて見てみます。

今回のメソッド

条件

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_withString#end_withはどちらもRubyのメソッドです。

startとendをstartsとendsにした理由を知りたかったので履歴を追ってみたところ、元々DHHが独自に実装したものが数回のリファクタリングを経て「なんだ、Rubyに同じものがあるじゃん」ということでエイリアスされたようです。

なお、三人称単数になってない#start_with?#end_with?は英語圏では割りと残念に感じられると思います。

関連記事

[Rails5] Active Supportの概要をつかむ

[Rails5] Railsの主要なライブラリ構成

Rails: ビューのHTMLエスケープは#link_toなどのヘルパーメソッドで解除されることがある


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。