#101
Apr 14
Refactoring Out Helper Object
If you have complex view logic, this can easily lead to helper methods which call each other. See how to refactor this out into another object in this episode.
I failed to mention in the screencast, the 3 methods which aren’t called externally for the object should be made private. See the code below for an example.
# application_helper.rb def render_stars(rating) StarsRenderer.new(rating, self).render_stars end # helpers/stars_renderer.rb class StarsRenderer def initialize(rating, template) @rating = rating @template = template end def render_stars content_tag :div, star_images, :class => 'stars' end private def star_images (0...5).map do |position| star_image(((@rating-position)*2).round) end.join end def star_image(value) image_tag "/images/#{star_type(value)}_star.gif", :size => '15x15' end def star_type(value) if value <= 0 'empty' elsif value == 1 'half' else 'full' end end def method_missing(*args, &block) @template.send(*args, &block) end end




Another excellent episode (I see you've moved the sponsor ads to the beginning of the shows now too :( ).
I found Railscasts yesterday, and am catching up on all 101 episodes ASAP ^_^ Keep up the great work.
Have you considered a series of screencasts where you make an actual application from scratch, rather than edit an existing one. I know that for some people like myself, it would help get a grasp of which foot to start off on ;)
I know a guestbook was made in 10 minutes (there was a demo somewhere). How about building a forum ? :P I don't think there is one out there atm ^_^
Haven't rails default pagination helpers used this model?
Considering you (for example) need to render a rating for like... 100 products it would take 100 objects with their initializations, isn't that a bit too slow?
@Artūras Šlajus:
you may cache the results. so rails won't initialize the StarsRenderer each time.
Hello Ryan, thanks for this great episode. I was wondering why this StarRenderer class cannot live in lib/ directory. I use to put all my custom code in there.
Haven't tried yet but, there is a reason?
Thanks again for your time and dedication.
Pretty cool! Thanks!
Ryan, this is your finest episode to date.
More episodes like this one! Where you get into refactoring and start touching on design patterns.
Good job kid!
Thanks for another great episode Ryan. Custom classes are still a little scary for me, but this exposure helps a lot. Thanks!
One question: rather than adding the method:
def method_missing(*args, &block)
@template.send(*args, &block)
end
could you also have the new class inheret the application controller? I.e.
class StarsRenderer < ApplicationController
I'm working on my 5 tips...
Pretty good =]
I'm working on my 5 tips too lol
[]'s
Hello Ryan your screencast are really great. They helped me to get a better rails coder :-).
Keep up the great work. Have you considerd a episode of the plugin SuperInPlaceControls for inline Edition or just some more about Ajax stuff?
Thanks for all episodes i watched them all.
It should be easier to share this widgets among different rails apps...
(I mean controller, images, views)
Do you know a more plug'n'play way to share these piece of code? I dunno embedded_actions, plugins, cells?
Is this not something for which you would want to use a component. I know using rails components is considered bad. But I think this problem could very well be solved with the concept of components. It would be great if rails would get a better and fast implementation for components.
This cast does not seem to have made its way onto the iTunes Podcast RSS Feeds. Is it because you've passed the magic 100?
Check out the stencil plugin. Its basically a subclass for these types of helpers.
http://rubyforge.org/projects/stencil/
I have a question about the method_missing part. Is it best practice to design your code around exception being thrown? The code in the method_missing part isn't unknown at the time the class is written (like the find_by methods). It seems an expensive way to get content_tag instead of @template.content_tag.
Thanks for the episodes they have been really helpful
Guys, I'd like to suggest going here www.rubyheroes.com and nominating someone (hint, hint) for his outstanding tutorials, delivered weekly, about Ruby on Rails. Six people will be selected to win an award on stage next month at RailsConf (which, while in my home town, I won't be able to attend this year, boo hoo!) If anyone deserves an award, it would be Ryan, don't you think?
@Artūras, a new object is created for each rating, yes. However, I don't think this will be a problem as each object is displayed on the screen. From my understanding, the problem with the old paginator is that each page was an object, so if you have thousands of pages you end up with thousands of objects even though not every one needs to be displayed. You will likely not need to display thousands of ratings at once.
@Jorge, good point! I considered placing this in the lib directory but decided not to because of the similarity these objects have with helpers. Either way I think it's best to make a renderer directory (which I really should have) and add it there.
@bryce, the renderer isn't a controller, so I'm not sure how inheriting from ApplicationController will benefit you. It does have some similarities in how it can render views, but the controller does so much more that this should not be concerned about. Also it's best to keep view code out of the controllers, but I feel okay to put view code in a renderer.
@Ale, I recommend moving this into a plugin if you want to share it between apps. However I find it's easy to get carried away with this and make the code more abstract than it really needs to be. Keep it specific to your application until you really see duplication with another app and then refactor that into a plugin.
@Joran, for me components often have too much overhead. I'm also not sure what benefit it has over this technique.
@James, I generally try to stay away from method_missing, especially in more complex objects when dealing with inheritance. However, the renderer seems simple enough where the downsides of method_missing aren't as apparent. In the code example I gave it's not really necessary, but if you're calling helper methods frequently in a renderer it can be very convenient.
I don't see how it became less complex than it was. It becomes even more complex when you're introducing template passing to renderer initializer. I thing this is the one of worst implementations of Presenter.
Further more you're using method missing where it's absolutely not needed. That's how Rails becomes slow.