formtastic 1.2.5 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/.yardopts +1 -0
  4. data/CHANGELOG +279 -0
  5. data/Gemfile +3 -0
  6. data/README.textile +155 -172
  7. data/RELEASE_PROCESS +7 -0
  8. data/Rakefile +52 -0
  9. data/app/assets/stylesheets/formtastic.css +275 -0
  10. data/app/assets/stylesheets/formtastic_ie6.css +27 -0
  11. data/app/assets/stylesheets/formtastic_ie7.css +17 -0
  12. data/formtastic.gemspec +51 -0
  13. data/lib/formtastic.rb +21 -1960
  14. data/lib/formtastic/engine.rb +7 -0
  15. data/lib/formtastic/form_builder.rb +83 -0
  16. data/lib/formtastic/helpers.rb +16 -0
  17. data/lib/formtastic/helpers/buttons_helper.rb +277 -0
  18. data/lib/formtastic/helpers/errors_helper.rb +113 -0
  19. data/lib/formtastic/helpers/fieldset_wrapper.rb +75 -0
  20. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  21. data/lib/formtastic/helpers/form_helper.rb +198 -0
  22. data/lib/formtastic/helpers/input_helper.rb +366 -0
  23. data/lib/formtastic/helpers/inputs_helper.rb +392 -0
  24. data/lib/formtastic/helpers/reflection.rb +33 -0
  25. data/lib/formtastic/helpers/semantic_form_helper.rb +11 -0
  26. data/lib/formtastic/html_attributes.rb +21 -0
  27. data/lib/formtastic/i18n.rb +1 -0
  28. data/lib/formtastic/inputs.rb +31 -0
  29. data/lib/formtastic/inputs/base.rb +61 -0
  30. data/lib/formtastic/inputs/base/associations.rb +31 -0
  31. data/lib/formtastic/inputs/base/choices.rb +103 -0
  32. data/lib/formtastic/inputs/base/collections.rb +94 -0
  33. data/lib/formtastic/inputs/base/database.rb +17 -0
  34. data/lib/formtastic/inputs/base/errors.rb +58 -0
  35. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  36. data/lib/formtastic/inputs/base/grouped_collections.rb +77 -0
  37. data/lib/formtastic/inputs/base/hints.rb +31 -0
  38. data/lib/formtastic/inputs/base/html.rb +52 -0
  39. data/lib/formtastic/inputs/base/labelling.rb +55 -0
  40. data/lib/formtastic/inputs/base/naming.rb +42 -0
  41. data/lib/formtastic/inputs/base/options.rb +18 -0
  42. data/lib/formtastic/inputs/base/stringish.rb +35 -0
  43. data/lib/formtastic/inputs/base/timeish.rb +128 -0
  44. data/lib/formtastic/inputs/base/validations.rb +166 -0
  45. data/lib/formtastic/inputs/base/wrapping.rb +40 -0
  46. data/lib/formtastic/inputs/boolean_input.rb +96 -0
  47. data/lib/formtastic/inputs/check_boxes_input.rb +179 -0
  48. data/lib/formtastic/inputs/country_input.rb +66 -0
  49. data/lib/formtastic/inputs/date_input.rb +14 -0
  50. data/lib/formtastic/inputs/datetime_input.rb +9 -0
  51. data/lib/formtastic/inputs/email_input.rb +40 -0
  52. data/lib/formtastic/inputs/file_input.rb +42 -0
  53. data/lib/formtastic/inputs/hidden_input.rb +66 -0
  54. data/lib/formtastic/inputs/number_input.rb +118 -0
  55. data/lib/formtastic/inputs/numeric_input.rb +21 -0
  56. data/lib/formtastic/inputs/password_input.rb +40 -0
  57. data/lib/formtastic/inputs/phone_input.rb +41 -0
  58. data/lib/formtastic/inputs/radio_input.rb +157 -0
  59. data/lib/formtastic/inputs/range_input.rb +119 -0
  60. data/lib/formtastic/inputs/search_input.rb +40 -0
  61. data/lib/formtastic/inputs/select_input.rb +210 -0
  62. data/lib/formtastic/inputs/string_input.rb +34 -0
  63. data/lib/formtastic/inputs/text_input.rb +47 -0
  64. data/lib/formtastic/inputs/time_input.rb +14 -0
  65. data/lib/formtastic/inputs/time_zone_input.rb +48 -0
  66. data/lib/formtastic/inputs/url_input.rb +40 -0
  67. data/lib/formtastic/localized_string.rb +105 -0
  68. data/lib/formtastic/railtie.rb +5 -7
  69. data/lib/formtastic/semantic_form_builder.rb +11 -0
  70. data/lib/formtastic/util.rb +6 -19
  71. data/lib/formtastic/version.rb +3 -0
  72. data/lib/generators/formtastic/install/install_generator.rb +28 -6
  73. data/lib/generators/templates/_form.html.erb +10 -4
  74. data/lib/generators/templates/_form.html.haml +8 -4
  75. data/lib/generators/templates/formtastic.rb +25 -32
  76. data/lib/locale/en.yml +1 -0
  77. data/lib/tasks/verify_rcov.rb +44 -0
  78. data/sample/basic_inputs.html +182 -0
  79. data/sample/config.ru +69 -0
  80. data/sample/index.html +14 -0
  81. data/spec/builder/custom_builder_spec.rb +109 -0
  82. data/spec/builder/error_proc_spec.rb +27 -0
  83. data/spec/builder/errors_spec.rb +193 -0
  84. data/spec/builder/semantic_fields_for_spec.rb +88 -0
  85. data/spec/helpers/buttons_helper_spec.rb +150 -0
  86. data/spec/helpers/commit_button_helper_spec.rb +470 -0
  87. data/spec/helpers/form_helper_spec.rb +135 -0
  88. data/spec/helpers/input_helper_spec.rb +837 -0
  89. data/spec/helpers/inputs_helper_spec.rb +562 -0
  90. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  91. data/spec/i18n_spec.rb +199 -0
  92. data/spec/inputs/boolean_input_spec.rb +184 -0
  93. data/spec/inputs/check_boxes_input_spec.rb +375 -0
  94. data/spec/inputs/country_input_spec.rb +133 -0
  95. data/spec/inputs/custom_input_spec.rb +52 -0
  96. data/spec/inputs/date_input_spec.rb +110 -0
  97. data/spec/inputs/datetime_input_spec.rb +115 -0
  98. data/spec/inputs/email_input_spec.rb +55 -0
  99. data/spec/inputs/file_input_spec.rb +59 -0
  100. data/spec/inputs/hidden_input_spec.rb +120 -0
  101. data/spec/inputs/include_blank_spec.rb +70 -0
  102. data/spec/inputs/label_spec.rb +104 -0
  103. data/spec/inputs/number_input_spec.rb +487 -0
  104. data/spec/inputs/numeric_input_spec.rb +41 -0
  105. data/spec/inputs/password_input_spec.rb +69 -0
  106. data/spec/inputs/phone_input_spec.rb +55 -0
  107. data/spec/inputs/placeholder_spec.rb +71 -0
  108. data/spec/inputs/radio_input_spec.rb +234 -0
  109. data/spec/inputs/range_input_spec.rb +477 -0
  110. data/spec/inputs/search_input_spec.rb +55 -0
  111. data/spec/inputs/select_input_spec.rb +545 -0
  112. data/spec/inputs/string_input_spec.rb +163 -0
  113. data/spec/inputs/text_input_spec.rb +158 -0
  114. data/spec/inputs/time_input_spec.rb +155 -0
  115. data/spec/inputs/time_zone_input_spec.rb +87 -0
  116. data/spec/inputs/url_input_spec.rb +55 -0
  117. data/spec/spec.opts +2 -0
  118. data/spec/spec_helper.rb +361 -0
  119. data/spec/support/custom_macros.rb +656 -0
  120. data/spec/support/deferred_garbage_collection.rb +21 -0
  121. data/spec/support/deprecation.rb +6 -0
  122. data/spec/support/test_environment.rb +30 -0
  123. metadata +306 -88
  124. data/generators/form/USAGE +0 -16
  125. data/generators/form/form_generator.rb +0 -111
  126. data/generators/formtastic/formtastic_generator.rb +0 -26
  127. data/init.rb +0 -5
  128. data/lib/formtastic/layout_helper.rb +0 -12
  129. data/lib/generators/formtastic/form/form_generator.rb +0 -84
  130. data/lib/generators/templates/formtastic.css +0 -145
  131. data/lib/generators/templates/formtastic_changes.css +0 -14
  132. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  133. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  134. data/rails/init.rb +0 -2
@@ -0,0 +1,7 @@
1
+ # Configure Rails 3.1 to have assert_select_jquery() in tests
2
+ module Formtastic
3
+ # Required for formtastic.css to be discoverable in the asset pipeline
4
+ # @private
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
@@ -0,0 +1,83 @@
1
+ module Formtastic
2
+ class FormBuilder < ActionView::Helpers::FormBuilder
3
+
4
+ def self.configure(name, value = nil)
5
+ class_attribute(name)
6
+ self.send(:"#{name}=", value)
7
+ end
8
+
9
+ configure :custom_namespace
10
+ configure :default_text_field_size
11
+ configure :default_text_area_height, 20
12
+ configure :default_text_area_width
13
+ configure :all_fields_required_by_default, true
14
+ configure :include_blank_for_select_by_default, true
15
+ configure :required_string, proc { Formtastic::Util.html_safe(%{<abbr title="#{Formtastic::I18n.t(:required)}">*</abbr>}) }
16
+ configure :optional_string, ''
17
+ configure :inline_errors, :sentence
18
+ configure :label_str_method, :humanize
19
+ configure :collection_label_methods, %w[to_label display_name full_name name title username login value to_s]
20
+ configure :collection_value_methods, %w[id to_s]
21
+ configure :custom_inline_order, {}
22
+ configure :file_methods, [ :file?, :public_filename, :filename ]
23
+ configure :file_metadata_suffixes, ['content_type', 'file_name', 'file_size']
24
+ configure :priority_countries, ["Australia", "Canada", "United Kingdom", "United States"]
25
+ configure :i18n_lookups_by_default, true
26
+ configure :escape_html_entities_in_hints_and_labels, true
27
+ configure :default_commit_button_accesskey
28
+ configure :default_inline_error_class, 'inline-errors'
29
+ configure :default_error_list_class, 'errors'
30
+ configure :default_hint_class, 'inline-hints'
31
+
32
+ attr_reader :template
33
+
34
+ attr_reader :auto_index
35
+
36
+ include Formtastic::HtmlAttributes
37
+
38
+ include Formtastic::Helpers::InputHelper
39
+ include Formtastic::Helpers::InputsHelper
40
+ include Formtastic::Helpers::ButtonsHelper
41
+ include Formtastic::Helpers::ErrorsHelper
42
+
43
+ # This is a wrapper around Rails' `ActionView::Helpers::FormBuilder#fields_for`, originally
44
+ # provided to ensure that the `:builder` from `semantic_form_for` was passed down into
45
+ # the nested `fields_for`. Rails 3 no longer requires us to do this, so this method is
46
+ # provided purely for backwards compatibility and DSL consistency.
47
+ #
48
+ # When constructing a `fields_for` form fragment *outside* of `semantic_form_for`, please use
49
+ # `Formtastic::Helpers::FormHelper#semantic_fields_for`.
50
+ #
51
+ # @see http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for ActionView::Helpers::FormBuilder#fields_for
52
+ # @see http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for ActionView::Helpers::FormHelper#fields_for
53
+ # @see Formtastic::Helpers::FormHelper#semantic_fields_for
54
+ #
55
+ # @example
56
+ # <% semantic_form_for @post do |post| %>
57
+ # <% post.semantic_fields_for :author do |author| %>
58
+ # <% author.inputs :name %>
59
+ # <% end %>
60
+ # <% end %>
61
+ #
62
+ # <form ...>
63
+ # <fieldset class="inputs">
64
+ # <ol>
65
+ # <li class="string"><input type='text' name='post[author][name]' id='post_author_name' /></li>
66
+ # </ol>
67
+ # </fieldset>
68
+ # </form>
69
+ #
70
+ # @todo is there a way to test the params structure of the Rails helper we wrap to ensure forward compatibility?
71
+ def semantic_fields_for(record_or_name_or_array, *args, &block)
72
+ # Add a :parent_builder to the args so that nested translations can be possible in Rails 3
73
+ options = args.extract_options!
74
+ options[:parent_builder] ||= self
75
+
76
+ # Wrap the Rails helper
77
+ fields_for(record_or_name_or_array, *(args << options), &block)
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
@@ -0,0 +1,16 @@
1
+ module Formtastic
2
+ # @private
3
+ module Helpers
4
+ autoload :ButtonsHelper, 'formtastic/helpers/buttons_helper'
5
+ autoload :ErrorsHelper, 'formtastic/helpers/errors_helper'
6
+ autoload :FieldsetWrapper, 'formtastic/helpers/fieldset_wrapper'
7
+ autoload :FileColumnDetection, 'formtastic/helpers/file_column_detection'
8
+ autoload :FormHelper, 'formtastic/helpers/form_helper'
9
+ autoload :InputHelper, 'formtastic/helpers/input_helper'
10
+ autoload :InputsHelper, 'formtastic/helpers/inputs_helper'
11
+ autoload :LabelHelper, 'formtastic/helpers/label_helper'
12
+ autoload :SemanticFormHelper, 'formtastic/helpers/semantic_form_helper'
13
+ autoload :Reflection, 'formtastic/helpers/reflection'
14
+ end
15
+ end
16
+
@@ -0,0 +1,277 @@
1
+ module Formtastic
2
+ module Helpers
3
+
4
+ # ButtonsHelper encapsulates the responsibilties of the {#buttons} and {#commit_button} helpers
5
+ # for submitting forms.
6
+ #
7
+ # {#buttons} is used to wrap the form's button(s) and actions in a `<fieldset>` and `<ol>`,
8
+ # with each item in the list containing the markup representing a single button.
9
+ #
10
+ # {#buttons} is usually called with a block containing a single {#commit_button} call:
11
+ #
12
+ # <%= semantic_form_for @post do |f| %>
13
+ # ...
14
+ # <%= f.buttons do %>
15
+ # <%= f.commit_button
16
+ # <% end %>
17
+ # <% end %>
18
+ #
19
+ # The HTML output will be something like:
20
+ #
21
+ # <form class="formtastic" method="post" action="...">
22
+ # ...
23
+ # <fieldset class="buttons">
24
+ # <ol>
25
+ # <li class="commit">
26
+ # <input type="submit" name="commit" value="Create Post" class="create">
27
+ # </li>
28
+ # </ol>
29
+ # </fieldset>
30
+ # </form>
31
+ #
32
+ # While this may seem slightly over-engineered, it is consistent with the way form inputs are
33
+ # handled, and makes room for other types of buttons and actions in future versions (such as
34
+ # cancel buttons or links, reset buttons and even alternate actions like 'save and continue
35
+ # editing').
36
+ #
37
+ # It's important to note that the `semantic_form_for` and {#buttons} blocks wrap the
38
+ # standard Rails `form_for` helper and form builder, so you have full access to every standard
39
+ # Rails form helper, with any HTML markup and ERB syntax, allowing you to "break free" from
40
+ # Formtastic when it doesn't suit to create your own buttons, links and actions:
41
+ #
42
+ # <%= semantic_form_for @post do |f| %>
43
+ # ...
44
+ # <%= f.buttons do %>
45
+ # <li class="save">
46
+ # <%= f.submit "Save" %>
47
+ # <li>
48
+ # <li class="cancel-link">
49
+ # Or <%= link_to "Cancel", posts_url %>
50
+ # <li>
51
+ # <% end %>
52
+ # <% end %>
53
+ #
54
+ # There are many other syntax variations and arguments to customize your form. See the
55
+ # full documentation of {#buttons} and {#commit_button} for details.
56
+ module ButtonsHelper
57
+ include Formtastic::Helpers::FieldsetWrapper
58
+ include Formtastic::LocalizedString
59
+
60
+ # Creates a fieldset and ol tag wrapping for use around a set of buttons. It can be
61
+ # called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc),
62
+ # or with a list of named buttons. These two examples are functionally equivalent:
63
+ #
64
+ # # With a block:
65
+ # <% semantic_form_for @post do |f| %>
66
+ # ...
67
+ # <% f.buttons do %>
68
+ # <%= f.commit_button %>
69
+ # <% end %>
70
+ # <% end %>
71
+ #
72
+ # # With a list of fields:
73
+ # <% semantic_form_for @post do |f| %>
74
+ # <%= f.buttons :commit %>
75
+ # <% end %>
76
+ #
77
+ # # Output:
78
+ # <form ...>
79
+ # <fieldset class="inputs">
80
+ # <ol>
81
+ # <li class="commit">
82
+ # <input type="submit" ...>
83
+ # </li>
84
+ # </ol>
85
+ # </fieldset>
86
+ # </form>
87
+ #
88
+ # Only one type of named button is supported at this time (:commit), and it's assumed to be
89
+ # the default choice, so this is also functionally equivalent, but may change in the future:
90
+ #
91
+ # # With no args:
92
+ # <% semantic_form_for @post do |f| %>
93
+ # ...
94
+ # <%= f.buttons %>
95
+ # <% end %>
96
+ #
97
+ # While this may seem slightly over-engineered, it is consistent with the way form inputs are
98
+ # handled, and makes room for other types of buttons and actions in future versions (such as
99
+ # cancel buttons or links, reset buttons and even alternate actions like 'save and continue
100
+ # editing').
101
+ #
102
+ # All options except `:name` and `:title` are passed down to the fieldset as HTML
103
+ # attributes (`id`, `class`, `style`...). If provided, the `:name` or `:title` option is
104
+ # passed into a `<legend>` inside the `<fieldset>` to name the set of buttons.
105
+ #
106
+ # @example Quickly add button(s) to the form, accepting all default values, options and behaviors
107
+ # <% semantic_form_for @post do |f| %>
108
+ # ...
109
+ # <%= f.buttons %>
110
+ # <% end %>
111
+ #
112
+ # @example Specify which named buttons you want, accepting all default values, options and behaviors
113
+ # <% semantic_form_for @post do |f| %>
114
+ # ...
115
+ # <%= f.buttons :commit %>
116
+ # <% end %>
117
+ #
118
+ # @example Specify which named buttons you want, and name the fieldset
119
+ # <% semantic_form_for @post do |f| %>
120
+ # ...
121
+ # <%= f.buttons :commit, :name => "Actions" %>
122
+ # or
123
+ # <%= f.buttons :commit, :label => "Actions" %>
124
+ # <% end %>
125
+ #
126
+ # @example Get full control over the commit_button options
127
+ # <% semantic_form_for @post do |f| %>
128
+ # ...
129
+ # <%= f.buttons do %>
130
+ # <%= f.commit_button :label => "Go", :button_html => { :class => "pretty" :disable_with => "Wait..." }, :wrapper_html => { ... }
131
+ # <% end %>
132
+ # <% end %>
133
+ #
134
+ # @example Make your own custom buttons, links or actions with standard Rails helpers or HTML
135
+ # <% semantic_form_for @post do |f| %>
136
+ # ...
137
+ # <%= f.buttons do %>
138
+ # <li class="submit">
139
+ # <%= f.submit "Submit" %>
140
+ # </li>
141
+ # <li class="reset">
142
+ # <input type="reset" value="Reset">
143
+ # </li>
144
+ # <li class="cancel">
145
+ # <%= link_to "Cancel", posts_url %>
146
+ # </li>
147
+ # <% end %>
148
+ # <% end %>
149
+ #
150
+ # @example Add HTML attributes to the fieldset
151
+ # <% semantic_form_for @post do |f| %>
152
+ # ...
153
+ # <%= f.buttons :commit, :style => "border:1px;" %>
154
+ # or
155
+ # <%= f.buttons :style => "border:1px;" do %>
156
+ # ...
157
+ # <% end %>
158
+ # <% end %>
159
+ #
160
+ # @option *args :label [String, Symbol]
161
+ # Optionally specify text for the legend of the fieldset
162
+ #
163
+ # @option *args :name [String, Symbol]
164
+ # Optionally specify text for the legend of the fieldset (alias for `:label`)
165
+ #
166
+ # @todo document i18n keys
167
+ def buttons(*args, &block)
168
+ html_options = args.extract_options!
169
+ html_options[:class] ||= "buttons"
170
+
171
+ if block_given?
172
+ field_set_and_list_wrapping(html_options, &block)
173
+ else
174
+ args = [:commit] if args.empty?
175
+ contents = args.map { |button_name| send(:"#{button_name}_button") }
176
+ field_set_and_list_wrapping(html_options, contents)
177
+ end
178
+ end
179
+
180
+ # Creates a submit input tag with the value "Save [model name]" (for existing records) or
181
+ # "Create [model name]" (for new records) by default. The output is an `<input>` tag with the
182
+ # `type` of `submit` and a class of either `create` or `update` (if Formtastic can determin if)
183
+ # the record is new or not) with `submit` as a fallback class. The submit button is wrapped in
184
+ # an `<li>` tag with a class of `commit`, and is intended to be rendered inside a {#buttons}
185
+ # block which wraps the button in a `fieldset` and `ol`.
186
+ #
187
+ # The textual value of the label can be changed from this default through the `:label`
188
+ # argument or through i18n.
189
+ #
190
+ # You can pass HTML attributes down to the `<input>` tag with the `:button_html` option, and
191
+ # pass HTML attributes to the wrapping `<li>` tag with the `:wrapper_html` option.
192
+ #
193
+ # @example Basic usage
194
+ # # form
195
+ # <%= semantic_form_for @post do |f| %>
196
+ # ...
197
+ # <%= f.buttons do %>
198
+ # <%= f.commit_button %>
199
+ # <% end %>
200
+ # <% end %>
201
+ #
202
+ # # output
203
+ # <form ...>
204
+ # ...
205
+ # <fieldset class="buttons">
206
+ # <input name="commit" type="submit" value="Create Post" class="create">
207
+ # </fieldset>
208
+ # </form>
209
+ #
210
+ # @example Set the value through the `:label` option
211
+ # <%= f.commit_button :label => "Go" %>
212
+ #
213
+ # @example Set the value through the optional first argument (like Rails' `f.submit`)
214
+ # <%= f.commit_button "Go" %>
215
+ #
216
+ # @example Pass HTML attributes down to the `<input>`
217
+ # <%= f.commit_button :button_html => { :class => 'pretty', :accesskey => 'g', :disable_with => "Wait..." } %>
218
+ # <%= f.commit_button :label => "Go", :button_html => { :class => 'pretty', :accesskey => 'g', :disable_with => "Wait..." } %>
219
+ # <%= f.commit_button "Go", :button_html => { :class => 'pretty', :accesskey => 'g', :disable_with => "Wait..." } %>
220
+ #
221
+ # @example Pass HTML attributes down to the `<li>` wrapper
222
+ # <%= f.commit_button :wrapper_html => { :class => 'special', :id => 'whatever' } %>
223
+ # <%= f.commit_button :label => "Go", :wrapper_html => { :class => 'special', :id => 'whatever' } %>
224
+ # <%= f.commit_button "Go", :wrapper_html => { :class => 'special', :id => 'whatever' } %>
225
+ #
226
+ # @option *args :label [String, Symbol]
227
+ # Override the label text with a String or a symbold for an i18n translation key
228
+ #
229
+ # @option *args :button_html [Hash]
230
+ # Override or add to the HTML attributes to be passed down to the `<input>` tag
231
+ #
232
+ # @option *args :wrapper_html [Hash]
233
+ # Override or add to the HTML attributes to be passed down to the wrapping `<li>` tag
234
+ #
235
+ # @todo document i18n keys
236
+ # @todo strange that `:accesskey` seems to be supported in the top level args as well as `:button_html`
237
+ def commit_button(*args)
238
+ options = args.extract_options!
239
+ text = options.delete(:label) || args.shift
240
+
241
+ if @object && (@object.respond_to?(:persisted?) || @object.respond_to?(:new_record?))
242
+ key = @object.persisted? ? :update : :create
243
+
244
+ # Deal with some complications with ActiveRecord::Base.human_name and two name models (eg UserPost)
245
+ # ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost")
246
+ # if there's no i18n, which is pretty crappy. In this circumstance we want to detect this
247
+ # fall back (human_name == name.humanize) and do our own thing name.underscore.humanize ("User Post")
248
+ if @object.class.model_name.respond_to?(:human)
249
+ object_name = @object.class.model_name.human
250
+ else
251
+ object_human_name = @object.class.human_name # default is UserPost => "Userpost", but i18n may do better ("User post")
252
+ crappy_human_name = @object.class.name.humanize # UserPost => "Userpost"
253
+ decent_human_name = @object.class.name.underscore.humanize # UserPost => "User post"
254
+ object_name = (object_human_name == crappy_human_name) ? decent_human_name : object_human_name
255
+ end
256
+ else
257
+ key = :submit
258
+ object_name = @object_name.to_s.send(label_str_method)
259
+ end
260
+
261
+ text = (localized_string(key, text, :action, :model => object_name) ||
262
+ Formtastic::I18n.t(key, :model => object_name)) unless text.is_a?(::String)
263
+
264
+ button_html = options.delete(:button_html) || {}
265
+ button_html.merge!(:class => [button_html[:class], key].compact.join(' '))
266
+
267
+ wrapper_html_class = ['commit', 'button'] # TODO: Add class reflecting on form action.
268
+ wrapper_html = options.delete(:wrapper_html) || {}
269
+ wrapper_html[:class] = (wrapper_html_class << wrapper_html[:class]).flatten.compact.join(' ')
270
+
271
+ accesskey = (options.delete(:accesskey) || default_commit_button_accesskey) unless button_html.has_key?(:accesskey)
272
+ button_html = button_html.merge(:accesskey => accesskey) if accesskey
273
+ template.content_tag(:li, Formtastic::Util.html_safe(submit(text, button_html)), wrapper_html)
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,113 @@
1
+ module Formtastic
2
+ module Helpers
3
+ module ErrorsHelper
4
+ include Formtastic::Helpers::FileColumnDetection
5
+ include Formtastic::Helpers::Reflection
6
+ include Formtastic::LocalizedString
7
+
8
+ INLINE_ERROR_TYPES = [:sentence, :list, :first]
9
+
10
+ # Generates an unordered list of error messages on the base object and optionally for a given
11
+ # set of named attribute. This is idea for rendering a block of error messages at the top of
12
+ # the form for hidden/special/virtual attributes (the Paperclip Rails plugin does this), or
13
+ # errors on the base model.
14
+ #
15
+ # A hash can be used as the last set of arguments to pass HTML attributes to the `<ul>`
16
+ # wrapper.
17
+ #
18
+ # @example A list of errors on the base model
19
+ # <%= semantic_form_for ... %>
20
+ # <%= f.semantic_errors %>
21
+ # ...
22
+ # <% end %>
23
+ #
24
+ # @example A list of errors on the base and named attributes
25
+ # <%= semantic_form_for ... %>
26
+ # <%= f.semantic_errors :something_special %>
27
+ # ...
28
+ # <% end %>
29
+ #
30
+ # @example A list of errors on the base model, with custom HTML attributes
31
+ # <%= semantic_form_for ... %>
32
+ # <%= f.semantic_errors :class => "awesome" %>
33
+ # ...
34
+ # <% end %>
35
+ #
36
+ # @example A list of errors on the base model and named attributes, with custom HTML attributes
37
+ # <%= semantic_form_for ... %>
38
+ # <%= f.semantic_errors :something_special, :something_else, :class => "awesome", :onclick => "Awesome();" %>
39
+ # ...
40
+ # <% end %>
41
+ def semantic_errors(*args)
42
+ html_options = args.extract_options!
43
+ args = args - [:base]
44
+ full_errors = args.inject([]) do |array, method|
45
+ attribute = localized_string(method, method.to_sym, :label) || humanized_attribute_name(method)
46
+ errors = Array(@object.errors[method.to_sym]).to_sentence
47
+ errors.present? ? array << [attribute, errors].join(" ") : array ||= []
48
+ end
49
+ full_errors << @object.errors[:base]
50
+ full_errors.flatten!
51
+ full_errors.compact!
52
+ return nil if full_errors.blank?
53
+ html_options[:class] ||= "errors"
54
+ template.content_tag(:ul, html_options) do
55
+ Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join)
56
+ end
57
+ end
58
+
59
+ # Generates error messages for the given method, used for displaying errors right near the
60
+ # field for data entry. Uses the `:inline_errors` config to determin the right presentation,
61
+ # which may be an ordered list, a paragraph sentence containing all errors, or a paragraph
62
+ # containing just the first error. If configred to `:none`, no error is shown.
63
+ #
64
+ # See the `:inline_errors` config documentation for more details.
65
+ #
66
+ # This method is mostly used internally, but can be used in your forms when creating your own
67
+ # custom inputs, so it's been made public and aliased to `errors_on`.
68
+ #
69
+ # @example
70
+ # <%= semantic_form_for @post do |f| %>
71
+ # <li class='my-custom-text-input'>
72
+ # <%= f.label(:body) %>
73
+ # <%= f.text_field(:body) %>
74
+ # <%= f.errors_on(:body) %>
75
+ # </li>
76
+ # <% end %>
77
+ #
78
+ # @deprecated See the README for the currently supported approach to custom inputs.
79
+ def inline_errors_for(method, options = {})
80
+ ActiveSupport::Deprecation.warn('inline_errors_for and errors_on are deprecated and will be removed on or after version 2.1', caller)
81
+ if render_inline_errors?
82
+ errors = error_keys(method, options).map{|x| @object.errors[x] }.flatten.compact.uniq
83
+ send(:"error_#{inline_errors}", [*errors], options) if errors.any?
84
+ else
85
+ nil
86
+ end
87
+ end
88
+ alias :errors_on :inline_errors_for
89
+
90
+
91
+ protected
92
+
93
+ def error_keys(method, options)
94
+ @methods_for_error ||= {}
95
+ @methods_for_error[method] ||= begin
96
+ methods_for_error = [method.to_sym]
97
+ methods_for_error << file_metadata_suffixes.map{|suffix| "#{method}_#{suffix}".to_sym} if is_file?(method, options)
98
+ methods_for_error << [association_primary_key_for_method(method)] if [:belongs_to, :has_many].include? association_macro_for_method(method)
99
+ methods_for_error.flatten.compact.uniq
100
+ end
101
+ end
102
+
103
+ def has_errors?(method, options)
104
+ methods_for_error = error_keys(method,options)
105
+ @object && @object.respond_to?(:errors) && methods_for_error.any?{|error| !@object.errors[error].blank?}
106
+ end
107
+
108
+ def render_inline_errors?
109
+ @object && @object.respond_to?(:errors) && Formtastic::FormBuilder::INLINE_ERROR_TYPES.include?(inline_errors)
110
+ end
111
+ end
112
+ end
113
+ end