From 587dd7d56c1451c1d597535ede27c2163be72e0b Mon Sep 17 00:00:00 2001
From: Andrew Radev <andrey.radev@gmail.com>
Date: Tue, 14 Jun 2011 22:45:22 +0300
Subject: Grouped select helper

FormBuilder#select now works with a nested data structure.
---
 .../lib/action_view/helpers/form_options_helper.rb | 14 +++++++--
 .../test/template/form_options_helper_test.rb      | 36 ++++++++++++++++++++++
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 3dc6d65432..7c43dc04e0 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -105,7 +105,10 @@ module ActionView
 
       # Create a select tag and a series of contained option tags for the provided object and method.
       # The option currently held by the object will be selected, provided that the object is available.
-      # See options_for_select for the required format of the choices parameter.
+      #
+      # There are two possible formats for the choices parameter, corresponding to other helpers' output:
+      #   * A flat collection: see options_for_select
+      #   * A nested collection: see grouped_options_for_select
       #
       # Example with @post.person_id => 1:
       #   select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :include_blank => true })
@@ -575,7 +578,14 @@ module ActionView
 
       def to_select_tag(choices, options, html_options)
         selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
-        select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, html_options)
+
+        if !choices.empty? && choices.try(:first).try(:second).respond_to?(:each)
+          option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
+        else
+          option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
+        end
+
+        select_content_tag(option_tags, options, html_options)
       end
 
       def to_collection_select_tag(collection, value_method, text_method, options, html_options)
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index a4599a3f00..6aea991f7c 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -385,6 +385,42 @@ class FormOptionsHelperTest < ActionView::TestCase
     )
   end
 
+  def test_select_with_grouped_collection_as_nested_array
+    @post = Post.new
+
+    countries_by_continent = [
+      ["<Africa>", [["<South Africa>", "<sa>"], ["Somalia", "so"]]],
+      ["Europe",   [["Denmark", "dk"], ["Ireland", "ie"]]],
+    ]
+
+    assert_dom_equal(
+      [
+        %Q{<select id="post_origin" name="post[origin]"><optgroup label="&lt;Africa&gt;"><option value="&lt;sa&gt;">&lt;South Africa&gt;</option>},
+        %Q{<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk">Denmark</option>},
+        %Q{<option value="ie">Ireland</option></optgroup></select>},
+      ].join("\n"),
+      select("post", "origin", countries_by_continent)
+    )
+  end
+
+  def test_select_with_grouped_collection_as_hash
+    @post = Post.new
+
+    countries_by_continent = {
+      "<Africa>" => [["<South Africa>", "<sa>"], ["Somalia", "so"]],
+      "Europe"   => [["Denmark", "dk"], ["Ireland", "ie"]],
+    }
+
+    assert_dom_equal(
+      [
+        %Q{<select id="post_origin" name="post[origin]"><optgroup label="&lt;Africa&gt;"><option value="&lt;sa&gt;">&lt;South Africa&gt;</option>},
+        %Q{<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk">Denmark</option>},
+        %Q{<option value="ie">Ireland</option></optgroup></select>},
+      ].join("\n"),
+      select("post", "origin", countries_by_continent)
+    )
+  end
+
   def test_select_with_boolean_method
     @post = Post.new
     @post.allow_comments = false
-- 
cgit v1.2.3