formtastic 1.2.4 → 3.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'readonly option' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe "placeholder text" do
14
+ [:email, :number, :password, :phone, :search, :string, :url, :text, :date_picker, :time_picker, :datetime_picker].each do |type|
15
+
16
+ describe "for #{type} inputs" do
17
+
18
+ describe "when readonly is found in input_html" do
19
+ it "sets readonly attribute" do
20
+ concat(semantic_form_for(@new_post) do |builder|
21
+ concat(builder.input(:title, :as => type, input_html: {readonly: true}))
22
+ end)
23
+ output_buffer.should have_tag((type == :text ? 'textarea' : 'input') + '[@readonly]')
24
+ end
25
+ end
26
+
27
+ describe "when readonly not found in input_html" do
28
+ describe "when column is not readonly attribute" do
29
+ it "doesn't set readonly attribute" do
30
+ concat(semantic_form_for(@new_post) do |builder|
31
+ concat(builder.input(:title, :as => type))
32
+ end)
33
+ output_buffer.should_not have_tag((type == :text ? 'textarea' : 'input') + '[@readonly]')
34
+ end
35
+ end
36
+ describe "when column is readonly attribute" do
37
+ it "sets readonly attribute" do
38
+ input_class = "Formtastic::Inputs::#{type.to_s.camelize}Input".constantize
39
+ expect_any_instance_of(input_class).to receive(:readonly_attribute?).at_least(1).and_return(true)
40
+ concat(semantic_form_for(@new_post) do |builder|
41
+ concat(builder.input(:title, :as => type))
42
+ end)
43
+ output_buffer.should have_tag((type == :text ? 'textarea' : 'input') + '[@readonly]')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'search input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe "when object is provided" do
14
+ before do
15
+ concat(semantic_form_for(@new_post) do |builder|
16
+ concat(builder.input(:search))
17
+ end)
18
+ end
19
+
20
+ it_should_have_input_wrapper_with_class(:search)
21
+ it_should_have_input_wrapper_with_class(:input)
22
+ it_should_have_input_wrapper_with_class(:stringish)
23
+ it_should_have_input_wrapper_with_id("post_search_input")
24
+ it_should_have_label_with_text(/Search/)
25
+ it_should_have_label_for("post_search")
26
+ it_should_have_input_with_id("post_search")
27
+ it_should_have_input_with_type(:search)
28
+ it_should_have_input_with_name("post[search]")
29
+
30
+ end
31
+
32
+ describe "when namespace is provided" do
33
+
34
+ before do
35
+ concat(semantic_form_for(@new_post, :namespace => "context2") do |builder|
36
+ concat(builder.input(:search))
37
+ end)
38
+ end
39
+
40
+ it_should_have_input_wrapper_with_id("context2_post_search_input")
41
+ it_should_have_label_and_input_with_id("context2_post_search")
42
+
43
+ end
44
+
45
+ describe "when index is provided" do
46
+
47
+ before do
48
+ @output_buffer = ''
49
+ mock_everything
50
+
51
+ concat(semantic_form_for(@new_post) do |builder|
52
+ concat(builder.fields_for(:author, :index => 3) do |author|
53
+ concat(author.input(:name, :as => :search))
54
+ end)
55
+ end)
56
+ end
57
+
58
+ it 'should index the id of the wrapper' do
59
+ output_buffer.should have_tag("li#post_author_attributes_3_name_input")
60
+ end
61
+
62
+ it 'should index the id of the select tag' do
63
+ output_buffer.should have_tag("input#post_author_attributes_3_name")
64
+ end
65
+
66
+ it 'should index the name of the select tag' do
67
+ output_buffer.should have_tag("input[@name='post[author_attributes][3][name]']")
68
+ end
69
+
70
+ end
71
+
72
+ describe "when required" do
73
+ it "should add the required attribute to the input's html options" do
74
+ with_config :use_required_attribute, true do
75
+ concat(semantic_form_for(@new_post) do |builder|
76
+ concat(builder.input(:title, :as => :search, :required => true))
77
+ end)
78
+ output_buffer.should have_tag("input[@required]")
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+
@@ -0,0 +1,615 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'select input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'explicit values' do
14
+ describe 'using an array of values' do
15
+ before do
16
+ @array_with_values = ["Title A", "Title B", "Title C"]
17
+ @array_with_keys_and_values = [["Title D", 1], ["Title E", 2], ["Title F", 3]]
18
+ concat(semantic_form_for(@new_post) do |builder|
19
+ concat(builder.input(:title, :as => :select, :collection => @array_with_values))
20
+ concat(builder.input(:title, :as => :select, :collection => @array_with_keys_and_values))
21
+ end)
22
+ end
23
+
24
+ it 'should have a option for each key and/or value' do
25
+ @array_with_values.each do |v|
26
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
27
+ end
28
+ @array_with_keys_and_values.each do |v|
29
+ output_buffer.should have_tag("form li select option[@value='#{v.second}']", /^#{v.first}$/)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'using a set of values' do
35
+ before do
36
+ @set_with_values = Set.new(["Title A", "Title B", "Title C"])
37
+ @set_with_keys_and_values = [["Title D", :d], ["Title E", :e], ["Title F", :f]]
38
+ concat(semantic_form_for(@new_post) do |builder|
39
+ concat(builder.input(:title, :as => :select, :collection => @set_with_values))
40
+ concat(builder.input(:title, :as => :select, :collection => @set_with_keys_and_values))
41
+ end)
42
+ end
43
+
44
+ it 'should have a option for each key and/or value' do
45
+ @set_with_values.each do |v|
46
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
47
+ end
48
+ @set_with_keys_and_values.each do |v|
49
+ output_buffer.should have_tag("form li select option[@value='#{v.second}']", /^#{v.first}$/)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "using a related model without reflection's options (Mongoid Document)" do
55
+ before do
56
+ @new_post.stub(:mongoid_reviewer)
57
+ concat(semantic_form_for(@new_post) do |builder|
58
+ concat(builder.input(:mongoid_reviewer, :as => :select))
59
+ end)
60
+ end
61
+
62
+ it 'should draw select options' do
63
+ output_buffer.should have_tag('form li select')
64
+ output_buffer.should have_tag('form li select#post_reviewer_id')
65
+ output_buffer.should_not have_tag('form li select#post_mongoid_reviewer_id')
66
+ end
67
+ end
68
+
69
+ describe 'using a range' do
70
+ before do
71
+ @range_with_values = 1..5
72
+ concat(semantic_form_for(@new_post) do |builder|
73
+ concat(builder.input(:title, :as => :select, :collection => @range_with_values))
74
+ end)
75
+ end
76
+
77
+ it 'should have an option for each value' do
78
+ @range_with_values.each do |v|
79
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe 'using a string' do
85
+ before do
86
+ @string ="<option value='0'>0</option><option value='1'>1</option>".html_safe
87
+ concat(semantic_form_for(@new_post) do |builder|
88
+ concat(builder.input(:title, :as => :select, :collection => @string))
89
+ end)
90
+ end
91
+
92
+ it 'should render select options using provided HTML string' do
93
+ 2.times do |v|
94
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
95
+ end
96
+ end
97
+ end
98
+
99
+ describe 'using a nil name' do
100
+ before do
101
+ concat(semantic_form_for(@new_post) do |builder|
102
+ concat(builder.input(:title, :as => :select, :collection => [], :input_html => {:name => nil}))
103
+ end)
104
+ end
105
+
106
+ it_should_have_select_with_name("post[title]")
107
+ end
108
+ end
109
+
110
+ describe 'for boolean columns' do
111
+ describe 'default formtastic locale' do
112
+ before do
113
+ # Note: Works, but something like Formtastic.root.join(...) would probably be "safer".
114
+ ::I18n.load_path = [File.join(File.dirname(__FILE__), *%w[.. .. lib locale en.yml])]
115
+ ::I18n.backend.send(:init_translations)
116
+
117
+ concat(semantic_form_for(@new_post) do |builder|
118
+ concat(builder.input(:published, :as => :select))
119
+ end)
120
+ end
121
+
122
+ after do
123
+ ::I18n.load_path = []
124
+ ::I18n.backend.store_translations :en, {}
125
+ end
126
+
127
+ it 'should render a select with at least options: true/false' do
128
+ output_buffer.should have_tag("form li select option[@value='true']", /^Yes$/)
129
+ output_buffer.should have_tag("form li select option[@value='false']", /^No$/)
130
+ end
131
+ end
132
+
133
+ describe 'custom locale' do
134
+ before do
135
+ @boolean_select_labels = {:yes => 'Yep', :no => 'Nope'}
136
+ ::I18n.backend.store_translations :en, :formtastic => @boolean_select_labels
137
+
138
+ concat(semantic_form_for(@new_post) do |builder|
139
+ concat(builder.input(:published, :as => :select))
140
+ end)
141
+ end
142
+
143
+ after do
144
+ ::I18n.backend.store_translations :en, {}
145
+ end
146
+
147
+ it 'should render a select with at least options: true/false' do
148
+ output_buffer.should have_tag("form li select option[@value='true']", /#{@boolean_select_labels[:yes]}/)
149
+ output_buffer.should have_tag("form li select option[@value='false']", /#{@boolean_select_labels[:no]}/)
150
+ end
151
+ end
152
+ end
153
+
154
+ describe 'for a enum column' do
155
+ before do
156
+ @new_post.stub(:status) { 'inactive' }
157
+ statuses = ActiveSupport::HashWithIndifferentAccess.new("active"=>0, "inactive"=>1)
158
+ @new_post.class.stub(:statuses) { statuses }
159
+ @new_post.stub(:defined_enums) { { "status" => statuses } }
160
+ end
161
+
162
+ context 'single choice' do
163
+ before do
164
+ concat(semantic_form_for(@new_post) do |builder|
165
+ concat(builder.input(:status, :as => :select))
166
+ end)
167
+ end
168
+
169
+ it_should_have_input_wrapper_with_class("select")
170
+ it_should_have_input_wrapper_with_class(:input)
171
+ it_should_have_input_wrapper_with_id("post_status_input")
172
+ it_should_have_label_with_text(/Status/)
173
+ it_should_have_label_for('post_status')
174
+ it_should_apply_error_logic_for_input_type(:select)
175
+
176
+ it 'should have a select inside the wrapper' do
177
+ output_buffer.should have_tag('form li select')
178
+ output_buffer.should have_tag('form li select#post_status')
179
+ end
180
+
181
+ it 'should have a valid name' do
182
+ output_buffer.should have_tag("form li select[@name='post[status]']")
183
+ output_buffer.should_not have_tag("form li select[@name='post[status][]']")
184
+ end
185
+
186
+ it 'should not create a multi-select' do
187
+ output_buffer.should_not have_tag('form li select[@multiple]')
188
+ end
189
+
190
+ it 'should not add a hidden input' do
191
+ output_buffer.should_not have_tag('form li input[@type="hidden"]')
192
+ end
193
+
194
+ it 'should create a select without size' do
195
+ output_buffer.should_not have_tag('form li select[@size]')
196
+ end
197
+
198
+ it 'should have a blank option' do
199
+ output_buffer.should have_tag("form li select option[@value='']")
200
+ end
201
+
202
+ it 'should have a select option for each defined enum status' do
203
+ output_buffer.should have_tag("form li select[@name='post[status]'] option", :count => @new_post.class.statuses.count + 1)
204
+ @new_post.class.statuses.each do |label, value|
205
+ output_buffer.should have_tag("form li select option[@value='#{label}']", /#{label.humanize}/)
206
+ end
207
+ end
208
+
209
+ it 'should have one option with a "selected" attribute (TODO)' do
210
+ output_buffer.should have_tag("form li select[@name='post[status]'] option[@selected]", :count => 1)
211
+ end
212
+ end
213
+
214
+ context 'multiple choice' do
215
+ it 'raises an error' do
216
+ expect {
217
+ concat(semantic_form_for(@new_post) do |builder|
218
+ concat(builder.input(:status, :as => :select, :multiple => true))
219
+ end)
220
+ }.to raise_error Formtastic::UnsupportedEnumCollection
221
+ end
222
+ end
223
+ end
224
+
225
+ describe 'for a belongs_to association' do
226
+ before do
227
+ concat(semantic_form_for(@new_post) do |builder|
228
+ concat(builder.input(:author, :as => :select))
229
+ concat(builder.input(:reviewer, :as => :select))
230
+ end)
231
+ end
232
+
233
+ it_should_have_input_wrapper_with_class("select")
234
+ it_should_have_input_wrapper_with_class(:input)
235
+ it_should_have_input_wrapper_with_id("post_author_input")
236
+ it_should_have_label_with_text(/Author/)
237
+ it_should_have_label_for('post_author_id')
238
+ it_should_apply_error_logic_for_input_type(:select)
239
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
240
+ it_should_use_the_collection_when_provided(:select, 'option')
241
+
242
+ it 'should have a select inside the wrapper' do
243
+ output_buffer.should have_tag('form li select')
244
+ output_buffer.should have_tag('form li select#post_author_id')
245
+ output_buffer.should have_tag('form li select#post_reviewer_id')
246
+ end
247
+
248
+ it 'should have a valid name' do
249
+ output_buffer.should have_tag("form li select[@name='post[author_id]']")
250
+ output_buffer.should_not have_tag("form li select[@name='post[author_id][]']")
251
+ output_buffer.should_not have_tag("form li select[@name='post[reviewer_id][]']")
252
+ end
253
+
254
+ it 'should not create a multi-select' do
255
+ output_buffer.should_not have_tag('form li select[@multiple]')
256
+ end
257
+
258
+ it 'should not add a hidden input' do
259
+ output_buffer.should_not have_tag('form li input[@type="hidden"]')
260
+ end
261
+
262
+ it 'should create a select without size' do
263
+ output_buffer.should_not have_tag('form li select[@size]')
264
+ end
265
+
266
+ it 'should have a blank option' do
267
+ output_buffer.should have_tag("form li select option[@value='']")
268
+ end
269
+
270
+ it 'should have a select option for each Author' do
271
+ output_buffer.should have_tag("form li select[@name='post[author_id]'] option", :count => ::Author.all.size + 1)
272
+ ::Author.all.each do |author|
273
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
274
+ end
275
+ end
276
+
277
+ it 'should have one option with a "selected" attribute (bob)' do
278
+ output_buffer.should have_tag("form li select[@name='post[author_id]'] option[@selected]", :count => 1)
279
+ end
280
+
281
+ it 'should not singularize the association name' do
282
+ @new_post.stub(:author_status).and_return(@bob)
283
+ @new_post.stub(:author_status_id).and_return(@bob.id)
284
+ @new_post.stub(:column_for_attribute).and_return(double('column', :type => :integer, :limit => 255))
285
+
286
+ concat(semantic_form_for(@new_post) do |builder|
287
+ concat(builder.input(:author_status, :as => :select))
288
+ end)
289
+
290
+ output_buffer.should have_tag('form li select#post_author_status_id')
291
+ end
292
+ end
293
+
294
+ describe "for a belongs_to association with :conditions" do
295
+ before do
296
+ ::Post.stub(:reflect_on_association).with(:author) do
297
+ mock = double('reflection', :options => {:conditions => {:active => true}}, :klass => ::Author, :macro => :belongs_to)
298
+ mock.stub(:[]).with(:class_name).and_return("Author")
299
+ mock
300
+ end
301
+ end
302
+
303
+ it "should call author.(scoped|where) with association conditions" do
304
+ if Formtastic::Util.rails3?
305
+ ::Author.should_receive(:scoped).with(:conditions => {:active => true})
306
+ else
307
+ ::Author.should_receive(:where).with({:active => true})
308
+ end
309
+
310
+ semantic_form_for(@new_post) do |builder|
311
+ concat(builder.input(:author, :as => :select))
312
+ end
313
+ end
314
+ end
315
+
316
+ describe 'for a has_many association' do
317
+ before do
318
+ concat(semantic_form_for(@fred) do |builder|
319
+ concat(builder.input(:posts, :as => :select))
320
+ end)
321
+ end
322
+
323
+ it_should_have_input_wrapper_with_class("select")
324
+ it_should_have_input_wrapper_with_id("author_posts_input")
325
+ it_should_have_label_with_text(/Post/)
326
+ it_should_have_label_for('author_post_ids')
327
+ it_should_apply_error_logic_for_input_type(:select)
328
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
329
+ it_should_use_the_collection_when_provided(:select, 'option')
330
+
331
+ it 'should have a select inside the wrapper' do
332
+ output_buffer.should have_tag('form li select')
333
+ output_buffer.should have_tag('form li select#author_post_ids')
334
+ end
335
+
336
+ it 'should have a multi-select select' do
337
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
338
+ end
339
+
340
+ it 'should append [] to the name attribute for multiple select' do
341
+ output_buffer.should have_tag('form li select[@multiple="multiple"][@name="author[post_ids][]"]')
342
+ end
343
+
344
+ it 'should have a hidden field' do
345
+ output_buffer.should have_tag('form li input[@type="hidden"][@name="author[post_ids][]"]', :count => 1)
346
+ end
347
+
348
+ it 'should have a select option for each Post' do
349
+ output_buffer.should have_tag('form li select option', :count => ::Post.all.size)
350
+ ::Post.all.each do |post|
351
+ output_buffer.should have_tag("form li select option[@value='#{post.id}']", /#{post.to_label}/)
352
+ end
353
+ end
354
+
355
+ it 'should not have a blank option by default' do
356
+ output_buffer.should_not have_tag("form li select option[@value='']")
357
+ end
358
+
359
+ it 'should respect the :include_blank option for single selects' do
360
+ concat(semantic_form_for(@fred) do |builder|
361
+ concat(builder.input(:posts, :as => :select, :multiple => false, :include_blank => true))
362
+ end)
363
+
364
+ output_buffer.should have_tag("form li select option[@value='']")
365
+ end
366
+
367
+ it 'should respect the :include_blank option for multiple selects' do
368
+ concat(semantic_form_for(@fred) do |builder|
369
+ concat(builder.input(:posts, :as => :select, :multiple => true, :include_blank => true))
370
+ end)
371
+
372
+ output_buffer.should have_tag("form li select option[@value='']")
373
+ end
374
+
375
+ it 'should have one option with a "selected" attribute' do
376
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
377
+ end
378
+ end
379
+
380
+ describe 'for a has_and_belongs_to_many association' do
381
+ before do
382
+ concat(semantic_form_for(@freds_post) do |builder|
383
+ concat(builder.input(:authors, :as => :select))
384
+ end)
385
+ end
386
+
387
+ it_should_have_input_wrapper_with_class("select")
388
+ it_should_have_input_wrapper_with_id("post_authors_input")
389
+ it_should_have_label_with_text(/Author/)
390
+ it_should_have_label_for('post_author_ids')
391
+ it_should_apply_error_logic_for_input_type(:select)
392
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
393
+ it_should_use_the_collection_when_provided(:select, 'option')
394
+
395
+ it 'should have a select inside the wrapper' do
396
+ output_buffer.should have_tag('form li select')
397
+ output_buffer.should have_tag('form li select#post_author_ids')
398
+ end
399
+
400
+ it 'should have a multi-select select' do
401
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
402
+ end
403
+
404
+ it 'should have a select option for each Author' do
405
+ output_buffer.should have_tag('form li select option', :count => ::Author.all.size)
406
+ ::Author.all.each do |author|
407
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
408
+ end
409
+ end
410
+
411
+ it 'should not have a blank option by default' do
412
+ output_buffer.should_not have_tag("form li select option[@value='']")
413
+ end
414
+
415
+ it 'should respect the :include_blank option for single selects' do
416
+ concat(semantic_form_for(@freds_post) do |builder|
417
+ concat(builder.input(:authors, :as => :select, :multiple => false, :include_blank => true))
418
+ end)
419
+
420
+ output_buffer.should have_tag("form li select option[@value='']")
421
+ end
422
+
423
+ it 'should respect the :include_blank option for multiple selects' do
424
+ concat(semantic_form_for(@freds_post) do |builder|
425
+ concat(builder.input(:authors, :as => :select, :multiple => true, :include_blank => true))
426
+ end)
427
+
428
+ output_buffer.should have_tag("form li select option[@value='']")
429
+ end
430
+
431
+ it 'should have one option with a "selected" attribute' do
432
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
433
+ end
434
+ end
435
+
436
+ describe 'when :prompt => "choose something" is set' do
437
+ before do
438
+ @new_post.stub(:author_id).and_return(nil)
439
+ concat(semantic_form_for(@new_post) do |builder|
440
+ concat(builder.input(:author, :as => :select, :prompt => "choose author"))
441
+ end)
442
+ end
443
+
444
+ it 'should have a select with prompt' do
445
+ output_buffer.should have_tag("form li select option[@value='']", /choose author/, :count => 1)
446
+ end
447
+
448
+ it 'should not have a second blank select option' do
449
+ output_buffer.should have_tag("form li select option[@value='']", :count => 1)
450
+ end
451
+ end
452
+
453
+ describe 'when no object is given' do
454
+ before(:each) do
455
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
456
+ concat(builder.input(:author, :as => :select, :collection => ::Author.all))
457
+ end)
458
+ end
459
+
460
+ it 'should generate label' do
461
+ output_buffer.should have_tag('form li label', /Author/)
462
+ output_buffer.should have_tag("form li label[@for='project_author']")
463
+ end
464
+
465
+ it 'should generate select inputs' do
466
+ output_buffer.should have_tag('form li select#project_author')
467
+ output_buffer.should have_tag('form li select option', :count => ::Author.all.size + 1)
468
+ end
469
+
470
+ it 'should generate an option to each item' do
471
+ ::Author.all.each do |author|
472
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
473
+ end
474
+ end
475
+ end
476
+
477
+ describe 'when no association exists' do
478
+
479
+ it 'should still generate a valid name attribute' do
480
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
481
+ concat(builder.input(:author_name, :as => :select, :collection => ::Author.all))
482
+ end)
483
+ output_buffer.should have_tag("form li select[@name='project[author_name]']")
484
+ end
485
+
486
+ describe 'and :multiple is set to true through :input_html' do
487
+ it "should make the select a multi-select" do
488
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
489
+ concat(builder.input(:author_name, :as => :select, :input_html => {:multiple => true} ))
490
+ end)
491
+ output_buffer.should have_tag("form li select[@multiple]")
492
+ end
493
+ end
494
+
495
+ describe 'and :multiple is set to true' do
496
+ it "should make the select a multi-select" do
497
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
498
+ concat(builder.input(:author_name, :as => :select, :multiple => true, :collection => ["Fred", "Bob"]))
499
+ end)
500
+ output_buffer.should have_tag("form li select[@multiple]")
501
+ end
502
+ end
503
+
504
+ end
505
+
506
+ describe 'when a grouped collection collection is given' do
507
+ before(:each) do
508
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
509
+ @grouped_opts = [['one', ['pencil', 'crayon', 'pen']],
510
+ ['two', ['eyes', 'hands', 'feet']],
511
+ ['three', ['wickets', 'witches', 'blind mice']]]
512
+ concat(builder.input(:author, :as => :select, :collection => grouped_options_for_select(@grouped_opts, "hands")))
513
+ end)
514
+ end
515
+
516
+ it 'should generate an option to each item' do
517
+ @grouped_opts.each do |opt_pair|
518
+ output_buffer.should have_tag("form li select optgroup[@label='#{opt_pair[0]}']")
519
+ opt_pair[1].each do |v|
520
+ output_buffer.should have_tag("form li select optgroup[@label='#{opt_pair[0]}'] option[@value='#{v}']")
521
+ end
522
+ end
523
+ output_buffer.should have_tag("form li select optgroup option[@selected]","hands")
524
+ end
525
+ end
526
+
527
+ describe "enum" do
528
+ before do
529
+ @output_buffer = ''
530
+ @some_meta_descriptions = ["One", "Two", "Three"]
531
+ @new_post.stub(:meta_description).at_least(:once)
532
+ end
533
+
534
+ describe ":as is not set" do
535
+ before do
536
+ concat(semantic_form_for(@new_post) do |builder|
537
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
538
+ end)
539
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
540
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
541
+ end)
542
+ end
543
+
544
+ it "should render a select field" do
545
+ output_buffer.should have_tag("form li select", :count => 2)
546
+ end
547
+ end
548
+
549
+ describe ":as is set" do
550
+ before do
551
+ # Should not be a case, but just checking :as got highest priority in setting input type.
552
+ concat(semantic_form_for(@new_post) do |builder|
553
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
554
+ end)
555
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
556
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
557
+ end)
558
+ end
559
+
560
+ it "should render a text field" do
561
+ output_buffer.should have_tag("form li input[@type='text']", :count => 2)
562
+ end
563
+ end
564
+ end
565
+
566
+ describe 'when a namespace is provided' do
567
+ before do
568
+ concat(semantic_form_for(@freds_post, :namespace => 'context2') do |builder|
569
+ concat(builder.input(:authors, :as => :select))
570
+ end)
571
+ end
572
+ it_should_have_input_wrapper_with_id("context2_post_authors_input")
573
+ it_should_have_select_with_id("context2_post_author_ids")
574
+ it_should_have_label_for("context2_post_author_ids")
575
+ end
576
+
577
+ describe "when index is provided" do
578
+
579
+ before do
580
+ @output_buffer = ''
581
+ mock_everything
582
+
583
+ concat(semantic_form_for(@new_post) do |builder|
584
+ concat(builder.fields_for(:author, :index => 3) do |author|
585
+ concat(author.input(:name, :as => :select))
586
+ end)
587
+ end)
588
+ end
589
+
590
+ it 'should index the id of the wrapper' do
591
+ output_buffer.should have_tag("li#post_author_attributes_3_name_input")
592
+ end
593
+
594
+ it 'should index the id of the select tag' do
595
+ output_buffer.should have_tag("select#post_author_attributes_3_name")
596
+ end
597
+
598
+ it 'should index the name of the select' do
599
+ output_buffer.should have_tag("select[@name='post[author_attributes][3][name]']")
600
+ end
601
+
602
+ end
603
+
604
+ context "when required" do
605
+ it "should add the required attribute to the select's html options" do
606
+ with_config :use_required_attribute, true do
607
+ concat(semantic_form_for(@new_post) do |builder|
608
+ concat(builder.input(:author, :as => :select, :required => true))
609
+ end)
610
+ output_buffer.should have_tag("select[@required]")
611
+ end
612
+ end
613
+ end
614
+
615
+ end