<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1883082014470911865</id><updated>2011-04-21T22:49:37.400+02:00</updated><category term='acts_as_tree'/><category term='rails'/><title type='text'>Swirling Brain</title><subtitle type='html'>development stuff coming out of my brain.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://swirlingbrain.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1883082014470911865/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://swirlingbrain.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Pascal Hurni</name><uri>http://www.blogger.com/profile/06075299317261241700</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1883082014470911865.post-5491915108032941933</id><published>2007-10-05T12:19:00.000+02:00</published><updated>2007-10-05T14:12:39.066+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='acts_as_tree'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>select tags for acts_as_tree</title><content type='html'>Like many of you, I came accross the issue of having a select tag used in an edit or new form for a model that acts_as_tree. The common models are a category that belongs to a product. The category is chosen in a tree of categories.&lt;br /&gt;&lt;br /&gt;After some search, I found some entries in the Rails wiki like &lt;a href="http://wiki.rubyonrails.org/rails/pages/HowToUseActsAsTree"&gt;HowToUseActsAsTree&lt;/a&gt; and its brothers. I wasn't really happy with them. I already put my &lt;span style="font-family:courier new;"&gt;options_from_collection_for_select&lt;/span&gt; in the views so I wanted to replace it easily.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The helpers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I divided the problem and made a first function that will construct the array of items (an ID and a name)  that can be passed to &lt;span style="font-family:courier new;"&gt;options_for_select&lt;/span&gt;. This adds some flexibility so that you can wrap your own variation of options_for_select. This function is named &lt;span style="font-family:courier new;"&gt;options_from_tree&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;It is resonsible of formatting the names with correct indentation and order. Here it is:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;def options_from_tree(roots, value_method, text_method, initial_options = nil)&lt;br /&gt;  sub_items = lambda do |items, depth|&lt;br /&gt;    items.inject([]) do |options, item|&lt;br /&gt;      options &lt;&lt; [block_given? ? yield(item, depth).to_s : (" "*depth + item.send(text_method)), item.send(value_method)]&lt;br /&gt;      options += sub_items.call(item.children, depth+1)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  (initial_options || []) + sub_items.call(roots, 0)&lt;br /&gt;end&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The one-liner version (copy and paste directly into your code)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;code&gt;&lt;span style="font-size:100%;"&gt;def options_from_tree(roots, value_method, text_method, initial_options = nil)&lt;br /&gt;  sub_items = lambda {|items, depth| items.inject([]) {|options, item| options &lt;&lt; [block_given? ? yield(item, depth).to_s : (" "*depth + item.send(text_method)), item.send(value_method)]; options += sub_items.call(item.children, depth+1) }}&lt;br /&gt;  (initial_options || []) + sub_items.call(roots, 0)&lt;br /&gt;end&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The second function is a helper wrapping the previous one and acting like &lt;span style="font-family: courier new;"&gt;options_from_collection_for_select&lt;/span&gt;, I called it &lt;span style="font-family: courier new;"&gt;options_from_tree_for_select&lt;/span&gt;. Here it is:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# _roots_ is a collection of root items that will be traversed&lt;br /&gt;# other params are the same as options_from_collection_for_select&lt;br /&gt;# _initial_options_ is a collection of options added in front of the result (for the format see options_for_select)&lt;br /&gt;def options_from_tree_for_select(roots, value_method, text_method, selected_value = nil, initial_options = nil)&lt;br /&gt;  options_for_select(options_from_tree(roots, value_method, text_method, initial_options), selected_value)&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Because I prefer spaces to indent the content of the select tag and because they are coded as HTML entities, they got escaped by &lt;span style="font-family: courier new;"&gt;options_for_select&lt;/span&gt;. So to workaround I used a not so known rails helper, take a look at that:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# override options_for_select to fix any double escaped HTML entities&lt;br /&gt;def options_for_select(*args)&lt;br /&gt;  fix_double_escape(super(*args))&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Neat isn't it?&lt;br /&gt;&lt;br /&gt;All of this code is to be put in a helper, you may of course put it in &lt;span style="font-family: courier new;"&gt;application_helper.rb&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The view&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now to generate such a select tag in your views, put something like that:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;select name="product[category_id]"&amp;gt;&lt;br /&gt;  &amp;lt;%= options_from_tree_for_select(Category.find_roots, :id, :name) %&amp;gt;&lt;br /&gt;&amp;lt;/select&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice the find_roots method on the Category model, it simply &lt;code&gt;find(:all, :conditions =&gt; 'parent_id IS NULL')&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;'Cerise sur le gâteau'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I saw some of you, why the heck is there a call to &lt;span style="font-family: courier new;"&gt;block_given?&lt;/span&gt; in the &lt;span style="font-family: courier new;"&gt;options_from_tree&lt;/span&gt; code?&lt;br /&gt;&lt;br /&gt;This adds flexibility in &lt;span style="font-style: italic;"&gt;how&lt;/span&gt; the items are output in the select tag. Some of you may dislike the indentation made of spaces, you'd prefer arrows. Or maybe you want to add each parent as in:&lt;br /&gt;&lt;br /&gt;Meat&lt;br /&gt;Meat-&gt;Poultry&lt;br /&gt;Meat-&gt;Poultry-&gt;Chicken&lt;br /&gt;&lt;br /&gt;So how to do that? simply pass a block to &lt;span style="font-family: courier new;"&gt;options_from_tree&lt;/span&gt; or to &lt;span style="font-family: courier new;"&gt;options_from_tree_for_select&lt;/span&gt; which will receive the item name and the depth as argument, create your own string and return it. Example:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;options_from_tree_for_select(Category.find_all_by_parent_id(nil), :id, nil) do |item, depth|&lt;br /&gt;  (item.ancestors.reverse &lt;&lt;&gt;")&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt; &lt;br /&gt;That's all folks, feel free to comment !&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1883082014470911865-5491915108032941933?l=swirlingbrain.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://swirlingbrain.blogspot.com/feeds/5491915108032941933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1883082014470911865&amp;postID=5491915108032941933' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1883082014470911865/posts/default/5491915108032941933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1883082014470911865/posts/default/5491915108032941933'/><link rel='alternate' type='text/html' href='http://swirlingbrain.blogspot.com/2007/10/select-tags-for-actsastree.html' title='select tags for acts_as_tree'/><author><name>Pascal Hurni</name><uri>http://www.blogger.com/profile/06075299317261241700</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1883082014470911865.post-6512603875460807321</id><published>2007-10-05T12:06:00.000+02:00</published><updated>2007-10-05T12:09:43.375+02:00</updated><title type='text'>Welcome aboard</title><content type='html'>Now is the time. You've just entered in my mad brain or at least some of its output.&lt;br /&gt;&lt;br /&gt;This blog will concentrate on IT development and these time I'm working with Rails. So expect some code and ideas.&lt;br /&gt;&lt;br /&gt;Have a good trip.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1883082014470911865-6512603875460807321?l=swirlingbrain.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://swirlingbrain.blogspot.com/feeds/6512603875460807321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1883082014470911865&amp;postID=6512603875460807321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1883082014470911865/posts/default/6512603875460807321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1883082014470911865/posts/default/6512603875460807321'/><link rel='alternate' type='text/html' href='http://swirlingbrain.blogspot.com/2007/10/welcome-aboard.html' title='Welcome aboard'/><author><name>Pascal Hurni</name><uri>http://www.blogger.com/profile/06075299317261241700</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
