Forum: Ruby on Rails Forwarding between controllers

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
22bb6be0cf01f0fc77d2bd61339cecd9?d=identicon&s=25 Matt Spendlove (Guest)
on 2007-04-03 02:37
(Received via mailing list)
Hi

I am a little confused - perhaps because of coming at this with a Java
head but..

I am looking for the equivalent of a forward from Java MVC frameworks
whereby the request and it's model data contents can be forwarded to
another controller ?

Required scenario :

- NewsController posts to CommentsController to add a comment.
- If the comment is valid we REDIRECT back to NewsContoller.show, all
good.
- If the comment is invalid we FORWARD back to NewsContoller.show. The
comment (and error msgs) would still be in request scope so that view
can render appropriate hints.

I currently get round this by using redirect_to (in place of a
forward) and sticking the comment in the session. Then, in
NewsController.show i check the session before loading the comment
from the DB. I think this is kinda sucky but I found a DHH post that
said there are no kinda forwards in rails :

http://groups.google.com/group/rubyonrails-talk/br...

How do peeps approach this scenario ?

Thanks

Matt
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 02:48
Hi Matt,

Have you tried render :action?  That way, the parameters, variable,
etc... will remain in the current scope (in the same method in the
controller).

For example:

In NewsController:

def add_comment
  @comment = Comment.new

  if request.post?
    if is_valid?(@comment)
      redirect_to :action => 'show'
    else
      render :action => 'show'
    end
  end
end

With render :action, you will remain in the same method in the
controller even though you are displaying show.rhtml.
22bb6be0cf01f0fc77d2bd61339cecd9?d=identicon&s=25 Matt Spendlove (Guest)
on 2007-04-03 03:13
(Received via mailing list)
Yea, I've seen render and thought of this approach.

Things is, I'd need to have an add_comment on all *Controllers that
have comments.

That's why I wanted to (re)use CommentsController.create

Perhaps Rails just isn't meant to work like this ?

Any thoughts ?

Matt


On Apr 3, 10:48 am, Wai Tsang <rails-mailing-l...@andreas-s.net>
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 03:32
You may use before_filter to add the comment validations to all
controllers by putting it in application_controller.

e.g. before_filter :add_comment, :only => [:edit, :new]

http://api.rubyonrails.org/classes/ActionControlle...


However, if you just want validation, you should look into the validate
and validation methods in model instead.

http://rails.rubyonrails.com/classes/ActiveRecord/...
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 03:35
> - NewsController posts to CommentsController to add a comment.

BTW, is there any particular reason why the NewsController would post to
another Controller instead of a Model?
22bb6be0cf01f0fc77d2bd61339cecd9?d=identicon&s=25 Matt Spendlove (Guest)
on 2007-04-03 05:58
(Received via mailing list)
Wai, thanks for the suggestions (and your patience !).

I don't think filters are what I'm after and the validation is already
within my model class (Comment)..

I guess this is coming down to a stylistic thing (and, as I said,
perhaps a hangup from Java MVC) but in my understanding, controllers
are the exposed facade for CRUD style actions. All I am really asking
is why one controller can't use another in the same request scope ?

> > - NewsController posts to CommentsController to add a comment.
>
> BTW, is there any particular reason why the NewsController would post to
> another Controller instead of a Model?

Badly worded on my part, sorry. What I mean is the HTML form, rendered
by a NewsController.show, POSTs to CommentsController.create (actually
just via "/comments" with a POST and the ActiveResource stuff takes
care of the mapping to CommentsController.create).

The comments form is wrapped up in a partial that I can reuse in any
view (say, Interview). Which would also POST to
CommentsController.create..

The comments model (acts_as_commentable) has the context and the id of
it's creator ([NEWS,1,"a comment","matt"],[NEWS,2,"another comment",
"fred"], [INTERVIEW,1,"great interview","john"]). So thats all well
and good. And, upon a successful creation, CommentsController
redirects back to it's caller (News or Interviews). It's just when not
successful, I'd like to FORWARD back to the caller without new request
scope or session munging.

Perhaps the code may help (CommentsController.create):

--------------------------------------------------------------------------
    def create

        @comment = Comment.new(params[:comment])
        @comment.commentable_type = params[:commentable_type];
        @comment.commentable_id = params[:commentable_id];

        object_to_comment = eval "#{@comment.commentable_type}.find
#{@comment.commentable_id}"

        respond_to do |format|
            if @comment.save
                object_to_comment.add_comment @comment
                flash[:notice] = 'Comment was successfully created.'
            # hmmmmm ! stinky ??
            format.html { redirect_to_url "/
#{@comment.commentable_type}/#{@comment.commentable_id}" }
            format.xml  { head :created, :location =>
comment_url(@comment) }
            else
                flash[:notice] = "You didn't enter a comment ?"
                format.html do
                    # I don't like this but I still don't get how to
ActionForward ?
                    session[:comment] = @comment
                    redirect_to  :controller =>
object_to_comment.class.to_s, :action => "show", :id =>
object_to_comment.id, :anchor => "commentsForm"
                end
                format.xml  { render :xml => @comment.errors.to_xml }

            end
        end
    end
--------------------------------------------------------------------------

Matt
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 16:31
I see.  Sorry for the misunderstanding.  The convention used in Rails is
to "share nothing" to reduce coupling, so in order for a page to use the
same variable, one can either forward the request with the variable (via
HTTP GET/POST), store the variable in session, or store the variable in
a storage (such as database).  Nevertheless, your code could be
simplified into something like this.

def create
  <snipped>
  if @comment.save
    object_to_comment.add_comment @comment
    flash[:notice] = 'Comment was successfully created.'

    format.html { redirect_to :controller => @comment.commentable_type,
:action => @comment.commentable_id }
    format.xml  { head :created, :location => comment_url(@comment) }
  else
    flash[:notice] = "You didn't enter a comment ?"
    format.html do
      session[:comment] = @comment
      redirect_to_url(request.env["HTTP_REFERER"])
    end
    format.xml  { render :xml => @comment.errors.to_xml }
  end
end
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 16:44
I just realize I misinterpretated what you want to do in the create
method.  You can use

render :controller => @comment.commentable_type,
:action => @comment.commentable_id

to render the page without going out of the request scope, until the
comment is valid (then use redirect_to).
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-03 16:53
Here is an example of what you may do:

class DocumentsController << ApplicationController
  def create
    <snipped>
    @comment.save!
    object_to_comment.add_comment @comment
    flash[:notice] = 'Comment was successfully created.'

    format.html { redirect_to :action => 'index' }
    format.xml  { head :created, :location => comment_url(@comment) }

  rescue ActiveRecord::RecordInvalid => detail
    flash[:notice] = "You didn't enter a comment ?"
    format.html do
      render :action => 'index'
    end
    format.xml  { render :xml => @comment.errors.to_xml }
  end
end

class NewsController << DocumentsController
  def index
  end
end

class ABCController << DocumentsController
  def index
  end
end

index.rhtml:
<h2>Blah Blah</h2>
<% form_tag :action => 'create' do -%>
  <% text_field :comment %>
  <%= submit_tag "Add Comment" -%>
<% end -%>
22bb6be0cf01f0fc77d2bd61339cecd9?d=identicon&s=25 Matt Spendlove (Guest)
on 2007-04-04 04:13
(Received via mailing list)
Grrr - google groups keeps erroring !

Already tried to reply to this, here we go again.

> I just realize I misinterpretated what you want to do in the create
> method.  You can use
>
> render :controller => @comment.commentable_type,
> :action => @comment.commentable_id
>
> to render the page without going out of the request scope, until the
> comment is valid (then use redirect_to).

"render" doesn't take a :controller symbol so it looks like I am stuck
with a "redirect" and chaining the Comment model into the session for
now..

AFAIK "render" only works for actions within the current controller.
Yes you can use partial templates but that's a different usage.

Thanks anyway..
52cb4115a870ff1942e7e1ae96f4306f?d=identicon&s=25 Tyler MacDonald (Guest)
on 2007-04-04 04:17
(Received via mailing list)
> "render" doesn't take a :controller symbol so it looks like I am stuck
> with a "redirect" and chaining the Comment model into the session for
> now..
>
> AFAIK "render" only works for actions within the current controller.
> Yes you can use partial templates but that's a different usage.

  You want render_component instead, it listens to :controller ... the
documentation says it's slower, tho.

  - Tyler
7c8f8b6eae4dd991e3a9065c68451361?d=identicon&s=25 Wai Tsang (sneptune)
on 2007-04-04 16:55
Matt Spendlove wrote:
> "render" doesn't take a :controller symbol so it looks like I am stuck
> with a "redirect" and chaining the Comment model into the session for
> now..
>

Sorry, I didn't noticed.  You should use render :template =>
@comment.commentable_type+'/'+@comment.commentable_id instead of render
:action.  The root of render :template is the base of the template
folder i.e. app/views.
D3fc5887a2f39f2e0c8989d39ce5e6f9?d=identicon&s=25 Bharat Ruparel (bruparel)
on 2007-04-04 21:57
Consider buying Ruby on Rails for Java Developers.  I have Java/Struts
background same as you.  This book is very useful in translating the
knowledge.  Though, I have been with Ruby/Ruby on Rails for a few months
now and those sorts of mental translations are not much needed anymore.
I would have really liked to have the aforementioned book when or before
I was starting out with Ruby/RoR.  It still has quite a bit of value.
You will find detailed discussion of the types of questions that you are
asking above.
Regards,
Bharat
22bb6be0cf01f0fc77d2bd61339cecd9?d=identicon&s=25 Matt Spendlove (Guest)
on 2007-04-09 06:43
(Received via mailing list)
@Bharat - Got the book next to the laptop :) Doesn't cover such a
scenario tho..

Its seems (as I suspected) that this concept just doesn't exist in
Rails, especially considering the DHH response (link in my first
post).

All the methods suggested are about reusing view templates in the
context of the current controller. Yes, I can use "render" and
reference another template but this is in the _context_ of the same
controller (and URL), you'd have to be sure to set up the correct
instance variables for the view also (which required some more dynamic
coding in my case).

My initial workaround is still used in my code and works fine. I'd
just rather not be doing it that way.

I think Rails would rather I just put an "add_comments" entry point in
each controller utilising comments rather than form submitting to the
CommentsController.create. Perhaps, just a stylistic thing..

If I am wrong, I'd love to be told. But please, make sure you
understand this thread !

Cheers

Matt
This topic is locked and can not be replied to.