はじめに
Railsではcontrollerで定義したインスタンス変数をview側で使うことができますが、最近、ふと「これなんで?」と思ったのでrailsの中身を見てみようと思います。
コードリーディング
作成されるcontrollerはApplicationControllerを継承していて、ApplicationControllerはActionController::Baseを継承しているので、ActionController::Baseを見にいきます。
# == Renders
#
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
# The controller passes objects to the view by assigning instance variables:
#
# def show
# @post = Post.find(params[:id])
# end
#
# Which are then automatically available to the view:
#
# Title: <%= @post.title %>
#
# You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
# will use the manual rendering methods:
#
# def search
# @results = Search.find(params[:query])
# case @results.count
# when 0 then render action: "no_results"
# when 1 then render action: "show"
# when 2..10 then render action: "show_many"
# end
# end
#
# Read more about writing ERB and Builder templates in ActionView::Base.
The controller passes objects to the view by assigning instance variables:
instance variableをassignすることで、controllerはview側にオブジェクトを渡すと書かれています。
まさにこのことです。
Read more about writing ERB and Builder templates in ActionView::Base.
もっと知りたかったらActionView::Baseを見に行けと書かれているので見にいきます。
def initialize(lookup_context, assigns, controller) # :nodoc:
@_config = ActiveSupport::InheritableOptions.new
@lookup_context = lookup_context
@view_renderer = ActionView::Renderer.new @lookup_context
@current_template = nil
assign(assigns)
assign_controller(controller)
_prepare_context
end
ActionView::Baseクラスのインスタンスが生成されるときの処理内容です。
def assign(new_assigns) # :nodoc:
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
今回対象となるのはassignメソッドです。(ここはエスパーしました。)
ActionView::Baseのインスタンス変数@_assignsに、ActionView::Baseがinitializeされた際の引数assignsを代入しています。
次はActionView::Baseがinitializeされるところを見にいきます。
def view_context
view_context_class.new(lookup_context, view_assigns, self)
end
ActionView::Baseはview_contextメソッドによって、initializeされます。(ここはまた別の記事で書きます。)
view_contextメソッドはそのすぐ下にある_render_templateメソッドから呼び出されていて、このメソッドはレスポンスのbodyを作るメソッドです。
view_contextに渡される第2引数がassignメソッドに渡される引数(view側で使うことができるインスタンス変数)です。
view_assignsメソッドを見にいきます。
def view_assigns
variables = instance_variables - _protected_ivars
variables.each_with_object({}) do |name, hash|
hash[name.slice(1, name.length)] = instance_variable_get(name)
end
end
view_assignsメソッドは最終的に変数variablesを返しますが、その中身はinstance_variablesで、これはRuby由来のメソッドでその呼び出し元のオブジェクトのインスタンス変数を返すメソッドです。
このときの呼び出し元のオブジェクトはcontrollerのインスタンスなので、controllerで定義したインスタンス変数がそれにあたります。
ここでcontrollerのインスタンス変数がview側に渡されています。
参考にしたサイト