It will do the same with RequestDispatcher: Use the server to call to
another controller.
> Hey folks.
>
> Sanity check:
>
> A student in my Rails class asked me if there is an equivalent to the
> J2EE/Servlet getRequestDispatcher.forward. In the Servlet spec., this asks
> the container to hand off control to another resource, most typically,
> though not necessarily, another Servlet. (
> http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/RequestDispatcher.html#forward(javax.servlet.ServletRequest,%20javax.servlet.ServletResponse)
# Renders the template for the action "goal" within the current controller
render :action => "goal"
http://api.rubyonrails.org/classes/ActionController/Base.html#M000272
See? "Renders the TEMPLATE."
What I am talking about is actually getting
OtherController#some_action to run; i.e., the method definition.
That isn't what the docs say, and when I looked at the Rails source,
that doesn't seem to be what ActionController::Base or dispatcher.rb
wants to do.
So my test case is:
def action1
print "\n\n**** Action1 ****\n\n"
end
def action2
print "\n\n**** Action2 ****\n\n"
render :action => :action1
end
browse to . . . . /action2 , and you will never see on the console
*** Action1 ***, which supports what the docs say. render :action
means pick the template for the other action. It doesn't mean run the
other action.
I'm not making some big claim -- the behavior seems obvious enough
from the documentation and the Rails source -- just trying to
emphasize what a J2EE person would perceive as a gap.
But, of course, I wish to stand corrected. :-)
File test_one_controller.rb
---------------------------------------------------
class TestOneController < ApplicationController
def action_one
render :action => :action_two
end
def action_two
end
def action_dispatch
render_component :controller => "test_two", :action => :action_three
end
end
----------------------------------------------------
File test_two_controller.rb
----------------------------------------------------
class TestTwoController < ApplicationController
def action_three
end
end
----------------------------------------------------
You will need to create 3 template files to test
app/views/test_one/action_one.rhtml
app/views/test_one/action_two.rhtml
app/views/test_two/action_three.rhtml
In each file you just need to write "Action One", "Action Two" and "Action
Three" respectively to distinguish them.
So, if you try
http://localhost:3000/test_one/action_one -> You will see "Action Two". It
means that the method action_two is called.
http://localhost:3000/test_one/action_two -> You will see "Action Two".
http://localhost:3000/test_one/action_dispatch => You will see "Action
Three". It means the method action_three in the controller
TestTwoController is called, although you start with TestOneController.
If you still don't believe me, should we make a friendly bet for a beer or
a sushi dinner? Next time we have Boston Group meeting, I will bring the
code.
That was exactly what I was trying to find, which render wasn't doing
for me. In other words, I think everyone I wrote was correct, I had
just completely spaced off render_component.
(Now, as to forgetting render_component: It is probably because the
new edition of Agile Web Development with Rails is hostile to
components, and says it will be ejected from Rails core for 2.0.0,
turned into a separate plugin -- which I haven't checked. True? Are
you getting a deprecation warning for render_component in 1.2.5?)
I don't really use render component, so I don't notice about it. I write
code in certain way to re-use partials and dynamically pass params to
avoid using components. However, I will make a test to see if it is
deprecated in 1.2.5.
About beer, when we have the group meeting, it is very likely we will have
beer afterward anyway, so we can chat more about Ruby and Rails.
Have a great weekend.
Rails only renders the templates which are correspondent to the action. It
does not really call the action itself.
In the 1) case, it really does what it tells: Render the template of the
action, not calling the action. (I made an error about understanding right
here).
In the 2) case, even the Rails API document says that it forward the
request to the controller/action, and provides an example about it.
http://api.rubyonrails.org/classes/ActionController/Components/InstanceMethods.html#M000084
But that example doesn't work that way. It simply renders the template of
the controller/action, without calling any code in the method. I guess
that it is Rails implementation.
So eventually, it looks like Rails doesn't have anything like
RequestDispatcher.forward.
For instance, we would have a Login servlet that would check your
login and then that would forward appropriately if you were in fact
logged in and the other servlets could forward to login if you weren't
logged in etc.
But the rails way of doing it with filters is much cleaner and clearer
and easier to test and follow.
I'm sure if anybody had a need it would be easy enough to hack the
rails dispatcher and implement a forward in the java style. But I see
so many superior alternatives, there's simply no need.
All forwarding and passing of control should be done directly via
method calls anyway, if I want to call another action I should just
call it. If I want to do some partial request processing and then
hand off to another action, I can do it in a filter.
In J2EE Servlets you couldn't pass control with a method call, you HAD
to use the "forward" mechanism and if you wanted to pass any arguments
had to stick them into the request object with some "key" that was
probably a constant from some giant constants file somewhere. We had
giant XML files configuring how instances of one servlet would forward
to specifically configured instance of some other servlet.
I think one of the "Big Enterprise" supposed reasons for Servlet
forwarding was that theoretically the Servlet container could decide
to forward to a completely different process on possibly even a
different machine to continue your request processing.... but, we
never actually used anything like that.
So I would tell you student, there's simply need to for dispatcher
forwarding in rails, if you think there's a problem that would be
solved by forwarding, it's probably more cleanly solved by something
else...
One pain point, though, about calling a method on another controller
is that you would have to instantiate it, which is kind of awkward.
(Or you'd have to write a handler of some kind to do that.) So I think
the dispatcher is the place to intervene.
In any case, what you describe is pretty much what I said to my
students: The J2EE dependence on forwards is an artifact of the
architecture, and can be done more elegantly, generally, using filters
and other means in Rails.
Thanks!!