LoginSignup
0
0

More than 1 year has passed since last update.

Railsコードリーデイング③ route.rbのgetメソッドを深掘りしてみた 【備忘録】

Posted at

はじめに

MVCにおいてどのように内部処理が行われページ遷移に至るのかフレームワークを使用していると何も気にせずとも開発できてしまいます。
しかしこの基本的な動作の仕組みを理解することで、エンジニアとしての基本的な思考を理解できるのではと考えました。
今回は最も基本的なgetメソッドを見ていきます。

準備

以下のルーティングを作成

rails/config/route.rb
Rails.application.routes.draw do
  binding.pry
  get 'users' => 'users#index'
end

実行

getメソッド

rails/actionpack/lib/action_dispatch/routing/mapper.rb
module ActionDispatch
  module Routing
    class Mapper

      (省略)

      module HttpHelpers
        # Define a route that only recognizes HTTP GET.
        # For supported arguments, see match[rdoc-ref:Base#match]
        #
        #   get 'bacon', to: 'food#bacon'
        def get(*args, &block)
          map_method(:get, args, &block)
        end

args: {"users"=>"users#index"}
block: []

 学び① 仮引数の* (*args)

仮引数に*をつけることで余りを全て配列として受け取る事ができる。これを可変長引数と呼ぶ。
https://docs.oracle.com/javase/jp/7/technotes/guides/language/varargs.html

引数の数に柔軟に対応したいときに使えそうです。

学び② ブロック

ブロックは「処理の集合体」です。無名関数とかラムダ式とかクロージャのようなものです。
https://qiita.com/genya0407/items/1a34244cba6c3089a317

getに対し処理を追記した場合にblockに処理が入ると思われます。
{ |e| e + 1 }
こちらのような表記です。

map_methodメソッド

rails/actionpack/lib/action_dispatch/routing/mapper.rb
    module HttpHelpers
      private
          def map_method(method, args, &block)
            options = args.extract_options!
            options[:via] = method
            match(*args, options, &block)
            self
          end
      end

argsをpopしています。今回要素は1つだけなので、optionsにargsがそのまま代入されます。
map_methodメソッドは、整形してoptionsに
getメソッド
{"users"=>"users#index"}
という2点の情報をまとめる機能を担っています。

rails/actionpack/lib/action_dispatch/routing/mapper.rb
    module Resources
        # Matches a URL pattern to one or more routes.
        # For more information, see match[rdoc-ref:Base#match].
        #
        #   match 'path' => 'controller#action', via: :patch
        #   match 'path', to: 'controller#action', via: :post
        #   match 'path', 'otherpath', on: :member, via: :get
        def match(path, *rest, &block)
          if rest.empty? && Hash === path
            options  = path
            path, to = options.find { |name, _value| name.is_a?(String) }

            raise ArgumentError, "Route path not specified" if path.nil?

            case to
            when Symbol
              options[:action] = to
            when String
              if /#/.match?(to)
                options[:to] = to
              else
                options[:controller] = to
              end
            else
              options[:to] = to
            end

            options.delete(path)
            paths = [path]
          else
            options = rest.pop || {}
            paths = [path] + rest
          end

          if options.key?(:defaults)
            defaults(options.delete(:defaults)) { map_match(paths, options, &block) }
          else
            map_match(paths, options, &block)
          end
        end

学び③ ===

self === obj -> bool
指定された obj が self かそのサブクラスのインスタンスであるとき真を返します。また、obj が self をインクルードしたクラスか>そのサブクラスのインスタンスである場合にも真を返します。上記のいずれでもない場合に false を返します。
https://docs.ruby-lang.org/ja/latest/method/Module/i/=3d=3d=3d.html

モジュール内で型を確認する場合に使えそうです。
obj.kind_of?(self)と同じ機能ということですが、===の方がスッキリかけます。ただobjとselfの並びが逆なことには注意が必要です。

学び④ findメソッドと多重代入の組み合わせ

path, to = options.find { |name, _value| name.is_a?(String) }
ハッシュのname(keyと呼ぶことの方が多いが)がStringの場合は、pathとtoに代入します。
シンボルで表記されていた場合は、代入されないということになります。

変数を_valueと先頭にアンダーバーを付けているのは何故でしょうか。
こちらは、先頭アンダーバーのもので絞り込むことによって探しやすいというメリットがあるようです。処理内の変数なのでこの表記にする理由はいまいちわかりませんでした。
https://anderson02.com/cs/cs-rules/cs-rules10/

最後に

今日はここまでです。前処理の部分でこんなに学びがあるとはという感想です。
まだまだ、routingがどのように処理されているかに踏み込めていないため、この部分のコードリーディング継続する予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0