Forum: Ruby on Rails Teaching Models to Render Themselves in the Controller

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.
68d740cd154a35d1bb42edc855e65f31?d=identicon&s=25 Nathan Roling (Guest)
on 2006-02-14 08:59
(Received via mailing list)
I am trying to teach my models how to render themselves, i.e.

<%= my_model_object.render() %>

Let me explain my reasoning and proposed method before this gets shot
down as anti-MVC.

Let's say I am writing a contact-management application. I have a
class Contact. I will need to display this class all over the
application. My first choice is to use a partial.

Now I can render my Contact with:

<% render_partial 'contact_view', :contact => my_contact %>

However, I start doing some ajax things, where I need to render a
contact and return it with an AJAX call. So, I simply render the
partial from my controller. Not too bad, as long as I can keep
straight what partial to call and what locals I need to pass where.
However, to make things a little easier on myself, I can create a
helper method in the controller like this:

helper_method :render_contact
def render_contact(contact)
	render_partial('contact_view', :contact => my_contact)
end

I can now call this same function from my view to render the page
initially, and from my controller to render new contacts for AJAX calls.

The next step is where I've run into trouble. Instead of having all
of these methods in the controller and calling out to them whenever I
need a Contact rendered, how about sticking the methods right onto
the model itself? Of course this breaks MVC if I put it on the model
in the contact.rb file, the contact is no longer reusable in other
places. But with ruby I'm supposed to be able to dynamically extend
objects. So why not extend my contact in my controller, teaching it
to render itself? In this way, different controllers could have
different rendering code for a Contact, and the Contact class itself
is never altered, but the code on my page goes from:

<%= render_contact contact %>

to

<%= contact.render() %>

and the controller AJAX code is likewise simplified.

So that's the what and why, but for the life of me, I cannot figure
out the how. I am fairly new to ruby, and I think I'm supposed to be
able to do something like

def Contact
	def render()
		# render code goes here
	end
end

inside of the controller, but I cannot for the life of me to get it
to work. If anyone call tell me how to do something like this, or if
there's some alternative method that's just as clean, I'm all ears.

Sorry for the longwinded message, but I felt that I should try to
explain myself as clearly as possibly with something that looks on
the surface to be a blatant breach of MVC.

In case I haven't been clear, or maybe just because it's in my head
and it's late, here's a metaphor I came up with:

Imagine paratroopers getting ready to jump out of a plane. As they
file towards the front of the plane, the officer hands them a
parachute, which they then put on and jump out. On the way down they
pull their own cord and parachute to safety. This is what I am trying
to do, with the model objects being the soldiers, the officer being
the controller, and the sky being the view. The method which I am
trying to avoid is that of pushing the soldiers out of the plane, and
then sending a parachute down after them.

Thanks for even reading this far, if you have,

Ozzi
675475d0b65710be6d992eb5eb2c61c2?d=identicon&s=25 Gregory Seidman (Guest)
on 2006-02-14 15:13
(Received via mailing list)
On Tue, Feb 14, 2006 at 01:59:10AM -0600, Nathan Roling wrote:
} I am trying to teach my models how to render themselves, i.e.
}
} <%= my_model_object.render() %>
}
} Let me explain my reasoning and proposed method before this gets shot
} down as anti-MVC.
[...]

Having a model render itself would be anti-MVC in most languages, but...

} The next step is where I've run into trouble. Instead of having all
} of these methods in the controller and calling out to them whenever I
} need a Contact rendered, how about sticking the methods right onto
} the model itself? Of course this breaks MVC if I put it on the model
} in the contact.rb file, the contact is no longer reusable in other
} places. But with ruby I'm supposed to be able to dynamically extend
} objects. So why not extend my contact in my controller, teaching it
} to render itself? In this way, different controllers could have
} different rendering code for a Contact, and the Contact class itself
} is never altered, but the code on my page goes from:
[...]

...you're on the right track.

} So that's the what and why, but for the life of me, I cannot figure
} out the how. I am fairly new to ruby, and I think I'm supposed to be
} able to do something like
}
} def Contact
} 	def render()
} 		# render code goes here
} 	end
} end
}
} inside of the controller, but I cannot for the life of me to get it
} to work. If anyone call tell me how to do something like this, or if
} there's some alternative method that's just as clean, I'm all ears.
[...]

I'd say you should put them in your helper files, rather than your
controller files. The helper files are part of the view layer.

} Thanks for even reading this far, if you have,
} Ozzi
--Greg
A63764f318f10379c8b51349b757cf4b?d=identicon&s=25 Jay Levitt (Guest)
on 2006-02-14 17:53
(Received via mailing list)
On Tue, 14 Feb 2006 08:31:08 -0500, Gregory Seidman wrote:

> I'd say you should put them in your helper files, rather than your
> controller files. The helper files are part of the view layer.

Maybe the OP is saying something I was thinking last night - it'd be
nice
if helpers could provide methods to a model, so that you COULD create a
helper for contact.render() instead of render_contact(contact).  Just
some
syntactic sugar.

Jay Levitt
Cb610750ee94ca103aef4b2dc7b1b768?d=identicon&s=25 Nick Stuart (Guest)
on 2006-02-14 18:05
(Received via mailing list)
There's nothing stopping you from doing this currently. All objects in
ruby are open for modifacations, even instances of objects, not just
the class definitions. You can add a method to one instance of an
object and only that object will have it,

-Nick
675475d0b65710be6d992eb5eb2c61c2?d=identicon&s=25 Gregory Seidman (Guest)
on 2006-02-14 18:23
(Received via mailing list)
On Tue, Feb 14, 2006 at 11:40:40AM -0500, Jay Levitt wrote:
} On Tue, 14 Feb 2006 08:31:08 -0500, Gregory Seidman wrote:
}
} > I'd say you should put them in your helper files, rather than your
} > controller files. The helper files are part of the view layer.
}
} Maybe the OP is saying something I was thinking last night - it'd be
nice
} if helpers could provide methods to a model, so that you COULD create
a
} helper for contact.render() instead of render_contact(contact).  Just
some
} syntactic sugar.

It certainly can. Suppose you have controllers/foo_controller.rb,
helpers/foo_helper.rb, and models/foo.rb in your app directory. You
don't
want to put the render method in the models/foo.rb or
controllers/foo_controller.rb files because it belongs in the view
rather
than the model or controller, respectively. But if you put it in the
FooHelper module in the helpers/foo_helper.rb then it winds up in the
foo_controller class, and that's not what you want either. Instead, your
helpers/foo_helper.rb file looks like this:

module FooHelper
end

class Foo
  def render
    #...
  end
end

When you are in views/foo/*.rhtml the helpers/foo_helper.rb file has
clearly been loaded, right? So the Foo class (i.e. the model) will have
the
render method added to it. I'm not 100% certain of whether you need  to
specify the ActiveRecord::Base subclass in the helper file, but I
suspect
not. You may also want to add a require "foo" or "models/foo" or
whatever
to make sure the model has already been loaded. Play with it and see.
Let
me know how it turns out.

} Jay Levitt
--Greg
59de94a56fd2c198f33d9515d1c05961?d=identicon&s=25 Tom Mornini (Guest)
on 2006-02-14 18:29
(Received via mailing list)
On Feb 14, 2006, at 8:40 AM, Jay Levitt wrote:

> Just some
> syntactic sugar.

Why not make a dispatcher method in helper, render() that cases object
type, and either executes the valid code directly or as part of a
specific
render helper for the object type?

def show(object)
   case object.class
   when User
     # render code for User
     # or show_user(object)
   when Blah
     # render code for Blah
     # or show_blah(object)
   else
     raise :YourVoiceStridently
   end
end

Then it's just a syntax issue:

   contact.render

-vs-

   show contact

--
-- Tom Mornini
4005a47a8f2ceee49670b920593c1d52?d=identicon&s=25 Ben Munat (Guest)
on 2006-02-14 18:47
(Received via mailing list)
Tom, I enjoy your posts a lot, but I have to respectfully disagree
here... I think adding
behavior to the object to which it belongs is always the better choice
over starting a
never-ending case statement.

I hope Ozzi will try the model-extending approach that Greg suggested
and let us know how
it goes!

b
3c61994644f5d19eb104fb36de8f46da?d=identicon&s=25 Tony Collen (Guest)
on 2006-02-14 18:56
(Received via mailing list)
On 2/14/06, Nathan Roling <ozzilee@gmail.com> wrote:

Imagine paratroopers getting ready to jump out of a plane. As they
> file towards the front of the plane, the officer hands them a
> parachute, which they then put on and jump out. On the way down they
> pull their own cord and parachute to safety. This is what I am trying
> to do, with the model objects being the soldiers, the officer being
> the controller, and the sky being the view. The method which I am
> trying to avoid is that of pushing the soldiers out of the plane, and
> then sending a parachute down after them.
>

This is a good metaphor but MVC webapps is nothing like parachuting :)

Tony
59de94a56fd2c198f33d9515d1c05961?d=identicon&s=25 Tom Mornini (Guest)
on 2006-02-14 20:19
(Received via mailing list)
On Feb 14, 2006, at 9:47 AM, Ben Munat wrote:

> Tom, I enjoy your posts a lot, but I have to respectfully disagree
> here... I think adding behavior to the object to which it belongs
> is always the better choice over starting a never-ending case
> statement.
>
> I hope Ozzi will try the model-extending approach that Greg
> suggested and let us know how it goes!

Hey, I largely agree with you Ben, so don't sweat the disagreement!

It's like the choosing the worst of two evils. :-(

If the case just distributes to helper methods, then it's not such an
ugly thing...

P.S. I think Greg's extension was to be handled in application.rb. If
that's the case, I just ran into
      an issue with such things, in that those modifications WILL NOT
be available to the models when
      firing up a console, and unit tests likely won't be able to hit
them either.

--
-- Tom Mornini
675475d0b65710be6d992eb5eb2c61c2?d=identicon&s=25 Gregory Seidman (Guest)
on 2006-02-14 20:58
(Received via mailing list)
On Tue, Feb 14, 2006 at 11:18:29AM -0800, Tom Mornini wrote: [...]
} P.S. I think Greg's extension was to be handled in application.rb. If
} that's the case, I just ran into an issue with such things, in that
those
} modifications WILL NOT  be available to the models when firing up a
} console, and unit tests likely won't be able to hit  them either.

I was suggesting putting them in helpers/<controller>_helper.rb,
actually.
I'm not sure how that affects the console or unit tests, but the point
is
to make the methods available in views and nowhere else.

Now that Rails 1.0 is out and solid, it might be time to start thinking
about what should go into 1.1 (or 2.0). I'd suggest that the directories
under app should be split up more. The helpers directory should be
renamed
something clearer (controller_render_helpers, or something more concise)
and
that there should be another directory of "helpers" for models, which
would
suit this need perfectly.

Okay, I'm on a roll now. Consider the following directory structure
within
app:

controllers
controllers/render
models
models/validation
models/render
views
views/layouts

The models/render, controllers/render, and models/validation directories
contain modules which are automatically included in the associated model
or
controller as appropriate for the context. The controllers/render
directory
is essentially identical to the existing helpers directory.

Of course, this may not make sense. Please comment.

} -- Tom Mornini
--Greg
68d740cd154a35d1bb42edc855e65f31?d=identicon&s=25 Nathan Roling (Guest)
on 2006-02-14 23:03
(Received via mailing list)
First of all, thanks for all the replies. Sorry I haven't been able
to get back until now.

Greg, I like your idea, I think it'll work great. Unfortunately,
being the newbie I am, I've run into a problem I haven't quite
figured a way around.

First of all my helper class (foo.rb):

   class Foo < Foo.superclass
     def render
         "Test."
     end
   end

This seems to work great -- I can call the method in my view for
normal page rendering, and in my controller for AJAX rendering.

(I needed to add the "< Foo.superclass" because i was getting a
parent class mismatch or some such. "< ActiveRecord::Base" would work
the same, I just think this is cleaner.)

However, if I try to do:

   class Foo < Foo.superclass
     def render
         render_partial 'foo_partial'
     end
   end

I get a method not found error. Of course this makes sense, but what
is the cleanest way of including the render_partial method there? I
suspect it has something to do with blocks or closures. Could I
somehow define the render() method inside the helper method where
render_partial is available and then attach it back to the class? Or
is there a simpler way?

Like I said, I'm new at ruby, and I'm coming off doing mostly java,
so I'm not really sure about how some of these things work. I
appreciate all of the help though, thanks :-)

Ozzi
675475d0b65710be6d992eb5eb2c61c2?d=identicon&s=25 Gregory Seidman (Guest)
on 2006-02-14 23:57
(Received via mailing list)
On Tue, Feb 14, 2006 at 04:00:06PM -0600, Nathan Roling wrote:
[...]
} First of all my helper class (foo.rb):
}
}   class Foo < Foo.superclass
}     def render
}         "Test."
}     end
}   end
}
} This seems to work great -- I can call the method in my view for
} normal page rendering, and in my controller for AJAX rendering.
}
} (I needed to add the "< Foo.superclass" because i was getting a
} parent class mismatch or some such. "< ActiveRecord::Base" would work
} the same, I just think this is cleaner.)

Excellent! I thought it would work and I'm glad it did. Also, I like the
elegance of using Foo.superclass.

} However, if I try to do:
}
}   class Foo < Foo.superclass
}     def render
}         render_partial 'foo_partial'
}     end
}   end
}
} I get a method not found error. Of course this makes sense, but what
} is the cleanest way of including the render_partial method there? I
} suspect it has something to do with blocks or closures. Could I
} somehow define the render() method inside the helper method where
} render_partial is available and then attach it back to the class? Or
} is there a simpler way?
[...]

Well, render_partial is a method of the ActionController::Base class
(i.e.
the base class of all controllers). What you might try doing is making
render take an argument, and calling render with self, e.g.:

<%= @foo.render(self) %>

...and define render as:

class Foo < Foo.superclass
  def render(controller)
    controller.render_partial 'foo_partial'
  end
end

No guarantees that this will work, but it's worth a try.

} Ozzi
--Greg
50aeab2d063ed4514ad5d2d4a371e6d8?d=identicon&s=25 Jim Nicholson (jnicholson)
on 2006-02-15 00:31
Tony Collen wrote:
> On 2/14/06, Nathan Roling <ozzilee@gmail.com> wrote:
>
> Imagine paratroopers getting ready to jump out of a plane. As they
>> file towards the front of the plane, the officer hands them a
>> parachute, which they then put on and jump out. On the way down they
>> pull their own cord and parachute to safety. This is what I am trying
>> to do, with the model objects being the soldiers, the officer being
>> the controller, and the sky being the view. The method which I am
>> trying to avoid is that of pushing the soldiers out of the plane, and
>> then sending a parachute down after them.
>>
>
> This is a good metaphor but MVC webapps is nothing like parachuting :)
>
> Tony


I think the metaphor might work with some tweaking. Let's say that
rather than the officer, the *pilot* is the controller. How MVC works is
this: The pilot calls back to the soldiers and tells them to jump, then
- without checking to see if they've left the plane - he flys in a loop
back around to where he *thinks* they will be falling, and throws open
parachutes out of the plane to catch them.

:)
68d740cd154a35d1bb42edc855e65f31?d=identicon&s=25 Nathan Roling (Guest)
on 2006-02-15 00:33
(Received via mailing list)
I'll try that, unfortunately I think it kind of defeats the elegance
I'm going for in the page, i.e. <=% foo.render() %> as opposed to <%
foo.render(@controller) %> or whatever it would work out to.

I won't get a chance to play with it until tomorrow sometime, so
maybe I can build off of that into something more elegant. But if
anyone has any ideas, please, keep them coming. As always, thanks for
the help. I'll let you know what I come up with when I get a chance
to play with it.
5d15c6821f3c3054c04b85471824ba7c?d=identicon&s=25 Kevin Olbrich (Guest)
on 2006-02-15 00:48
(Received via mailing list)
On Tuesday, February 14, 2006, at 4:00 PM, Nathan Roling wrote:
>   class Foo < Foo.superclass
>     def render
>         render_partial 'foo_partial'
>     end
>   end

Try

def render
  render :partial=>'foo_partial'
end

make sure you name the file '_foo_partial'

_Kevin
68d740cd154a35d1bb42edc855e65f31?d=identicon&s=25 Nathan Roling (Guest)
on 2006-02-15 21:16
(Received via mailing list)
>   def render(controller)
>     controller.render_partial 'foo_partial'
>   end
> end

No dice. First of all, render_partial is a protected method. Trying
to get around that, I made a public method in the controller that
would call the partial, but then I get an error to the effect of only
one render being allowed at a time. So at this point I really seems
stuck to doing all of my render calls from the controller or helper.

This still leaves me with my base problem, and maybe there's an
easier answer. I want to be able to call the same method in the view
and the controller to render a block of HTML. In the view I need it
for initial page rendering, and in the controller I need it for AJAX
calls. The only way I have found to do this is by defining my method
in the controller and using ":helper_method" to export it to the
view. It works, but it just seems messy/wrong. I would rather put the
code in the helper, but there doesn't seem to be a way to call it
from the controller. Tacking the code onto the model seemed like a
good way to go, but I don't seem to be able to figure out a way to do
that either.

So for now I'm going to stick with putting my methods in the
controller and exporting them with ":helper_method", but if someone
came up with a cleaner way I would definitely like to hear it.

Thanks everyone for the help,

Ozzi
Eea7ad39737b0dbf3de38874e0a6c7d8?d=identicon&s=25 Justin Forder (Guest)
on 2006-02-18 14:54
(Received via mailing list)
Tony Collen wrote:
>     the controller, and the sky being the view. The method which I am
>     trying to avoid is that of pushing the soldiers out of the plane, and
>     then sending a parachute down after them.
>
>
> This is a good metaphor but MVC webapps is nothing like parachuting :)

This suggests wrapping the model with a renderer which has an
appropriate to_s implementation, and giving the wrapper to the view.

regards

   Justin
3c61994644f5d19eb104fb36de8f46da?d=identicon&s=25 Tony Collen (Guest)
on 2006-02-18 17:13
(Received via mailing list)
On 2/18/06, Justin Forder <justin@justinforder.me.uk> wrote:
> Tony Collen wrote:
> > This is a good metaphor but MVC webapps is nothing like parachuting :)
>
> This suggests wrapping the model with a renderer which has an
> appropriate to_s implementation, and giving the wrapper to the view.


Which brings up the question, is #to_s the appropriate place for
something to convert itself to HTML?  Perhaps #to_html would be more
correct.

Tony
4005a47a8f2ceee49670b920593c1d52?d=identicon&s=25 Ben Munat (Guest)
on 2006-02-18 19:21
(Received via mailing list)
Tony Collen wrote:
>
> Which brings up the question, is #to_s the appropriate place for
> something to convert itself to HTML?  Perhaps #to_html would be more
> correct.
>
The only problem there is what if you need different html
representations of the same
model object... like a "view" representation vs. an "edit"
representation?

Another idea -- something I've toyed with in Java -- is to "domify" the
model object. I
noticed there's an xml-mapping gem that maps Ruby objects to xml and
back. So, you map the
object to xml, and then use a variety of xslt's to create the various
html representations
you want.

The hard part there looks like doing the xslt's... there don't appear to
be any xslt gems
and the only other xslt for ruby I could find is distributed as c source
code. :-(

b
Eea7ad39737b0dbf3de38874e0a6c7d8?d=identicon&s=25 Justin Forder (Guest)
on 2006-02-18 22:37
(Received via mailing list)
Tony Collen wrote:
> On 2/18/06, Justin Forder <justin@justinforder.me.uk> wrote:
>> Tony Collen wrote:
>>> This is a good metaphor but MVC webapps is nothing like parachuting :)
>> This suggests wrapping the model with a renderer which has an
>> appropriate to_s implementation, and giving the wrapper to the view.
>
>
> Which brings up the question, is #to_s the appropriate place for
> something to convert itself to HTML?  Perhaps #to_html would be more
> correct.

Sorry if I didn't understand the context (I'm still catching up on an
800-message backlog), but I assumed that the view was ERB, in which case
to_s is what is going to be called.

regards

   Justin
1e99128ebac5ca41c91940291d315332?d=identicon&s=25 Luke Redpath (Guest)
on 2006-02-18 23:14
(Received via mailing list)
I'm not sure I see what problem you are having here.

Firstly, a model should know nothing about rendering. It is not a models
responsibility to render itself. Rendering belongs in the view, even if
you
use renderer classes to render your models. This is all about good
design
and loose coupling.

If you want to render something in a normal view and in response to an
AJAX
call, have you considered upgrading to Edge Rails and using RJS
templates?
They would seem like the perfect solution.

Cheer
Luke Redpath
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-02-18 23:57
(Received via mailing list)
On Feb 13, 2006, at 11:59 PM, Nathan Roling wrote:

> I am trying to teach my models how to render themselves, i.e.
>
> <%= my_model_object.render() %>

Bad idea.

[...]

> <% render_partial 'contact_view', :contact => my_contact %>

[why you think this is bad]

Agreed, much to clumsy.

[...]

> <%= render_contact contact %>

Good idea, but not generic enough.

I had a similar thought, a model should know its URL.  I did it this
way:

http://blog.zenspider.com/archives/2005/12/overrid...

Following that approach, it is better to teach controllers how to
render models:

<%= render :model => contact %>

Where you use inheritance (I didn't test this, but it should work
straight-up):

class ApplicationController < ActionController::Base
   def render(options = {}, deprecated_status = nil)
     if options.include? :model then
       model = options.delete :model
       model_name = model.class.name.downcase # there's some
inflector thingy for this
       defaults = {
         :partial => "partials/#{model_name}"
         model_name.intern => model
       }
       defaults = defaults.merge model.render_params if
model.respond_to? :render_params
       options = defaults.merge options
     end

     return super(options, deprecated_status)
   end
end

This way a model doesn't need to know how to render itself, the
controller can guess.  In the (hopefully very few) instances where
the model needs extra options the controller can ask the model for them.

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
68d740cd154a35d1bb42edc855e65f31?d=identicon&s=25 unknown (Guest)
on 2006-02-23 05:18
(Received via mailing list)
Once again, sorry for the long time between replies. I had actually
given up on my original idea and implemented something very much like
this. mostly inspired by Tom Mornini's idea:

>   when Blah
>     # render code for Blah
>     # or show_blah(object)
>   else
>     raise :YourVoiceStridently
>   end
> end

Instead of using a case statement, though, I implemented "show"
something like the render :model method below. My version figures out
a method name and a partial name, and tries the method first. This
would be good for more complex rendering. It also takes a parameter
"style", which just gets appended to the name of the partial/method,
to allow for different ways of rendering objects. locals is simply a
hash that gets sent to the partial (and, I suppose, I will have to
send to the method. I haven't used that yet in my project, so it's
not completely implemented)

My (ugly) code:

   def show(object, style = nil, locals = {})
     name = Inflector::underscore(object.class)
     style = '_' + Inflector::underscore(style.to_s) if !style.nil?
&& style.to_s

     method = 'render_' + name + style.to_s
     partial = '/objects/' + name + '/' + name + style.to_s

     if (self.respond_to?(method, true))
       self.send(method, object)
     else
       render :partial => 'objects/' + name + '/' + name + style.to_s,
           :locals => locals.merge({ name.to_sym => object })
     end
   end

I also implemented a somewhat similar function to show a list of
objects. This one needs to explicitly know the class to use:

   def list(collection, klass, style = nil, locals = {})
     name = Inflector::underscore(klass)
     names = Inflector::pluralize(name)
     style = '_' + Inflector::underscore(style.to_s) if !style.nil?
&& style.to_s

     method = 'render_list_' + names + style.to_s
     partial = '/objects/' + name + '/list_' + names + style.to_s

     if (self.respond_to?(method, true))
       self.send(method, collection)
     else
       render :partial => partial,
           :locals => locals.merge({ names.to_sym => collection })
     end
   end

I intend to look into your code more deeply when I get the time. I
haven't as of yet gotten my head around it fully, but I like the
syntax of

<%= render :model => model %>

and could see this going to something like

<%= render :model => model, :style => 'style', :locals => { :key =>
'value' } %>

to include everything my code does.

Thanks for the replies,

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