formtastic 1.2.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
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,19 @@
1
+ class <%= name.camelize %>Input <%= @extension_sentence %>
2
+ <%- if !options[:extend] -%>
3
+ include Formtastic::Inputs::Base
4
+ <%- end -%>
5
+
6
+ <%- if !options[:extend] || (options[:extend] == "extend") -%>
7
+ def to_html
8
+ # Add your custom input definition here.
9
+ <%- if options[:extend] == "extend" -%>
10
+ super
11
+ <%- end -%>
12
+ end
13
+
14
+ <%- else -%>
15
+ def input_html_options
16
+ # Add your custom input extension here.
17
+ end
18
+ <%- end -%>
19
+ end
data/lib/locale/en.yml CHANGED
@@ -4,4 +4,7 @@ en:
4
4
  :no: 'No'
5
5
  :create: 'Create %{model}'
6
6
  :update: 'Update %{model}'
7
+ :submit: 'Submit %{model}'
8
+ :cancel: 'Cancel %{model}'
9
+ :reset: 'Reset %{model}'
7
10
  :required: 'required'
@@ -0,0 +1,224 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta content="noindex,nofollow" name="robots">
5
+ <meta charset="utf-8">
6
+ <title>Formtastic</title>
7
+
8
+ <link href="/formtastic.css" rel="stylesheet" type="text/css" />
9
+ <!--[if IE 6]><link href="/formtastic_ie6.css" rel="stylesheet" type="text/css" /><![endif]-->
10
+ <!--[if IE 7]><link href="/formtastic_ie7.css" rel="stylesheet" type="text/css" /><![endif]-->
11
+
12
+ <style>
13
+ body {
14
+ font-family: helvetica, arial;
15
+ font-size: 12px;
16
+ }
17
+ </style>
18
+ </head>
19
+ </html>
20
+ <body>
21
+ <h1>Formtastic</h1>
22
+
23
+ <form action="basic_inputs.html" class="formtastic gem" id="new_gem" method="get">
24
+ <fieldset class="inputs">
25
+ <ol>
26
+ <li class="input string stringish optional" id="gem_summary_input">
27
+ <label class="label" for="gem_summary">Summary</label>
28
+ <input id="gem_summary" name="gem[summary]" type="text" value="A Rails form builder plugin/gem with semantically rich and accessible markup" maxlength="255">
29
+ </li>
30
+
31
+ <li class="input text optional" id="gem_description_input">
32
+ <label class="label" for="gem_description">Description</label>
33
+ <textarea id="gem_description" rows="5">A Rails form builder plugin/gem with semantically rich and accessible markup</textarea>
34
+ </li>
35
+
36
+ <li class="input url stringish optional" id="gem_url_input">
37
+ <label class="label" for="gem_url">URL</label>
38
+ <input id="gem_url" name="gem[url]" type="url">
39
+ <p class="inline-hints">Example: https://github.com/justinfrench/formtastic</p>
40
+ </li>
41
+
42
+ <li class="input password stringish required" id="gem_password_input">
43
+ <label class="label" for="gem_password">Password<abbr title="required">*</abbr></label>
44
+ <input id="gem_password" name="gem[password]" type="password">
45
+ <p class="inline-errors">can't be blank</p>
46
+ </li>
47
+
48
+ <li class="input password stringish required" id="gem_password_input">
49
+ <label class="label" for="gem_password_confirmation">Password confirmation<abbr title="required">*</abbr></label>
50
+ <input id="gem_password_confirmation" name="gem[password_confirmation]" type="password">
51
+ <p class="inline-errors">can't be blank</p>
52
+ </li>
53
+
54
+ <li class="input numeric stringish optional" id="gem_downloads_input">
55
+ <label class="label" for="gem_downloads">Downloads</label>
56
+ <input id="gem_downloads" name="gem[downloads]" type="numeric" value="209,354">
57
+ </li>
58
+
59
+ <li class="input file optional" id="gem_file_input">
60
+ <label class="label" for="gem_file">File</label>
61
+ <input id="gem_file" name="gem[file]" type="file" value="value">
62
+ </li>
63
+
64
+ <li class="input email stringish optional" id="gem_email_input">
65
+ <label class="label" for="gem_email">Email</label>
66
+ <input id="gem_email" name="gem[email]" type="email">
67
+ </li>
68
+
69
+ <li class="input phone stringish optional" id="gem_phone_input">
70
+ <label class="label" for="gem_phone">Phone</label>
71
+ <input id="gem_phone" name="gem[phone]" type="phone">
72
+ </li>
73
+
74
+ <li class="input search stringish optional" id="gem_search_input">
75
+ <label class="label" for="gem_search">Search</label>
76
+ <input id="gem_search" name="gem[search]" type="search">
77
+ </li>
78
+
79
+ <li class="input date optional">
80
+ <fieldset class="fragments">
81
+ <legend class="label"><label>First release</label></legend>
82
+ <ol class="fragments-group">
83
+ <li class="fragment">
84
+ <label>Year</label>
85
+ <select id="gem_first_release_1i" name="gem[first_release][1i]">
86
+ <option>2009</option>
87
+ </select>
88
+ </li>
89
+ <li class="fragment">
90
+ <label>Month</label>
91
+ <select id="gem_first_release_2i" name="gem[first_release][2i]">
92
+ <option>October</option>
93
+ </select>
94
+ </li>
95
+ <li class="fragment">
96
+ <label>Day</label>
97
+ <select id="gem_first_release_3i" name="gem[first_release][3i]">
98
+ <option>15</option>
99
+ </select>
100
+ </li>
101
+ </ol>
102
+ </fieldset>
103
+ </li>
104
+
105
+ <li class="input date_picker optional">
106
+ <label class="label" for="gem_expiry">Expiry date</label>
107
+ <input id="gem_expiry" name="gem[gem_expiry]" type="date" size="10" maxlength="10" min="2012-01-01" max="2012-12-31" step="1" value="2012-01-01">
108
+ </li>
109
+
110
+ <li class="input datetime_picker optional">
111
+ <label class="label" for="gem_expiry">Expiry datetime-local</label>
112
+ <input id="gem_expiry" name="gem[gem_expiry]" type="datetime-local" size="16" maxlength="16" min="2012-01-01T00:00" max="2012-12-31T23:59" step="1800">
113
+ </li>
114
+
115
+ <li class="input datetime_picker optional">
116
+ <label class="label" for="gem_expiry">Expiry datetime</label>
117
+ <input id="gem_expiry" name="gem[gem_expiry]" type="datetime" size="16" maxlength="16" min="2012-01-01T00:00" max="2012-12-31T23:59" step="1800">
118
+ </li>
119
+
120
+ <li class="input time_picker optional">
121
+ <label class="label" for="gem_expiry">Expiry time</label>
122
+ <input id="gem_expiry" name="gem[gem_expiry]" type="time" size="5" maxlength="5" min="T00:00" max="23:59" step="60">
123
+ </li>
124
+
125
+ <li class="input string stringish optional autofocus" id="gem_autofocus_input">
126
+ <label class="label" for="gem_autofocus">Autofocus</label>
127
+ <input id="gem_autofocus" name="gem[autofocus]" type="text" value="The focus is set to this field" autofocus="autofocus">
128
+ </li>
129
+
130
+ <li class="input boolean optional" id="gem_terms_and_conditions_input">
131
+ <label for="gem_terms_and_conditions">
132
+ <input id="gem_terms_and_conditions" name="gem[terms_and_conditions]" type="checkbox" value="1" checked="checked">
133
+ I agree to your draconian terms of service even though I didn't read them
134
+ </label>
135
+ </li>
136
+
137
+ <li class="input select optional" id="gem_awesomeness_input">
138
+ <label class="label" for="gem_awesomeness">Select</label>
139
+ <select id="gem_awesomeness" name="gem[awesomeness]">
140
+ <option value="5">First choice</option>
141
+ <option value="4">Second choice</option>
142
+ </select>
143
+ </li>
144
+
145
+ <li class="input radio optional">
146
+ <fieldset class="choices">
147
+ <legend class="label"><label>Rate</label></legend>
148
+ <ol class="choices-group">
149
+ <li class="choice">
150
+ <label><input id="gem_rate_five" name="gem[rate]" type="radio" checked="checked"> Five</label></label>
151
+ </li>
152
+ <li class="choice">
153
+ <label><input id="gem_rate_four" name="gem[rate]" type="radio"> Four</label></label>
154
+ </li>
155
+ <li class="choice">
156
+ <label><input id="gem_rate_three" name="gem[rate]" type="radio"> Three</label></label>
157
+ </li>
158
+ <li class="choice">
159
+ <label><input id="gem_rate_two" name="gem[rate]" type="radio"> Two</label></label>
160
+ </li>
161
+ <li class="choice">
162
+ <label><input id="gem_rate_one" name="gem[rate]" type="radio"> One</label></label>
163
+ </li>
164
+ </ol>
165
+ </fieldset>
166
+ </li>
167
+
168
+ <li class="input check_boxes optional">
169
+ <fieldset class="choices">
170
+ <legend class="label"><label>Browsers</label></legend>
171
+ <ol class="choices-group">
172
+ <li class="choice">
173
+ <label><input id="gem_browser_google_chrome" name="gem[browser][]" type="checkbox"> Google Chrome</label></label>
174
+ </li>
175
+ <li class="choice">
176
+ <label><input id="gem_browser_apple_safari" name="gem[browser][]" type="checkbox"> Apple Safari</label></label>
177
+ </li>
178
+ <li class="choice">
179
+ <label><input id="gem_browser_mozilla_firefox" name="gem[browser][]" type="checkbox"> Mozilla Firefox</label></label>
180
+ </li>
181
+ <li class="choice">
182
+ <label><input id="gem_browser_opera" name="gem[browser][]" type="checkbox"> Opera</label></label>
183
+ </li>
184
+ <li class="choice">
185
+ <label><input id="gem_browser_internet_explorer" name="gem[browser][]" type="checkbox"> Internet Explorer (Although you really can't call it a browser can you? This is some long text to force wrapping.)</label></label>
186
+ </li>
187
+ </ol>
188
+ </fieldset>
189
+ </li>
190
+ </ol>
191
+ </fieldset>
192
+
193
+ <fieldset class="buttons">
194
+ <ol>
195
+ <li class="button commit">
196
+ <input class="create" id="gem_submit" type="submit">
197
+ </li>
198
+ </ol>
199
+ </fieldset>
200
+
201
+ <fieldset class="actions">
202
+ <ol>
203
+ <li class="action input_action">
204
+ <input id="gem_submit" type="submit" value="Create Thing">
205
+ </li>
206
+ </ol>
207
+ </fieldset>
208
+
209
+ <fieldset class="actions">
210
+ <ol>
211
+ <li class="action button_action">
212
+ <button id="gem_submit" type="submit">Create Thing</button>
213
+ </li>
214
+ <li class="action button_action">
215
+ <button id="gem_submit" type="reset">Reset</button>
216
+ </li>
217
+ <li class="action link_action">
218
+ <a href="#">Cancel</a>
219
+ </li>
220
+ </ol>
221
+ </fieldset>
222
+ </form>
223
+ </body>
224
+ </html>
data/sample/config.ru ADDED
@@ -0,0 +1,69 @@
1
+ module Rack
2
+ module Formtastic
3
+ class Samples
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ @status, @headers, @body = @app.call(env)
10
+ @env = env
11
+
12
+ @body = '' if favicon?
13
+ @body = static_file if !favicon? && static_file?
14
+ @body = stylesheet if stylesheet?
15
+ @headers ||= {}
16
+ @headers['Content-Type'] = mime(extension)
17
+ [@status, @headers, @body]
18
+ end
19
+
20
+ def static_file?
21
+ !stylesheet?
22
+ end
23
+
24
+ def stylesheet?
25
+ @env["PATH_INFO"] =~ /\.(css)/
26
+ end
27
+
28
+ def favicon?
29
+ @env["PATH_INFO"] =~ /favicon.ico$/
30
+ end
31
+
32
+ def extension
33
+ @env["PATH_INFO"].split(".").last
34
+ end
35
+
36
+ def mime(extension)
37
+ mimes[extension] || mimes['html']
38
+ end
39
+
40
+ def mimes
41
+ {
42
+ 'ico' => 'image/x-icon',
43
+ 'html' => 'text/html',
44
+ 'css' => 'text/css',
45
+ 'js' => 'text/javascript'
46
+ }
47
+ end
48
+
49
+ def static_file
50
+ ::File.open(file_path)
51
+ end
52
+
53
+ def stylesheet
54
+ ::File.open(::File.join("../app/assets/stylesheets", file_path))
55
+ end
56
+
57
+ def file_path
58
+ @env["PATH_INFO"].gsub(/^\//, '').gsub(/^$/, 'index.html')
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
65
+ use Rack::ContentLength
66
+ use Rack::Formtastic::Samples
67
+
68
+ app = lambda { |env| [200, @headers, @body] }
69
+ run app
data/sample/index.html ADDED
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>untitled</title>
6
+ </head>
7
+ <body>
8
+
9
+ <ul>
10
+ <li><a href="/basic_inputs.html">Basic Inputs</li>
11
+ </ul>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'formtastic/action_class_finder'
4
+
5
+ describe Formtastic::ActionClassFinder do
6
+ include FormtasticSpecHelper
7
+
8
+ it_behaves_like 'Specialized Class Finder' do
9
+ let(:default) { Formtastic::Actions }
10
+ let(:namespaces_setting) { :action_namespaces }
11
+ end
12
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'ButtonAction', 'when submitting' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+
12
+ concat(semantic_form_for(@new_post) do |builder|
13
+ concat(builder.action(:submit, :as => :button))
14
+ end)
15
+ end
16
+
17
+ it 'should render a submit type of button' do
18
+ output_buffer.should have_tag('li.action.button_action button[@type="submit"]')
19
+ end
20
+
21
+ end
22
+
23
+ describe 'ButtonAction', 'when resetting' do
24
+
25
+ include FormtasticSpecHelper
26
+
27
+ before do
28
+ @output_buffer = ''
29
+ mock_everything
30
+
31
+ concat(semantic_form_for(@new_post) do |builder|
32
+ concat(builder.action(:reset, :as => :button))
33
+ end)
34
+ end
35
+
36
+ it 'should render a reset type of button' do
37
+ output_buffer.should have_tag('li.action.button_action button[@type="reset"]', :text => "Reset Post")
38
+ end
39
+
40
+ it 'should not render a value attribute' do
41
+ output_buffer.should_not have_tag('li.action.button_action button[@value]')
42
+ end
43
+
44
+ end
45
+
46
+ describe 'InputAction', 'when cancelling' do
47
+
48
+ include FormtasticSpecHelper
49
+
50
+ before do
51
+ @output_buffer = ''
52
+ mock_everything
53
+ end
54
+
55
+ it 'should raise an error' do
56
+ lambda {
57
+ concat(semantic_form_for(@new_post) do |builder|
58
+ concat(builder.action(:cancel, :as => :button))
59
+ end)
60
+ }.should raise_error(Formtastic::UnsupportedMethodForAction)
61
+ end
62
+
63
+ end
@@ -0,0 +1,521 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'InputAction::Base' do
5
+
6
+ # Most basic Action class to test Base
7
+ class ::GenericAction
8
+ include ::Formtastic::Actions::Base
9
+
10
+ def supported_methods
11
+ [:submit, :reset, :cancel]
12
+ end
13
+
14
+ def to_html
15
+ wrapper do
16
+ builder.submit(text, button_html)
17
+ end
18
+ end
19
+ end
20
+
21
+ include FormtasticSpecHelper
22
+
23
+ before do
24
+ @output_buffer = ''
25
+ mock_everything
26
+ end
27
+
28
+ describe 'wrapping HTML' do
29
+
30
+ before do
31
+ concat(semantic_form_for(@new_post) do |builder|
32
+ concat(builder.action(:submit, :as => :generic,
33
+ :wrapper_html => { :foo => 'bah' }
34
+ ))
35
+ end)
36
+ end
37
+
38
+ it 'should add the #foo id to the li' do
39
+ output_buffer.should have_tag('li#post_submit_action')
40
+ end
41
+
42
+ it 'should add the .action and .generic_action classes to the li' do
43
+ output_buffer.should have_tag('li.action.generic_action')
44
+ end
45
+
46
+ it 'should pass :wrapper_html HTML attributes to the wrapper' do
47
+ output_buffer.should have_tag('li.action.generic_action[@foo="bah"]')
48
+ end
49
+
50
+ context "when a custom :id is provided" do
51
+
52
+ before do
53
+ concat(semantic_form_for(@new_post) do |builder|
54
+ concat(builder.action(:submit, :as => :generic,
55
+ :wrapper_html => { :id => 'foo_bah_bing' }
56
+ ))
57
+ end)
58
+ end
59
+
60
+ it "should use the custom id" do
61
+ output_buffer.should have_tag('li#foo_bah_bing')
62
+ end
63
+
64
+ end
65
+
66
+ context "when a custom class is provided as a string" do
67
+
68
+ before do
69
+ concat(semantic_form_for(@new_post) do |builder|
70
+ concat(builder.action(:submit, :as => :generic,
71
+ :wrapper_html => { :class => 'foo_bah_bing' }
72
+ ))
73
+ end)
74
+ end
75
+
76
+ it "should add the custom class strng to the existing classes" do
77
+ output_buffer.should have_tag('li.action.generic_action.foo_bah_bing')
78
+ end
79
+
80
+ end
81
+
82
+ context "when a custom class is provided as an array" do
83
+
84
+ before do
85
+ concat(semantic_form_for(@new_post) do |builder|
86
+ concat(builder.action(:submit, :as => :generic,
87
+ :wrapper_html => { :class => ['foo_bah_bing', 'zing_boo'] }
88
+ ))
89
+ end)
90
+ end
91
+
92
+ it "should add the custom class strng to the existing classes" do
93
+ output_buffer.should have_tag('li.action.generic_action.foo_bah_bing.zing_boo')
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+
100
+ describe 'button HTML' do
101
+
102
+ before do
103
+ concat(semantic_form_for(@new_post) do |builder|
104
+ concat(builder.action(:submit, :as => :generic,
105
+ :button_html => { :foo => 'bah' }
106
+ ))
107
+ end)
108
+ end
109
+
110
+ it 'should pass :button_html HTML attributes to the button' do
111
+ output_buffer.should have_tag('li.action.generic_action input[@foo="bah"]')
112
+ end
113
+
114
+ it 'should respect a default_commit_button_accesskey configuration with nil' do
115
+ with_config :default_commit_button_accesskey, nil do
116
+ concat(semantic_form_for(@new_post) do |builder|
117
+ concat(builder.action(:submit, :as => :generic))
118
+ end)
119
+ output_buffer.should_not have_tag('li.action input[@accesskey]')
120
+ end
121
+ end
122
+
123
+ it 'should respect a default_commit_button_accesskey configuration with a String' do
124
+ with_config :default_commit_button_accesskey, 's' do
125
+ concat(semantic_form_for(@new_post) do |builder|
126
+ concat(builder.action(:submit, :as => :generic))
127
+ end)
128
+ output_buffer.should have_tag('li.action input[@accesskey="s"]')
129
+ end
130
+ end
131
+
132
+ it 'should respect an accesskey through options over configration' do
133
+ with_config :default_commit_button_accesskey, 's' do
134
+ concat(semantic_form_for(@new_post) do |builder|
135
+ concat(builder.action(:submit, :as => :generic, :accesskey => 'o'))
136
+ end)
137
+ output_buffer.should_not have_tag('li.action input[@accesskey="s"]')
138
+ output_buffer.should have_tag('li.action input[@accesskey="o"]')
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ describe 'labelling' do
145
+
146
+ describe 'when used without object' do
147
+
148
+ describe 'when explicit label is provided' do
149
+ it 'should render an input with the explicitly specified label' do
150
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
151
+ concat(builder.action(:submit, :as => :generic, :label => "Click!"))
152
+ concat(builder.action(:reset, :as => :generic, :label => "Reset!"))
153
+ concat(builder.action(:cancel, :as => :generic, :label => "Cancel!"))
154
+ end)
155
+ output_buffer.should have_tag('li.generic_action input[@value="Click!"]')
156
+ output_buffer.should have_tag('li.generic_action input[@value="Reset!"]')
157
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel!"]')
158
+ end
159
+ end
160
+
161
+ describe 'when no explicit label is provided' do
162
+ describe 'when no I18n-localized label is provided' do
163
+ before do
164
+ ::I18n.backend.store_translations :en, :formtastic => {
165
+ :submit => 'Submit %{model}',
166
+ :reset => 'Reset %{model}',
167
+ :cancel => 'Cancel %{model}',
168
+ :actions => {
169
+ :message => {
170
+ :submit => 'Submit message',
171
+ :reset => 'Reset message',
172
+ :cancel => 'Cancel message'
173
+ }
174
+ }
175
+ }
176
+ end
177
+
178
+ after do
179
+ ::I18n.backend.reload!
180
+ end
181
+
182
+ it 'should render an input with default I18n-localized label (fallback)' do
183
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
184
+ concat(builder.action(:submit, :as => :generic))
185
+ concat(builder.action(:reset, :as => :generic))
186
+ concat(builder.action(:cancel, :as => :generic))
187
+ end)
188
+ output_buffer.should have_tag('li.generic_action input[@value="Submit Post"]')
189
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel Post"]')
190
+ output_buffer.should have_tag('li.generic_action input[@value="Reset Post"]')
191
+ end
192
+
193
+ it 'should render an input with custom resource name localized label' do
194
+ concat(semantic_form_for(:post, :as => :message, :url => 'http://example.com') do |builder|
195
+ concat(builder.action(:submit, :as => :generic))
196
+ concat(builder.action(:reset, :as => :generic))
197
+ concat(builder.action(:cancel, :as => :generic))
198
+ end)
199
+ output_buffer.should have_tag('li.generic_action input[@value="Submit message"]')
200
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel message"]')
201
+ output_buffer.should have_tag('li.generic_action input[@value="Reset message"]')
202
+ end
203
+ end
204
+
205
+ describe 'when I18n-localized label is provided' do
206
+
207
+ before do
208
+ ::I18n.backend.store_translations :en,
209
+ :formtastic => {
210
+ :actions => {
211
+ :submit => 'Custom Submit',
212
+ :reset => 'Custom Reset',
213
+ :cancel => 'Custom Cancel'
214
+ }
215
+ }
216
+ end
217
+
218
+ after do
219
+ ::I18n.backend.reload!
220
+ end
221
+
222
+ it 'should render an input with localized label (I18n)' do
223
+ with_config :i18n_lookups_by_default, true do
224
+ ::I18n.backend.store_translations :en,
225
+ :formtastic => {
226
+ :actions => {
227
+ :post => {
228
+ :submit => 'Custom Submit %{model}',
229
+ :reset => 'Custom Reset %{model}',
230
+ :cancel => 'Custom Cancel %{model}'
231
+ }
232
+ }
233
+ }
234
+
235
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
236
+ concat(builder.action(:submit, :as => :generic))
237
+ concat(builder.action(:reset, :as => :generic))
238
+ concat(builder.action(:cancel, :as => :generic))
239
+ end)
240
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Submit Post"]})
241
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset Post"]})
242
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel Post"]})
243
+ end
244
+ end
245
+
246
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
247
+ with_config :i18n_lookups_by_default, true do
248
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
249
+ concat(builder.action(:submit, :as => :generic))
250
+ concat(builder.action(:reset, :as => :generic))
251
+ concat(builder.action(:cancel, :as => :generic))
252
+ end)
253
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Submit"]})
254
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset"]})
255
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel"]})
256
+ end
257
+ end
258
+
259
+ end
260
+ end
261
+ end
262
+
263
+ describe 'when used on a new record' do
264
+ before do
265
+ @new_post.stub(:new_record?).and_return(true)
266
+ end
267
+
268
+ describe 'when explicit label is provided' do
269
+ it 'should render an input with the explicitly specified label' do
270
+ concat(semantic_form_for(@new_post) do |builder|
271
+ concat(builder.action(:submit, :as => :generic, :label => "Click!"))
272
+ concat(builder.action(:reset, :as => :generic, :label => "Reset!"))
273
+ concat(builder.action(:cancel, :as => :generic, :label => "Cancel!"))
274
+ end)
275
+ output_buffer.should have_tag('li.generic_action input[@value="Click!"]')
276
+ output_buffer.should have_tag('li.generic_action input[@value="Reset!"]')
277
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel!"]')
278
+ end
279
+ end
280
+
281
+ describe 'when no explicit label is provided' do
282
+ describe 'when no I18n-localized label is provided' do
283
+ before do
284
+ ::I18n.backend.store_translations :en, :formtastic => {
285
+ :create => 'Create %{model}',
286
+ :reset => 'Reset %{model}',
287
+ :cancel => 'Cancel %{model}'
288
+ }
289
+ end
290
+
291
+ after do
292
+ ::I18n.backend.reload!
293
+ end
294
+
295
+ it 'should render an input with default I18n-localized label (fallback)' do
296
+ concat(semantic_form_for(@new_post) do |builder|
297
+ concat(builder.action(:submit, :as => :generic))
298
+ concat(builder.action(:reset, :as => :generic))
299
+ concat(builder.action(:cancel, :as => :generic))
300
+ end)
301
+ output_buffer.should have_tag('li.generic_action input[@value="Create Post"]')
302
+ output_buffer.should have_tag('li.generic_action input[@value="Reset Post"]')
303
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel Post"]')
304
+ end
305
+ end
306
+
307
+ describe 'when I18n-localized label is provided' do
308
+ before do
309
+ ::I18n.backend.store_translations :en,
310
+ :formtastic => {
311
+ :actions => {
312
+ :create => 'Custom Create',
313
+ :reset => 'Custom Reset',
314
+ :cancel => 'Custom Cancel'
315
+ }
316
+ }
317
+ end
318
+
319
+ after do
320
+ ::I18n.backend.reload!
321
+ end
322
+
323
+ it 'should render an input with localized label (I18n)' do
324
+ with_config :i18n_lookups_by_default, true do
325
+ ::I18n.backend.store_translations :en,
326
+ :formtastic => {
327
+ :actions => {
328
+ :post => {
329
+ :create => 'Custom Create %{model}',
330
+ :reset => 'Custom Reset %{model}',
331
+ :cancel => 'Custom Cancel %{model}'
332
+ }
333
+ }
334
+ }
335
+ concat(semantic_form_for(@new_post) do |builder|
336
+ concat(builder.action(:submit, :as => :generic))
337
+ concat(builder.action(:reset, :as => :generic))
338
+ concat(builder.action(:cancel, :as => :generic))
339
+ end)
340
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Create Post"]})
341
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset Post"]})
342
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel Post"]})
343
+ end
344
+ end
345
+
346
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
347
+ with_config :i18n_lookups_by_default, true do
348
+ concat(semantic_form_for(@new_post) do |builder|
349
+ concat(builder.action(:submit, :as => :generic))
350
+ concat(builder.action(:reset, :as => :generic))
351
+ concat(builder.action(:cancel, :as => :generic))
352
+ end)
353
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Create"]})
354
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset"]})
355
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel"]})
356
+ end
357
+ end
358
+
359
+ end
360
+ end
361
+ end
362
+
363
+ describe 'when used on an existing record' do
364
+ before do
365
+ @new_post.stub(:persisted?).and_return(true)
366
+ end
367
+
368
+ describe 'when explicit label is provided' do
369
+ it 'should render an input with the explicitly specified label' do
370
+ concat(semantic_form_for(@new_post) do |builder|
371
+ concat(builder.action(:submit, :as => :generic, :label => "Click!"))
372
+ concat(builder.action(:reset, :as => :generic, :label => "Reset!"))
373
+ concat(builder.action(:cancel, :as => :generic, :label => "Cancel!"))
374
+ end)
375
+ output_buffer.should have_tag('li.generic_action input[@value="Click!"]')
376
+ output_buffer.should have_tag('li.generic_action input[@value="Reset!"]')
377
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel!"]')
378
+ end
379
+ end
380
+
381
+ describe 'when no explicit label is provided' do
382
+ describe 'when no I18n-localized label is provided' do
383
+ before do
384
+ ::I18n.backend.store_translations :en, :formtastic => {
385
+ :update => 'Save %{model}',
386
+ :reset => 'Reset %{model}',
387
+ :cancel => 'Cancel %{model}',
388
+ :actions => {
389
+ :message => {
390
+ :submit => 'Submit message',
391
+ :reset => 'Reset message',
392
+ :cancel => 'Cancel message'
393
+ }
394
+ }
395
+ }
396
+ end
397
+
398
+ after do
399
+ ::I18n.backend.reload!
400
+ end
401
+
402
+ it 'should render an input with default I18n-localized label (fallback)' do
403
+ concat(semantic_form_for(@new_post) do |builder|
404
+ concat(builder.action(:submit, :as => :generic))
405
+ concat(builder.action(:reset, :as => :generic))
406
+ concat(builder.action(:cancel, :as => :generic))
407
+ end)
408
+ output_buffer.should have_tag('li.generic_action input[@value="Save Post"]')
409
+ output_buffer.should have_tag('li.generic_action input[@value="Reset Post"]')
410
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel Post"]')
411
+ end
412
+
413
+ it 'should render an input with custom resource name localized label' do
414
+ concat(semantic_form_for(:post, :as => :message, :url => 'http://example.com') do |builder|
415
+ concat(builder.action(:submit, :as => :generic))
416
+ concat(builder.action(:reset, :as => :generic))
417
+ concat(builder.action(:cancel, :as => :generic))
418
+ end)
419
+ output_buffer.should have_tag('li.generic_action input[@value="Submit message"]')
420
+ output_buffer.should have_tag('li.generic_action input[@value="Cancel message"]')
421
+ output_buffer.should have_tag('li.generic_action input[@value="Reset message"]')
422
+ end
423
+ end
424
+
425
+ describe 'when I18n-localized label is provided' do
426
+ before do
427
+ ::I18n.backend.reload!
428
+ ::I18n.backend.store_translations :en,
429
+ :formtastic => {
430
+ :actions => {
431
+ :update => 'Custom Save',
432
+ :reset => 'Custom Reset',
433
+ :cancel => 'Custom Cancel'
434
+ }
435
+ }
436
+ end
437
+
438
+ after do
439
+ ::I18n.backend.reload!
440
+ end
441
+
442
+ it 'should render an input with localized label (I18n)' do
443
+ with_config :i18n_lookups_by_default, true do
444
+ ::I18n.backend.store_translations :en,
445
+ :formtastic => {
446
+ :actions => {
447
+ :post => {
448
+ :update => 'Custom Save %{model}',
449
+ :reset => 'Custom Reset %{model}',
450
+ :cancel => 'Custom Cancel %{model}'
451
+ }
452
+ }
453
+ }
454
+ concat(semantic_form_for(@new_post) do |builder|
455
+ concat(builder.action(:submit, :as => :generic))
456
+ concat(builder.action(:reset, :as => :generic))
457
+ concat(builder.action(:cancel, :as => :generic))
458
+ end)
459
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Save Post"]})
460
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset Post"]})
461
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel Post"]})
462
+ end
463
+ end
464
+
465
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
466
+ with_config :i18n_lookups_by_default, true do
467
+ concat(semantic_form_for(@new_post) do |builder|
468
+ concat(builder.action(:submit, :as => :generic))
469
+ concat(builder.action(:reset, :as => :generic))
470
+ concat(builder.action(:cancel, :as => :generic))
471
+ end)
472
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Save"]})
473
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Reset"]})
474
+ output_buffer.should have_tag(%Q{li.generic_action input[@value="Custom Cancel"]})
475
+ ::I18n.backend.store_translations :en, :formtastic => {}
476
+ end
477
+ end
478
+
479
+ end
480
+ end
481
+ end
482
+ end
483
+
484
+ describe 'when the model is two words' do
485
+
486
+ before do
487
+ output_buffer = ''
488
+ class ::UserPost
489
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
490
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
491
+
492
+ def id
493
+ end
494
+
495
+ def persisted?
496
+ end
497
+
498
+ # Rails does crappy human_name
499
+ def self.human_name
500
+ "User post"
501
+ end
502
+ end
503
+ @new_user_post = ::UserPost.new
504
+
505
+ @new_user_post.stub(:new_record?).and_return(true)
506
+ concat(semantic_form_for(@new_user_post, :url => '') do |builder|
507
+ concat(builder.action(:submit, :as => :generic))
508
+ concat(builder.action(:reset, :as => :generic))
509
+ concat(builder.action(:cancel, :as => :generic))
510
+ end)
511
+ end
512
+
513
+ it "should render the string as the value of the button" do
514
+ output_buffer.should have_tag('li input[@value="Create User post"]')
515
+ output_buffer.should have_tag('li input[@value="Reset User post"]')
516
+ output_buffer.should have_tag('li input[@value="Cancel User post"]')
517
+ end
518
+
519
+ end
520
+
521
+ end