Hi, I have a class called Outlet which has_many Jacks. Apart from internal id, each jack is unique by the attribute ''color''. I''d like to iterate through each Outlet, and be able to output data for a Jack of a certain color. Say, I want to access the orange jack for the current outlet: <td><%=h outlet.jack.color[''O''].ip_address %></td> undefined method `color'' for Jack:Class But ''color'' is an attribute/column in the Jack Class. I can do a for loop inside an Outlet loop, but I want to dynamically refer to each Jack inside Outlet by the color attribute of Jack. Is this possible? -- - Nic
has_many associations are pluralized, and I think you have the Ruby syntax a bit wonky: outlet.jacks.detect { |j| j.color == ''O'' }.ip_address Even better to add a method to Jack: def is_orange? # assumes color == ''O'' means orange color == ''O'' end Then: outlet.jacks.detect { |j| j.is_orange? }.ip_address Models should know about themselves, and be able to answer for themselves. Better to hide the implementation within the model. Better yet would be to make the addition to Jack as described, and add this to Outlet: def orange_jack jacks.detect { |j| j.is_orange } end and in the view: outlet.orange_jack.ip_address If you abstract your code this way, you''ll weep with joy when you revisit it a few months or years from now. And, anybody else who has to look at it will be similarly enthralled. :-) -- -- Tom Mornini On Feb 10, 2006, at 12:26 PM, Nic Werner wrote:> I have a class called Outlet which has_many Jacks. Apart from internal > id, each jack is unique by the attribute ''color''. > > I''d like to iterate through each Outlet, and be able to output data > for a Jack of a certain color. > > Say, I want to access the orange jack for the current outlet: > > <td><%=h outlet.jack.color[''O''].ip_address %></td> > > undefined method `color'' for Jack:Class > > But ''color'' is an attribute/column in the Jack Class. > > I can do a for loop inside an Outlet loop, but I want to dynamically > refer to each Jack inside Outlet by the color attribute of Jack. > > Is this possible? > > -- > - Nic > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails
Wow, thats beautiful, thanks for the help! I''m going to change some of my code now. One more thing: How would I handle dynamic jack colors? I have outlets that contain n jacks with n colors. I wouldn''t be able to refer to ''orange_jack'', but I would want to display/loop an outlet with all available jacks and their respective colors. My solution would be to take what you''ve done above, but create a more generic method, and try to pass it the color value...doesn''t seem clean though. - Nic. On 2/10/06, Tom Mornini <tmornini@infomania.com> wrote:> has_many associations are pluralized, and I think > you have the Ruby syntax a bit wonky: > > outlet.jacks.detect { |j| j.color == ''O'' }.ip_address > > Even better to add a method to Jack: > > def is_orange? # assumes color == ''O'' means orange > color == ''O'' > end > > Then: outlet.jacks.detect { |j| j.is_orange? }.ip_address > > Models should know about themselves, and be able to answer > for themselves. Better to hide the implementation within > the model. > > Better yet would be to make the addition to Jack as described, > and add this to Outlet: > > def orange_jack > jacks.detect { |j| j.is_orange } > end > > and in the view: > > outlet.orange_jack.ip_address > > If you abstract your code this way, you''ll weep with joy when > you revisit it a few months or years from now. And, anybody > else who has to look at it will be similarly enthralled. :-) > > -- > -- Tom Mornini > > On Feb 10, 2006, at 12:26 PM, Nic Werner wrote: > > > I have a class called Outlet which has_many Jacks. Apart from internal > > id, each jack is unique by the attribute ''color''. > > > > I''d like to iterate through each Outlet, and be able to output data > > for a Jack of a certain color. > > > > Say, I want to access the orange jack for the current outlet: > > > > <td><%=h outlet.jack.color[''O''].ip_address %></td> > > > > undefined method `color'' for Jack:Class > > > > But ''color'' is an attribute/column in the Jack Class. > > > > I can do a for loop inside an Outlet loop, but I want to dynamically > > refer to each Jack inside Outlet by the color attribute of Jack. > > > > Is this possible? > > > > -- > > - Nic > > _______________________________________________ > > Rails mailing list > > Rails@lists.rubyonrails.org > > http://lists.rubyonrails.org/mailman/listinfo/rails > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- - Nic
On Feb 10, 2006, at 5:28 PM, Nic Werner wrote:> Wow, thats beautiful, thanks for the help! I''m going to change some of > my code now.No problem. If you call my code beautiful, I''ll help anytime. :-)> One more thing: How would I handle dynamic jack colors? I have outlets > that contain n jacks with n colors.The Right(tm) way to do this would be to have a separate table and model with the colors in it...> I wouldn''t be able to refer to ''orange_jack'', but I would want to > display/loop an outlet with all available jacks and their respective > colors.outlet.jacks.each { |j| j.color.name }> My solution would be to take what you''ve done above, but create a more > generic method, and try to pass it the color value...doesn''t seem > clean though.If there are a fixed number of colors, just create a method for each one... But...why pass in a color if you just want to list all available? Perhaps for Jack#is_orange? replacement? With tables as suggested above, like this: outlet.jacks.first.is_color?(Color.find_by_name(''orange'')) in Jacks: def is_color?(match_color) color.name == match_color.name end There is advanced (intermediate?) Ruby that I''m not 100% comfortable with yet that allows trapping undefined method calls that would allow automatic handling of: is_*? method calls dynamically. Anyone care to enlighten us both? :-) -- -- Tom Mornini
Hi -- On Fri, 10 Feb 2006, Tom Mornini wrote:> With tables as suggested above, like this: > > outlet.jacks.first.is_color?(Color.find_by_name(''orange'')) > > in Jacks: > > def is_color?(match_color) > color.name == match_color.name > end > > There is advanced (intermediate?) Ruby that I''m not 100% comfortable > with yet that allows trapping undefined method calls that would allow > automatic handling of: > > is_*? method calls dynamically. > > Anyone care to enlighten us both? :-)The trapping way would involve method_missing, but that''s inadvisable with an ActiveRecord::Base subclass, since method_missing is already in use. Another approach would be to have the Jack class create the methods dynamically, based on what it could find out about the available colors -- something like: class Jack Color.find(:all).each do |c| define_method("is_#{c.name}?") { color.name == c.name } end # other code.... end David -- David A. Black (dblack@wobblini.net) Ruby Power and Light (http://www.rubypowerandlight.com) "Ruby for Rails" chapters now available from Manning Early Access Program! http://www.manning.com/books/black
On Feb 10, 2006, at 6:27 PM, dblack@wobblini.net wrote:> Hi -- > > On Fri, 10 Feb 2006, Tom Mornini wrote: > >> With tables as suggested above, like this: >> >> outlet.jacks.first.is_color?(Color.find_by_name(''orange'')) >> >> in Jacks: >> >> def is_color?(match_color) >> color.name == match_color.name >> end >> >> There is advanced (intermediate?) Ruby that I''m not 100% comfortable >> with yet that allows trapping undefined method calls that would allow >> automatic handling of: >> >> is_*? method calls dynamically. >> >> Anyone care to enlighten us both? :-) > > The trapping way would involve method_missing, but that''s inadvisable > with an ActiveRecord::Base subclass, since method_missing is already > in use.Ah... Could you chain them with super? i.e. (pseudo code to follow) def method_missing begin super # let ActiveRecord take a shot rescue do local method missing magic end end> Another approach would be to have the Jack class create the methods > dynamically, based on what it could find out about the available > colors -- something like: > > class Jack > Color.find(:all).each do |c| > define_method("is_#{c.name}?") { color.name == c.name } > end > > # other code.... > endThat works too. Thanks a bunch! -- -- Tom Mornini
Hi -- On Fri, 10 Feb 2006, Tom Mornini wrote:> On Feb 10, 2006, at 6:27 PM, dblack@wobblini.net wrote: > >> Hi -- >> >> On Fri, 10 Feb 2006, Tom Mornini wrote: >> >>> With tables as suggested above, like this: >>> >>> outlet.jacks.first.is_color?(Color.find_by_name(''orange'')) >>> >>> in Jacks: >>> >>> def is_color?(match_color) >>> color.name == match_color.name >>> end >>> >>> There is advanced (intermediate?) Ruby that I''m not 100% comfortable >>> with yet that allows trapping undefined method calls that would allow >>> automatic handling of: >>> >>> is_*? method calls dynamically. >>> >>> Anyone care to enlighten us both? :-) >> >> The trapping way would involve method_missing, but that''s inadvisable >> with an ActiveRecord::Base subclass, since method_missing is already >> in use. > > Ah... > > Could you chain them with super? i.e. (pseudo code to follow) > > def method_missing > begin > super # let ActiveRecord take a shot > rescue > do local method missing magic > end > endYes, you could definitely add to the chain. (I''m willing to admit that there''s an element of sheer superstition to my avoidance of collaborating on method_missing with AR :-) You''d have to rescue NoMethodError specifically, and I can''t quite puzzle through whether there would still be a danger of over-eager rescuing.... Also, if you have a lot of things you want to trap, the local method_missing could get unwieldy (which is one reason I tend not to reach for method_missing as a general solution to missing methods, though I probably should). But that probably wouldn''t happen too often. David -- David A. Black (dblack@wobblini.net) Ruby Power and Light (http://www.rubypowerandlight.com) "Ruby for Rails" chapters now available from Manning Early Access Program! http://www.manning.com/books/black
dblack@wobblini.net wrote:> The trapping way would involve method_missing, but that''s inadvisable > with an ActiveRecord::Base subclass, since method_missing is already > in use. > > Another approach would be to have the Jack class create the methods > dynamically, based on what it could find out about the available > colors -- something like: > > class Jack > Color.find(:all).each do |c| > define_method("is_#{c.name}?") { color.name == c.name } > end > > # other code.... > end >Curious... when would the db call get made? Only when the class is first loaded? Everytime one of the dynamically defined methods is called? very cool though... b
On Feb 10, 2006, at 10:12 PM, Ben Munat wrote:> dblack@wobblini.net wrote: >> The trapping way would involve method_missing, but that''s inadvisable >> with an ActiveRecord::Base subclass, since method_missing is already >> in use. >> Another approach would be to have the Jack class create the methods >> dynamically, based on what it could find out about the available >> colors -- something like: >> class Jack >> Color.find(:all).each do |c| >> define_method("is_#{c.name}?") { color.name == c.name } >> end >> # other code.... >> end > > Curious... when would the db call get made? Only when the class is > first loaded? Everytime one of the dynamically defined methods is > called?Just when the class is loaded. But...even if it was every time, worrying about before there was a problem is worrying about the wrong thing...unless you''re getting paid based upon how many requests per second the system can handle. -- -- Tom Mornini
> The Right(tm) way to do this would be to have a separate table and > model > with the colors in it...<big long discussion about abstracting small static collections into db tables> I would respectfully disagree in this case. Putting these types of constants in tables is a good time to think YAGNI. You want to ask, over the lifetime of such an activity, how many times would a user need to add a new color or edit a color? Almost certainly less than 10^2. And when they do, they are probably going to feel frustrated by the fact they have to go use an editor to add a new color in order to add a jack with a new color. And if you hide the process of creating the Color objects by creating them automatically from whatever string they type in, you''ve defeated the purpose. Opposed to that you have the likely practical issue that the constants will be used many places, and using them as db objects directly becomes a performance issue and they have to be cached. Now you have to think about / implement a caching system. Then, when you''ve got it all working, the most likely change is not that the classification system changes in a way that your abstraction helps you deal with productively, but instead that the classification system changes altogether in a way with respect to which your abstractions are MORE brittle than just using a varchar column on the object. All you need are some rudimentary mechanisms for keeping the classifications consistent. Rule of thumb that works for me: postpone abstracting collections of fewer than 20 or so things that are adjectives rather than nouns and/ or that the user rarely CRUDs. At least until the app is mature enough that you can count on the abstractions having some longevity, or complex enough that the abstractions save you now time (as opposed to future time). This was a very hard lesson for me to learn, as I''m by nature one of those abstract everything kind of programmers. Michael Johnston
Well, you''re right about the lack of changes. What happens is that here are commonly three outlets, that need to be presented in a certain order (B,O,G), but due to reality, not every jack has one of those colors, or the schema may change. I was thinking of having an Add New Jack function, whereupon the user can select no more than 12 colors - you wouldn''t have any more shades because then the jacks would look too similiar. Right now I just have the Jack with a ''color'' attribute which is the ''O'' or ''G'' and an html_color column. The ''color'' column is needed because the outlets full name has the color at the end, ie 032-B. Thanks everyone for the help, I''m still sorting out the responses, but now that we''re this far, the overall goal would be to have dynamic colors, dynamically refer them, but be able to set preferences on the order of the listing, ie B, O, G - not necessarily ascending or descending. - Nic. On 2/11/06, Michael Johnston <lastobelus@mac.com> wrote:> > The Right(tm) way to do this would be to have a separate table and > > model > > with the colors in it... > > <big long discussion about abstracting small static collections into > db tables> > > I would respectfully disagree in this case. Putting these types of > constants in tables is a good time to think YAGNI. > > You want to ask, over the lifetime of such an activity, how many > times would a user need to add a new color or edit a color? Almost > certainly less than 10^2. And when they do, they are probably going > to feel frustrated by the fact they have to go use an editor to add a > new color in order to add a jack with a new color. And if you hide > the process of creating the Color objects by creating them > automatically from whatever string they type in, you''ve defeated the > purpose. > > Opposed to that you have the likely practical issue that the > constants will be used many places, and using them as db objects > directly becomes a performance issue and they have to be cached. Now > you have to think about / implement a caching system. > > Then, when you''ve got it all working, the most likely change is not > that the classification system changes in a way that your abstraction > helps you deal with productively, but instead that the classification > system changes altogether in a way with respect to which your > abstractions are MORE brittle than just using a varchar column on the > object. All you need are some rudimentary mechanisms for keeping the > classifications consistent. > > Rule of thumb that works for me: postpone abstracting collections of > fewer than 20 or so things that are adjectives rather than nouns and/ > or that the user rarely CRUDs. At least until the app is mature > enough that you can count on the abstractions having some longevity, > or complex enough that the abstractions save you now time (as opposed > to future time). > > This was a very hard lesson for me to learn, as I''m by nature one of > those abstract everything kind of programmers. > > Michael Johnston > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- - Nic
Very cool. So say the user wants to have the ''orange'' appear first, and ''blue'' second. How would I insert the color name to call the is_<color here>? method? Same way you defined it? Pretending I have a array of colors (user_color) that indicates the order of colors to be listed, would I do: is_${user_color[0]]}? - Nic. On 2/11/06, Tom Mornini <tmornini@infomania.com> wrote:> On Feb 10, 2006, at 10:12 PM, Ben Munat wrote: > > > dblack@wobblini.net wrote: > >> The trapping way would involve method_missing, but that''s inadvisable > >> with an ActiveRecord::Base subclass, since method_missing is already > >> in use. > >> Another approach would be to have the Jack class create the methods > >> dynamically, based on what it could find out about the available > >> colors -- something like: > >> class Jack > >> Color.find(:all).each do |c| > >> define_method("is_#{c.name}?") { color.name == c.name } > >> end > >> # other code.... > >> end > > > > Curious... when would the db call get made? Only when the class is > > first loaded? Everytime one of the dynamically defined methods is > > called? > > Just when the class is loaded. > > But...even if it was every time, worrying about before there was a > problem is worrying about the wrong thing...unless you''re getting > paid based upon how many requests per second the system can handle. > > -- > -- Tom Mornini > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- - Nic
Hi -- On Sun, 12 Feb 2006, Nic Werner wrote:> Very cool. So say the user wants to have the ''orange'' appear first, > and ''blue'' second. How would I insert the color name to call the > is_<color here>? method? > > Same way you defined it? Pretending I have a array of colors > (user_color) that indicates the order of colors to be listed, would I > do: > > is_${user_color[0]]}?Not in Ruby :-) But I know what you mean: jack.send("is_#{user_color[0]}?") But you can get Ruby to do the sorting automatically for you. Let''s say, to keep it simple, you have the colors in the right order stored as strings (rather than Color objects). If you want to sort an array of jacks according to their color order, you could do: sorted_jacks = jacks.sort_by {|j| colors.index(j.color) } If you have duplicate-colored jacks, or if there''s a color not represented among the jacks, you''d have to do a little more... but something like that should work. You could also do it the other way around: sorted_jacks = colors.map {|c| jacks.find {|j| j.color == c } } or some variation on that. David> > > - Nic. > > > On 2/11/06, Tom Mornini <tmornini@infomania.com> wrote: >> On Feb 10, 2006, at 10:12 PM, Ben Munat wrote: >> >>> dblack@wobblini.net wrote: >>>> The trapping way would involve method_missing, but that''s inadvisable >>>> with an ActiveRecord::Base subclass, since method_missing is already >>>> in use. >>>> Another approach would be to have the Jack class create the methods >>>> dynamically, based on what it could find out about the available >>>> colors -- something like: >>>> class Jack >>>> Color.find(:all).each do |c| >>>> define_method("is_#{c.name}?") { color.name == c.name } >>>> end >>>> # other code.... >>>> end >>> >>> Curious... when would the db call get made? Only when the class is >>> first loaded? Everytime one of the dynamically defined methods is >>> called? >> >> Just when the class is loaded. >> >> But...even if it was every time, worrying about before there was a >> problem is worrying about the wrong thing...unless you''re getting >> paid based upon how many requests per second the system can handle. >> >> -- >> -- Tom Mornini >> >> _______________________________________________ >> Rails mailing list >> Rails@lists.rubyonrails.org >> http://lists.rubyonrails.org/mailman/listinfo/rails >> > > > -- > - Nic > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- David A. Black (dblack@wobblini.net) Ruby Power and Light (http://www.rubypowerandlight.com) "Ruby for Rails" chapters now available from Manning Early Access Program! http://www.manning.com/books/black