Skip to content
This repository
Browse code

Introduce grouped_collection_select helper.

[#1249 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
  • Loading branch information...
commit 8c32248acbd71f7906a037fad499e2f8cae61bed 1 parent 7d16e94
authored jeremy committed
2  actionpack/CHANGELOG
... ...
@@ -1,5 +1,7 @@
1 1
 *Edge*
2 2
 
  3
+* Introduce grouped_collection_select helper.  #1249 [Dan Codeape, Erik Ostrom]
  4
+
3 5
 * Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs]
4 6
 
5 7
 * Ruby 1.9: fix Content-Length for multibyte send_data streaming.  #2661 [Sava Chankov]
67  actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -162,6 +162,60 @@ def collection_select(object, method, collection, value_method, text_method, opt
162 162
         InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
163 163
       end
164 164
 
  165
+
  166
+      # Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
  167
+      # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
  168
+      # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
  169
+      # or <tt>:include_blank</tt> in the +options+ hash.
  170
+      #
  171
+      # Parameters:
  172
+      # * +object+ - The instance of the class to be used for the select tag
  173
+      # * +method+ - The attribute of +object+ corresponding to the select tag
  174
+      # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
  175
+      # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
  176
+      #   array of child objects representing the <tt><option></tt> tags.
  177
+      # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
  178
+      #   string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
  179
+      # * +option_key_method+ - The name of a method which, when called on a child object of a member of
  180
+      #   +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
  181
+      # * +option_value_method+ - The name of a method which, when called on a child object of a member of
  182
+      #   +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
  183
+      #
  184
+      # Example object structure for use with this method:
  185
+      #   class Continent < ActiveRecord::Base
  186
+      #     has_many :countries
  187
+      #     # attribs: id, name
  188
+      #   end
  189
+      #   class Country < ActiveRecord::Base
  190
+      #     belongs_to :continent
  191
+      #     # attribs: id, name, continent_id
  192
+      #   end
  193
+      #   class City < ActiveRecord::Base
  194
+      #     belongs_to :country
  195
+      #     # attribs: id, name, country_id
  196
+      #   end
  197
+      #
  198
+      # Sample usage:
  199
+      #   grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
  200
+      #
  201
+      # Possible output:
  202
+      #   <select name="city[country_id]">
  203
+      #     <optgroup label="Africa">
  204
+      #       <option value="1">South Africa</option>
  205
+      #       <option value="3">Somalia</option>
  206
+      #     </optgroup>
  207
+      #     <optgroup label="Europe">
  208
+      #       <option value="7" selected="selected">Denmark</option>
  209
+      #       <option value="2">Ireland</option>
  210
+      #     </optgroup>
  211
+      #   </select>
  212
+      #
  213
+      def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
  214
+        InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
  215
+      end
  216
+
  217
+
  218
+
165 219
       # Return select and option tags for the given object and method, using
166 220
       # #time_zone_options_for_select to generate the list of option tags.
167 221
       #
@@ -490,6 +544,15 @@ def to_collection_select_tag(collection, value_method, text_method, options, htm
490 544
         )
491 545
       end
492 546
 
  547
+      def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
  548
+        html_options = html_options.stringify_keys
  549
+        add_default_name_and_id(html_options)
  550
+        value = value(object)
  551
+        content_tag(
  552
+          "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
  553
+        )
  554
+      end
  555
+
493 556
       def to_time_zone_select_tag(priority_zones, options, html_options)
494 557
         html_options = html_options.stringify_keys
495 558
         add_default_name_and_id(html_options)
@@ -524,6 +587,10 @@ def collection_select(method, collection, value_method, text_method, options = {
524 587
         @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
525 588
       end
526 589
 
  590
+      def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
  591
+        @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
  592
+      end
  593
+
527 594
       def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
528 595
         @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
529 596
       end
34  actionpack/test/template/form_options_helper_test.rb
@@ -763,6 +763,40 @@ def test_time_zone_select_with_default_time_zone_and_value
763 763
                    html
764 764
   end
765 765
 
  766
+  def test_grouped_collection_select
  767
+    @continents = [
  768
+      Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
  769
+      Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
  770
+    ]
  771
+
  772
+    @post = Post.new
  773
+    @post.origin = 'dk'
  774
+
  775
+    assert_dom_equal(
  776
+      %Q{<select id="post_origin" name="post[origin]"><optgroup label="&lt;Africa&gt;"><option value="&lt;sa&gt;">&lt;South Africa&gt;</option>\n<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk" selected="selected">Denmark</option>\n<option value="ie">Ireland</option></optgroup></select>},
  777
+      grouped_collection_select("post", "origin", @continents, :countries, :continent_name, :country_id, :country_name)
  778
+    )
  779
+  end
  780
+
  781
+  def test_grouped_collection_select_under_fields_for
  782
+    @continents = [
  783
+      Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
  784
+      Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
  785
+    ]
  786
+
  787
+    @post = Post.new
  788
+    @post.origin = 'dk'
  789
+
  790
+    fields_for :post, @post do |f|
  791
+      concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name)
  792
+    end
  793
+
  794
+    assert_dom_equal(
  795
+      %Q{<select id="post_origin" name="post[origin]"><optgroup label="&lt;Africa&gt;"><option value="&lt;sa&gt;">&lt;South Africa&gt;</option>\n<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk" selected="selected">Denmark</option>\n<option value="ie">Ireland</option></optgroup></select>},
  796
+      output_buffer
  797
+    )
  798
+  end
  799
+
766 800
   private
767 801
 
768 802
     def dummy_posts

1 note on commit 8c32248

Iain Hecker

cool! thanks!

Damien Le Berrigaud

I think this should be name option_value_method

Damien Le Berrigaud

I think this should be named option_name_method

Please sign in to comment.
Something went wrong with that request. Please try again.