Teaching Models to Render Themselves in the Controller

Tony C. wrote:

On 2/18/06, Justin F. [email protected] wrote:

Tony C. wrote:

This is a good metaphor but MVC webapps is nothing like parachuting :slight_smile:
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

On Feb 13, 2006, at 11:59 PM, Nathan R. 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/overriding_url.html

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 H. - [email protected] - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

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 M.'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