はじめに
MVCにおいてどのように内部処理が行われページ遷移に至るのかフレームワークを使用していると何も気にせずとも開発できてしまいます。
しかしこの基本的な動作の仕組みを理解することで、エンジニアとしての基本的な思考を理解できるのではと考えました。
今回は最も基本的なgetメソッドを見ていきます。
https://qiita.com/engineer_ikuzou/items/ef70de5de070b1094ab6
こちらの記事の続きです。
matchメソッド
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チェック
raise ArgumentError, "Route path not specified" if path.nil?
学び① 例外処理 raise
throwと同じ役割のようです。
raise SomeException, 'message'
例外クラスとメッセージを引数として渡すことができます。
変数toの型に応じてoptionsハッシュに代入
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
デフォルト値のチェック
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
処理本体
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
にインスタンス変数として、ルーティング情報を記録しているということでしょうか。
こちらのモジュールを継承することによってルーティング情報を取得できるものと思われます。
最後に
本日はここまでです。処理の詳細は引き続き読んでみようと思いますが、ルーティングの情報はインスタンス変数に保存されるということでした。考えたら当たり前な気もしますが、謎に包まれていたものが少し見えてきたため、嬉しいです。