Rubyのbegin-end構文で変数を定義する

こんにちは!kossyです!




さて、今回はRubyのbegin-end構文で変数を定義する方法についてブログに残してみたいと思います。




begin-end構文とは?

begin-endは例外処理でよく用いられるイメージですが、例外処理以外の用途でも使うことができるようです。

What is use of ||= begin....end block in Ruby? - Stack Overflow

@valiable ||= begin
  処理
end

という構文で使うことができます。


Rails内での使われ方

Railsのコードなら間違いないだろうということで紹介してみます。

rails/engine.rb at 0105fd4a1ec5579fbc824c7a864794ba90351c39 · rails/rails · GitHub

def helpers
  @helpers ||= begin
    helpers = Module.new
    all = ActionController::Base.all_helpers_from_path(helpers_paths)
    ActionController::Base.modules_for_helpers(all).each do |mod|
      helpers.include(mod)
    end
    helpers
  end
end

@helpersというインスタンス変数を定義するためにbegin-end構文を用いてますね。

もう少し探ってみましょう。

rails/app_updater.rb at 0105fd4a1ec5579fbc824c7a864794ba90351c39 · rails/rails · GitHub

def app_generator
  @app_generator ||= begin
    gen = Rails::Generators::AppGenerator.new ["rails"], generator_options, destination_root: Rails.root
    File.exist?(Rails.root.join("config", "application.rb")) ? gen.send(:app_const) : gen.send(:valid_const?)
    gen
  end
end

rails/application.rb at 0105fd4a1ec5579fbc824c7a864794ba90351c39 · rails/rails · GitHub

def secrets
  @secrets ||= begin
    secrets = ActiveSupport::OrderedOptions.new
    files = config.paths["config/secrets"].existent
    files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets
    secrets.merge! Rails::Secrets.parse(files, env: Rails.env)

    # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
    secrets.secret_key_base ||= config.secret_key_base

    secrets
  end
end

冒頭にも記述した通り、

@valiable ||= begin
  処理
end

という構文さえ覚えておけば良さそうです。

ちなみに ||= という式は、
「左辺が偽または未定義であれば、右辺の評価値を代入する」というイディオムになります。



3つほど例を見て気がつきましたが、
インスタンスを生成してインスタンスのattrの値に複雑だったり冗長な処理結果を代入して、インスタンス自身を返り値としたい時に使ってますね。

実務で使う時は上記のシチュエーションに遭遇した時に使えそうです。


勉強になりました。



参考にさせていただいたサイト

GitHub - rails/rails: Ruby on Rails
Ruby で Proc を即時実行する代わりに begin 式で代入する - Qiita