0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

はじめに

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

https://qiita.com/engineer_ikuzou/items/ef70de5de070b1094ab6
こちらの記事の続きです。

matchメソッド

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

pathのnilチェック

rails/actionpack/lib/action_dispatch/routing/mapper.rb
raise ArgumentError, "Route path not specified" if path.nil?

 学び① 例外処理 raise

throwと同じ役割のようです。
raise SomeException, 'message'
例外クラスとメッセージを引数として渡すことができます。

変数toの型に応じてoptionsハッシュに代入

rails/actionpack/lib/action_dispatch/routing/mapper.rb
            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]

変数toは、"users#index"つまりStringなので、when Stringに入ります。
コントローラ#アクションで書かれるため、#がない場合は、controllerが書かれているというように処理します。

そして、
{"users"=>"users#index", :via=>:get, :to=>"users#index"}
からpath(users)を削除。
{:via=>:get, :to=>"users#index"}

最後にpathsにpathを配列で代入。

学び② case whenは型の比較にも適用できる。

学び③ matchメソッド

/#/.match?(to)で、to#が含まれているかどうか判定しています。
https://docs.ruby-lang.org/ja/latest/method/String/i/match.html
https://docs.ruby-lang.org/ja/latest/method/Regexp/i/match=3f.html

デフォルト値のチェック

rails/actionpack/lib/action_dispatch/routing/mapper.rb
          if options.key?(:defaults)
            defaults(options.delete(:defaults)) { map_match(paths, options, &block) }
          else
            map_match(paths, options, &block)
          end

route.rbで
get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
のようにデフォルト値を設定している場合は、trueの方に入ります。
今回はデフォルト値の設定なしなので、elseです。

学び④ key?メソッド

options.key?(:defaults)optionsが:defaultsというkeyを持つかどうかの判定です。
https://docs.ruby-lang.org/ja/latest/method/Hash/i/has_key=3f.html

処理本体

rails/actionpack/lib/action_dispatch/routing/mapper.rb
          def map_match(paths, options)
            if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
              raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
            end

            if @scope[:to]
              options[:to] ||= @scope[:to]
            end

            if @scope[:controller] && @scope[:action]
              options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
            end

            controller = options.delete(:controller) || @scope[:controller]
            option_path = options.delete :path
            to = options.delete :to
            via = Mapping.check_via Array(options.delete(:via) {
              @scope[:via]
            })
            formatted = options.delete(:format) { @scope[:format] }
            anchor = options.delete(:anchor) { true }
            options_constraints = options.delete(:constraints) || {}

            path_types = paths.group_by(&:class)
            (path_types[String] || []).each do |_path|
              route_options = options.dup
              if _path && option_path
                raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
              end
              to = get_to_from_path(_path, to, route_options[:action])
              decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
            end

            (path_types[Symbol] || []).each do |action|
              route_options = options.dup
              decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
            end

            self
          end

ついに前処理が終わり、ルーティングの本体の処理が見えました。
一旦、最終的に残ったものが何か見てみます。

[11] pry(#<ActionDispatch::Routing::Mapper>)> self
=> #<ActionDispatch::Routing::Mapper:0x00007faf9452b930
 @concerns={},
 @draw_paths=[#<Pathname:/Users/username/codereading/CodeReading/config/routes>],
 @scope=
  #<ActionDispatch::Routing::Mapper::Scope:0x00007faf9452b8e0
   @hash={:path_names=>{:new=>"new", :edit=>"edit"}},
   @parent=#<ActionDispatch::Routing::Mapper::Scope:0x00007faf9f10bd90 @hash=nil, @parent=nil, @scope_level=nil>,
   @scope_level=nil>,
 @set=#<ActionDispatch::Routing::RouteSet:0x00007faf9e98ada0>>

ActionDispatch::Routing::Mapperにインスタンス変数として、ルーティング情報を記録しているということでしょうか。
こちらのモジュールを継承することによってルーティング情報を取得できるものと思われます。

最後に

本日はここまでです。処理の詳細は引き続き読んでみようと思いますが、ルーティングの情報はインスタンス変数に保存されるということでした。考えたら当たり前な気もしますが、謎に包まれていたものが少し見えてきたため、嬉しいです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?