Forum: Ruby on Rails Components going out of style?

F3e0b49da3f2d0b35fe6236ac90b75c5?d=identicon&s=25 Nola Stowe (Guest)
on 2006-05-24 17:28
I see in the new Agile Rails 2nd edition that components are going out
of style?

Is this so? has anybody abandoned the idea of components for helpers?

Specifically I'm considering a menu sytem for a website that depends
heavily on the users permissions to decide which menu options a user
would need to see. I thought that components, since they have the logic
in the controller and view in another file would be a good use of
components.

any input?
573b9499030e1ccb867ef80f0ff1ac49?d=identicon&s=25 Justin Bailey (Guest)
on 2006-05-24 17:35
(Received via mailing list)
On 5/24/06, Nola Stowe <nstowe@thorntontomasetti.com> wrote:
>
>
> Specifically I'm considering a menu sytem for a website that depends
> heavily on the users permissions to decide which menu options a user
> would need to see. I thought that components, since they have the logic
> in the controller and view in another file would be a good use of
> components.



That may be a good seperation of concerns but it would be really slow.
Components are performance hogs, apparently.

I've heard plugins proposed as a replacement, but I don't know how
plugins
implement views and routing. Anyone?
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 David Heinemeier Hansson (Guest)
on 2006-05-24 17:45
(Received via mailing list)
> I see in the new Agile Rails 2nd edition that components are going out
> of style?

The problem with components is that they've never actually _been_ in
style. We just forgot to tell people that. Our bad, now being
rectified.

> Is this so? has anybody abandoned the idea of components for helpers?
>
> Specifically I'm considering a menu sytem for a website that depends
> heavily on the users permissions to decide which menu options a user
> would need to see. I thought that components, since they have the logic
> in the controller and view in another file would be a good use of
> components.

I'd just make a helper that depends on an interface of a user model,
but is not bundled with it. So you rely on the fact that the user
model will have user.have_access_to?(section) or similar. Optionally,
you wrap it in a plugin that also includes a permissions include, so
you can do class User; include Permissions; end to get the methods you
need.
--
David Heinemeier Hansson
http://www.loudthinking.com -- Broadcasting Brain
http://www.basecamphq.com   -- Online project management
http://www.backpackit.com   -- Personal information manager
http://www.rubyonrails.com  -- Web-application framework
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 David Heinemeier Hansson (Guest)
on 2006-05-24 17:48
(Received via mailing list)
> I've heard plugins proposed as a replacement, but I don't know how plugins
> implement views and routing. Anyone?

In general, the position is that trying to make components with views
is not recommended. Instead, try to abstract the logic behind the
views into easy-to-use plugins such that using custom views will be
easy as pie.

If you for some reason can't come to terms with this, I believe the
engine guys are there to help you out.
--
David Heinemeier Hansson
http://www.loudthinking.com -- Broadcasting Brain
http://www.basecamphq.com   -- Online project management
http://www.backpackit.com   -- Personal information manager
http://www.rubyonrails.com  -- Web-application framework
882cc23c77c5c6d27613c51396a02a0d?d=identicon&s=25 Stephen Bartholomew (Guest)
on 2006-05-24 18:05
(Received via mailing list)
I can see the logic behind moving away from components, but seeing as
i've been using them for a while, i'd be grateful for quick example of
the alternative.

If I have a shopping basket view that is shared in 2 places - say the
basket view page and a confirm order page.  I would (normally) create a
controller action with an associated view and call it in the respective
basket/checkout views.

What would be the correct way to implement this without using the
component?

Advice and/or api/site links would be mucho appreciated :0)

Steve
5d15c6821f3c3054c04b85471824ba7c?d=identicon&s=25 Kevin Olbrich (Guest)
on 2006-05-24 18:21
(Received via mailing list)
On Wednesday, May 24, 2006, at 5:28 PM, Nola Stowe wrote:
>
>any input?
>
>--
>Posted via http://www.ruby-forum.com/.
>_______________________________________________
>Rails mailing list
>Rails@lists.rubyonrails.org
>http://lists.rubyonrails.org/mailman/listinfo/rails

I recently implemented something like this with a partial.
In my case, I am also using the user/login engine combo, so I can do a
'link_if_authorized' or 'authorized?' block around sections of the menu
html.

It seems to work reasonably well, but I haven't tested it for
performance.

_Kevin
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 David Heinemeier Hansson (Guest)
on 2006-05-24 18:27
(Received via mailing list)
> I can see the logic behind moving away from components, but seeing as
> i've been using them for a while, i'd be grateful for quick example of
> the alternative.

I'll post the code here:

  class ShopController < ActionController::Base
    before_filter :set_cart

    def index
      @products = Product.find(:all)
    end

    def buy
      @cart << Product.find(1)
      redirect_to :action => "index"
    end

    private
      def set_cart
        @cart = Cart.find(session[:cart_id])
      end
  end

...and here's the index.rhtml view:

  <h1>My Magic Shop!</h1>

  <div id="products">
    <%= render :partial => "product", :collection => @products %>
  </div>

  <div id="cart">
    <%= render :partial => "cart" %>
  </div>

The new Rails book has the full argument and justification as to why
this is the recommended approach instead of components.
--
David Heinemeier Hansson
http://www.loudthinking.com -- Broadcasting Brain
http://www.basecamphq.com   -- Online project management
http://www.backpackit.com   -- Personal information manager
http://www.rubyonrails.com  -- Web-application framework
882cc23c77c5c6d27613c51396a02a0d?d=identicon&s=25 Stephen Bartholomew (Guest)
on 2006-05-24 18:40
(Received via mailing list)
Oh right - that's much better :0)  I've been using a mix of partials and
components but now i think about it there isn't really a need to.

My romantic attachement to paper books has stopped me upgrading my
'Agile' book but it's looking more of a necessity now :0)

Cheers,

Steve
39c5254d0a798765f37ac215fa6e0fc7?d=identicon&s=25 Curtis Spendlove (cuspendlove)
on 2006-05-24 18:57
(Received via mailing list)
On 5/24/06, Stephen Bartholomew <sb@2404.co.uk> wrote:
> My romantic attachement to paper books has stopped me upgrading my
> 'Agile' book but it's looking more of a necessity now :0)

Noooooo!  That's the dark side talking to you, don't listen.  I got my
email about the update for 2nd Edition last night.  Due to my position
in the queue I had to wait an entire 16 minutes for the gerbils to
pump out my copy, but...damn...  It was worth the wait...

I'm not far enough into 2nd Edition yet, so I appreciate DHH dropping
the code here (since I've been struggling with this issue as well--in
relation to multiple apps sharing the same user tables and "user
dashboard" view).  I look forward to reading more in the book though.
:)

I see now that it would be best to render the user dashboard via a
partial that just gets dumped into the view.  Maybe even in the
layout...  It's on nearly every page...and if it shouldn't be I could
even utilize "display:none" for the user dashboard div...  Probably
not perfectly clean...but...meh...

I'm curious to see how the menu gets rendered, I would imagine that's
in the form of a partial called from the layout, but I could be wrong.
 That may be an alternative way to do it.  Perhaps some code that
checks for the existance of a hash object and renders a partial if
it's there, or skips it if not (that could even be part of the partial
too, I suppose, so you wouldn't have to recode that in every view that
*might* use it).  Faster than components, and flexible enough if it's
not needed (just ensure it's null in the controller).
882cc23c77c5c6d27613c51396a02a0d?d=identicon&s=25 Stephen Bartholomew (Guest)
on 2006-05-25 12:02
(Received via mailing list)
> Noooooo!  That's the dark side talking to you, don't listen.
Well i've got over it and just bought the PDF of the 2nd edition.  I'm
not waiting until september for the paper version :0)

Oddly enough, i can't actually find any reference to the deprecation of
components - the section on using components is still there.  The
section at the beginning about sharing the cart view across with the
checkout however is not - i'll have to have full read through i think.

I did however, realise (i think) what i should be doing.  If i want to
display a list of links pages stored in the db for example, i create a
method in app/helpers/application_helper.rb called page_menu:

application_helper.rb
...
def page_menu
   @pages = Page.find(:all)
end
...

Then i create a partial:

_page_menu.rhtml
...
<% page_menu %>
<ul>
  <% @page.each do |page| %>
   <li><%= page.url %></li>
  <% end %>
</ul>
...

This partial can then be used in other templates
...
<%= render :partial => 'page_menu' %>
...

Now this seems kinda cool because it would mean that i can use the
helper method in different partials that use the same data (say, two
cart view partials, one with form elements, the other a straight
display).

However, i don't trust my own solution so it would be great if someone
could clarify this for me :0)

Cheers

Steve
39c5254d0a798765f37ac215fa6e0fc7?d=identicon&s=25 Curtis Spendlove (cuspendlove)
on 2006-05-25 14:54
(Received via mailing list)
Stephen Bartholomew wrote:
> Well i've got over it and just bought the PDF of the 2nd edition. I'm
> not waiting until september for the paper version :0)
>
> Oddly enough, i can't actually find any reference to the deprecation
> of components - the section on using components is still there. The
> section at the beginning about sharing the cart view across with the
> checkout however is not - i'll have to have full read through i think.
I haven't actually gotten to that part in the full book yet, and maybe
part of it is still being rewritten, but in the Intro portion it does
have a "Change Log" section which highlights the new Depot application
and some of its changes. One of the bulleted portions reads:

? It uses partials, rather than components (as components seem to be
on their way out).

So... There you go! Heehee... Seriously though, I honestly expect that
the code DHH posted earlier (I think to this thread even, but my brain
may be playing tricks on me) was probably taken straight from the book.
:) I'm liking the new book so far...several code snippets seem to do
pretty much exactly what you typed in the previous message.

Also, I selfishly downloaded the code from the FamilyConnection project
(mentioned in another thread). It runs through some similar things in
the main layouts. It goes even further though. It has a custom method
"render_partials_array()" (or such) which loops through an array of
partials and renders them. This is set during various aspects of a
page's living (typically in the controller, from what I've seen) and
very elegantly (in my opinion) resolves the side-menu content depending
on the role of the current user (guest, user, admin, etc)... For anyone
confused about partials, I recommend checking it out. It seems a good
way to do it. :)

-Curtis
882cc23c77c5c6d27613c51396a02a0d?d=identicon&s=25 Stephen Bartholomew (Guest)
on 2006-05-25 15:33
(Received via mailing list)
Yeah - i've got through the partials section the new depot app.  It
doesn't address the issue i had but generally i'm loving the updates to
the 2nd edition so far - worth the re-investment :0)

The problem (with regards to the issue i had) with the example in the
depot app, is that it doesn't seem to explain how logic code can be used
in partials (not a problem for the example obviously, but it's a
sticking point if you want to do anything more complex).

For example:
You have a cart display on the cart page and the same thing repeated on
the order confirm page.  This was in the 1st Agile book implemented
using a component, but is not in the 2nd edition.

In the 2nd edition, a cart preview is implemented using a partial:

--- views/store/index
<%= render :partial => 'cart', :object => @cart %>

--- controllers/store_controller
def index
   @cart = session[:cart]
end

--- views/store/_cart.rhtml
<% @cart.items.each do |item| %>
---

This is fine for this example.  But if you try and do the 'cart
displayed on order confirm' i mentioned above, it gets a little messy:

--- views/checkout/confirm_order.rhtml / views/cart/view.rhtml
<%= render :partial => 'cart/cart' %>

--- views/basket/_cart.rhtml
<% @cart.items.each do |item| %>
---

In order for this to work, we would need to pass the @cart object to the
partial in each respective view, like this:
--- views/checkout/confirm_order.rhtml / views/cart/view.rhtml
<%= render :partial => 'cart/cart', :object => @cart %>

In order for @cart to be available in the views, it would need to be
assigned in both the CartController::view and
CheckoutController::confirm_order actions - this seems at risk of
repetition.

I've implemented it by creating a helper method that the partial uses
(mentioned in my last post) - but that seems a bit un-rails-y and i
found it had an issue when i tried to assign something from the session.

I guess you could also create a method that the controllers could use
globally to assign this kind of thing:

--- cart controller
def view
   get_cart_view
end

--- checkout controller
def confirm_order
   get_cart_view
   # other stuff
end

--- application controller
def get_cart_view
   @cart = session[:cart]
end

... but again something about that isn't right :0)

Man, that was a bit of a ramble - think i'll have to write this all up
in a difinitive tutorial when i discover the 'right' way :0)  I have a
slight feeling of 'barking up the wrong tree' though...

Cheers,

Steve
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 David Heinemeier Hansson (Guest)
on 2006-05-25 21:09
(Received via mailing list)
> In order for @cart to be available in the views, it would need to be
> assigned in both the CartController::view and
> CheckoutController::confirm_order actions - this seems at risk of
> repetition.

That's where the wonders of filters come in. Abstract to something
like set_cart, just like the code example I had showed.
--
David Heinemeier Hansson
http://www.loudthinking.com -- Broadcasting Brain
http://www.basecamphq.com   -- Online project management
http://www.backpackit.com   -- Personal information manager
http://www.rubyonrails.com  -- Web-application framework
882cc23c77c5c6d27613c51396a02a0d?d=identicon&s=25 Stephen Bartholomew (Guest)
on 2006-05-26 10:12
(Received via mailing list)
Ah right - i getcha.

Cheers,

Steve
Eea7ad39737b0dbf3de38874e0a6c7d8?d=identicon&s=25 Justin Forder (Guest)
on 2006-05-26 12:43
(Received via mailing list)
Stephen Bartholomew wrote:

> Well i've got over it and just bought the PDF of the 2nd edition.  I'm
> not waiting until september for the paper version :0)
>
> Oddly enough, i can't actually find any reference to the deprecation of
> components - the section on using components is still there.  The
> section at the beginning about sharing the cart view across with the
> checkout however is not - i'll have to have full read through i think.

Be aware that the book is being revised incrementally, so what you have
at present is a mixture of second edition pages (with red headers) and
first edition pages (with grey headers). Section 19.9, Layouts and
Controllers, hasn't been revised yet.

regards

   Justin
D5c643fe88f5974700bb15a53cf7ef29?d=identicon&s=25 Todd Huss (Guest)
on 2006-09-02 14:11
David, I understand that components are going out, but I still think
they result in cleaner code than what you're proposing here and clean
and easy to understand code is what rails is all about isn't it?

For example I personally find:

1. Extracting the set_cart out and then mixing it in to all controllers
that need it, calling it via a before_filter, and then rendering a
partial in all views that need it

to be a lot more complicated than:

2. Calling render component in the views that need the component

For example, say I want to include a database driven web poll on certain
pages. Using the approach you describe would create both controller and
view dependencies on my web poll (even though say an article controller
really has nothing to do with a web poll). From a pure code perspective,
only needing to include the poll via render component just seems so much
cleaner.

-Todd
http://gabrito.com

David Heinemeier Hansson wrote:
>> In order for @cart to be available in the views, it would need to be
>> assigned in both the CartController::view and
>> CheckoutController::confirm_order actions - this seems at risk of
>> repetition.
>
> That's where the wonders of filters come in. Abstract to something
> like set_cart, just like the code example I had showed.
> --
> David Heinemeier Hansson
> http://www.loudthinking.com -- Broadcasting Brain
> http://www.basecamphq.com   -- Online project management
> http://www.backpackit.com   -- Personal information manager
> http://www.rubyonrails.com  -- Web-application framework
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 DHH (Guest)
on 2006-09-02 18:08
(Received via mailing list)
> For example, say I want to include a database driven web poll on certain
> pages. Using the approach you describe would create both controller and
> view dependencies on my web poll (even though say an article controller
> really has nothing to do with a web poll). From a pure code perspective,
> only needing to include the poll via render component just seems so much
> cleaner.

For components where the content is completely unrelated to the rest of
the page and operates on different base objects, components of some
form may be advantageous. The cart example plays of the fact that in a
shop, the prime directive is to buy stuff. Thus, its not a tangential
concern, so its very natural that the cart is available most every
where.

And that's been the far most common use of components I've seen. Where
essential pieces of the application, which uses data that the majority
of actions operate on (or which uses data that can be fetched from an
object which should be available always, like @person.preferences or
something), are chopped up. Components unnecessarily complicates such a
design and partials are usually a better fit.

But when you truly do have a case like the web poll, which is kind of
like a portlet, then the problem with render_component is mostly one of
implementation. Sounds like Ezra is exploring a way to deal with that
doing cells and I've even had thoughts of doing something like another,
internal HTTP request.

In any case, I consider components to be a rare specialty tool. Not a
general purpose architect-my-app-after-it kind of tool.
Eea3feaacbe44706164289d068d94828?d=identicon&s=25 unknown (Guest)
on 2006-09-02 18:12
(Received via mailing list)
Todd Huss wrote:
> David, I understand that components are going out, but I still think
> they result in cleaner code than what you're proposing here and clean
> and easy to understand code is what rails is all about isn't it?
>
> For example I personally find:
>
> 1. Extracting the set_cart out and then mixing it in to all controllers
> that need it, calling it via a before_filter, and then rendering a
> partial in all views that need it

It isn't complex to use the filters...

def ApplicationController
  private
    def set_cart
      @cart =
    end
end

def CartController < ApplicationController
  before_filter :set_cart :only=>[:show]

  def show
  end
end


def CheckoutController < ApplicationController
  before_filter :set_cart :only=>[:confirm_order]

  def confirm_order
  end
end

- Peter
912436ffe7c27e25658a3f5f556a8c96?d=identicon&s=25 Adam Fields (Guest)
on 2006-09-02 19:00
(Received via mailing list)
On Sat, Sep 02, 2006 at 03:59:17PM -0000, DHH wrote:
[...]
> But when you truly do have a case like the web poll, which is kind of
> like a portlet, then the problem with render_component is mostly one of
> implementation. Sounds like Ezra is exploring a way to deal with that
> doing cells and I've even had thoughts of doing something like another,
> internal HTTP request.
>
> In any case, I consider components to be a rare specialty tool. Not a
> general purpose architect-my-app-after-it kind of tool.

Last time I investigated this, it looked like everything I wanted out
of a component could be obtained by exposing
render_component_as_string, but I couldn't figure out how to do
that.

Is there a reason that's a bad idea?

--
				- Adam

** Expert Technical Project and Business Management
**** System Performance Analysis and Architecture
****** [ http://www.adamfields.com ]

[ http://www.aquick.org/blog ] ............ Blog
[ http://www.adamfields.com/resume.html ].. Experience
[ http://www.flickr.com/photos/fields ] ... Photos
[ http://www.aquicki.com/wiki ].............Wiki
6edd67c92a1dab5eb23fed79f3c18564?d=identicon&s=25 DHH (Guest)
on 2006-09-02 19:42
(Received via mailing list)
> Last time I investigated this, it looked like everything I wanted out
> of a component could be obtained by exposing
> render_component_as_string, but I couldn't figure out how to do
> that.
>
> Is there a reason that's a bad idea?

Currently, I believe components just have some performance issues. I'd
encourage you to benchmark with and without to see whether it matters
in your case. And then be on the lookout for other/better ideas that we
could possibly do this. As well as keeping in mind that components are
not a general strategy for compartmentalizing things in Rails, unless
they have this portlet-type flavor we're discussing.
912436ffe7c27e25658a3f5f556a8c96?d=identicon&s=25 Adam Fields (Guest)
on 2006-09-02 20:22
(Received via mailing list)
On Sat, Sep 02, 2006 at 05:34:08PM -0000, DHH wrote:
[...]
> Currently, I believe components just have some performance issues. I'd
> encourage you to benchmark with and without to see whether it matters
> in your case. And then be on the lookout for other/better ideas that we
> could possibly do this. As well as keeping in mind that components are
> not a general strategy for compartmentalizing things in Rails, unless
> they have this portlet-type flavor we're discussing.

Components have had performance issues in EVERY web platform I've
used, but usually that's related to the overhead of an additional
request/ssi, instantiating the execution stack, or something related.

I suspect (but haven't dug into the code to confirm) that the right
way to approach this might be to stick a continuation somewhere in the
rendering path to save all of that setup and use it later to execute
an additional action/view pair.

--
				- Adam

** Expert Technical Project and Business Management
**** System Performance Analysis and Architecture
****** [ http://www.adamfields.com ]

[ http://www.aquick.org/blog ] ............ Blog
[ http://www.adamfields.com/resume.html ].. Experience
[ http://www.flickr.com/photos/fields ] ... Photos
[ http://www.aquicki.com/wiki ].............Wiki
2bac2854f989c741396fa1283959c876?d=identicon&s=25 Frank (Guest)
on 2007-04-27 15:58
I understand why partials should be used instead of components, but how
can you set clever instance variables that will be used by the partial?

The thing is, I prefer not putting business logic into RHTML

Basically, instead of writing the following in a view :

<% if(@product.categories.includes?(some_category)) %>
<div>blablabla</div>
<% end %>

I prefer to keep the view clueless about the model relationships and
write something like that instead :

<% if(@display_some_section) %>
<div>blablabla</div>
<% end %>

With components, you can execute logic and create any instance variables
you need before the view gets rendered. That's what I liked about them.
Is there a way to do the same with partials?

By the way, what's your take about writing code like
"if(@product.categories.includes?(some_category))" in a view? Do you
think it is bad design or is it ok as long as you don't access the model
directly (Product.find(...)) ?
8310c5a7c769345114597bcdef111488?d=identicon&s=25 Ben Munat (Guest)
on 2007-04-27 19:56
(Received via mailing list)
You can pass vars to a partial using a render option called :locals
which points to a hash of names/values to be available as local vars in
the partial.

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

b
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.