formtastic-rails3 0.9.7 → 0.9.10.0
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.
- data/README.textile +23 -7
- data/Rakefile +27 -4
- data/generators/formtastic/templates/formtastic.css +9 -7
- data/generators/formtastic/templates/formtastic.rb +1 -1
- data/generators/formtastic/templates/formtastic_changes.css +4 -0
- data/lib/formtastic.rb +190 -142
- data/lib/formtastic/i18n.rb +8 -5
- data/lib/formtastic/railtie.rb +12 -0
- data/lib/formtastic/util.rb +35 -0
- data/lib/generators/formtastic/form/form_generator.rb +4 -3
- data/lib/generators/formtastic/install/install_generator.rb +2 -1
- data/rails/init.rb +5 -0
- data/spec/buttons_spec.rb +25 -8
- data/spec/commit_button_spec.rb +88 -64
- data/spec/custom_builder_spec.rb +1 -1
- data/spec/custom_macros.rb +67 -26
- data/spec/error_proc_spec.rb +1 -1
- data/spec/errors_spec.rb +21 -1
- data/spec/form_helper_spec.rb +47 -15
- data/spec/i18n_spec.rb +40 -19
- data/spec/include_blank_spec.rb +9 -5
- data/spec/input_spec.rb +224 -76
- data/spec/inputs/boolean_input_spec.rb +22 -11
- data/spec/inputs/check_boxes_input_spec.rb +103 -11
- data/spec/inputs/country_input_spec.rb +46 -8
- data/spec/inputs/date_input_spec.rb +80 -55
- data/spec/inputs/datetime_input_spec.rb +134 -83
- data/spec/inputs/file_input_spec.rb +4 -3
- data/spec/inputs/hidden_input_spec.rb +17 -3
- data/spec/inputs/numeric_input_spec.rb +3 -3
- data/spec/inputs/password_input_spec.rb +3 -3
- data/spec/inputs/radio_input_spec.rb +28 -11
- data/spec/inputs/select_input_spec.rb +122 -46
- data/spec/inputs/string_input_spec.rb +3 -3
- data/spec/inputs/text_input_spec.rb +4 -3
- data/spec/inputs/time_input_spec.rb +109 -53
- data/spec/inputs/time_zone_input_spec.rb +15 -7
- data/spec/inputs_spec.rb +85 -39
- data/spec/label_spec.rb +1 -1
- data/spec/layout_helper_spec.rb +5 -16
- data/spec/semantic_errors_spec.rb +7 -7
- data/spec/semantic_fields_for_spec.rb +5 -4
- data/spec/spec_helper.rb +102 -36
- metadata +11 -14
- data/lib/generators/formtastic/form/templates/_form.html.erb +0 -5
- data/lib/generators/formtastic/form/templates/_form.html.haml +0 -4
- data/lib/generators/formtastic/install/templates/formtastic.css +0 -144
- data/lib/generators/formtastic/install/templates/formtastic.rb +0 -58
- data/lib/generators/formtastic/install/templates/formtastic_changes.css +0 -10
- data/spec/inputs/currency_input_spec.rb +0 -80
data/README.textile
CHANGED
@@ -173,7 +173,7 @@ If you want to customize the label text, or render some hint text below the fiel
|
|
173
173
|
<% end %>
|
174
174
|
</pre>
|
175
175
|
|
176
|
-
Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
|
176
|
+
Nested forms (Rails 2.3) are also supported (don't forget your models need to be setup correctly with accepts_nested_attributes_for – search the Rails docs). You can do it in the Rails way:
|
177
177
|
|
178
178
|
<pre>
|
179
179
|
<% semantic_form_for @post do |form| %>
|
@@ -206,7 +206,7 @@ When working in has many association, you can even supply @"%i"@ in your fieldse
|
|
206
206
|
</pre>
|
207
207
|
|
208
208
|
|
209
|
-
Customize HTML attributes for any input using the @:input_html@ option. Typically
|
209
|
+
Customize HTML attributes for any input using the @:input_html@ option. Typically this is used to disable the input, change the size of a text field, change the rows in a textarea, or even to add a special class to an input to attach special behavior like "autogrow":http://plugins.jquery.com/project/autogrow textareas:
|
210
210
|
|
211
211
|
<pre>
|
212
212
|
<% semantic_form_for @post do |form| %>
|
@@ -239,6 +239,23 @@ Customize the HTML attributes for the @<li>@ wrapper around every input with the
|
|
239
239
|
<% end %>
|
240
240
|
</pre>
|
241
241
|
|
242
|
+
Many inputs provide a collection of options to choose from (like @:select@, @:radio@, @:check_boxes@, @:boolean@). In many cases, Formtastic can find choices through the model associations, but if you want to use your own set of choices, the @:collection@ option is what you want. You can pass in an Array of objects, an array of Strings, a Hash... Throw almost anything at it! Examples:
|
243
|
+
|
244
|
+
<pre>
|
245
|
+
f.input :authors, :as => :check_boxes, :collection => User.find(:all, :order => "last_name ASC")
|
246
|
+
f.input :authors, :as => :check_boxes, :collection => current_user.company.users.active
|
247
|
+
f.input :authors, :as => :check_boxes, :collection => [@justin, @kate]
|
248
|
+
f.input :authors, :as => :check_boxes, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
249
|
+
f.input :author, :as => :select, :collection => Author.find(:all)
|
250
|
+
f.input :author, :as => :select, :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
|
251
|
+
f.input :author, :as => :select, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
252
|
+
f.input :author, :as => :radio, :collection => User.find(:all)
|
253
|
+
f.input :author, :as => :radio, :collection => [@justin, @kate]
|
254
|
+
f.input :author, :as => :radio, :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
|
255
|
+
f.input :author, :as => :radio, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
256
|
+
f.input :admin, :as => :radio, :collection => ["Yes!", "No"]
|
257
|
+
</pre>
|
258
|
+
|
242
259
|
|
243
260
|
h2. The Available Inputs
|
244
261
|
|
@@ -258,10 +275,10 @@ The Formtastic input types:
|
|
258
275
|
* @:numeric@ - a text field (just like string). Default for column types: @:integer@, @:float@, and @:decimal@.
|
259
276
|
* @:file@ - a file field. Default for file-attachment attributes matching: "paperclip":http://github.com/thoughtbot/paperclip or "attachment_fu":http://github.com/technoweenie/attachment_fu.
|
260
277
|
* @:country@ - a select menu of country names. Default for column types: :string with name @"country"@ - requires a *country_select* plugin to be installed.
|
261
|
-
* @:currency@ - a select menu of currencies. Default for column types: :string with name @"currency"@ - requires a *currency_select* plugin to be installed.
|
262
278
|
* @:hidden@ - a hidden field. Creates a hidden field (added for compatibility).
|
263
279
|
|
264
|
-
The
|
280
|
+
The comments in the code are pretty good for each of these (what it does, what the output is, what the options are, etc.) so go check it out.
|
281
|
+
|
265
282
|
|
266
283
|
h2. Delegation for label lookups
|
267
284
|
|
@@ -320,7 +337,7 @@ Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the
|
|
320
337
|
title: "Choose a good title for you post."
|
321
338
|
body: "Write something inspiring here."
|
322
339
|
actions:
|
323
|
-
create: "Create my {
|
340
|
+
create: "Create my %{model}"
|
324
341
|
update: "Save changes"
|
325
342
|
dummie: "Launch!"
|
326
343
|
</pre>
|
@@ -337,7 +354,7 @@ Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the
|
|
337
354
|
<%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
338
355
|
<% end %>
|
339
356
|
<% form.buttons do %>
|
340
|
-
<%= form.commit_button %> # => "Create my {
|
357
|
+
<%= form.commit_button %> # => "Create my %{model}"
|
341
358
|
<% end %>
|
342
359
|
<% end %>
|
343
360
|
</pre>
|
@@ -514,7 +531,6 @@ There are none, but...
|
|
514
531
|
|
515
532
|
* if you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin is installed, you won't have to specify the @:required@ option (it checks the validations on the model instead).
|
516
533
|
* if you want to use the @:country@ input, you'll need to install the "iso-3166-country-select plugin":http://github.com/rails/iso-3166-country-select (or any other country_select plugin with the same API).
|
517
|
-
* if you want to use the @:currency@ input, you'll need to install the "currency_select plugin":http://github.com/gavinlaking/currency_select (or any other currency_select plugin with the same API).
|
518
534
|
* "rspec":http://github.com/dchelimsky/rspec/, "rspec_hpricot_matchers":http://rubyforge.org/projects/rspec-hpricot/ and "rcov":http://github.com/relevance/rcov gems (plus any of their own dependencies) are required for the test suite.
|
519
535
|
|
520
536
|
|
data/Rakefile
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require 'rubygems'
|
2
3
|
require 'rake'
|
3
4
|
require 'rake/rdoctask'
|
4
5
|
|
5
6
|
begin
|
7
|
+
gem 'rspec', '>= 1.2.6'
|
8
|
+
gem 'rspec-rails', '>= 1.2.6'
|
9
|
+
require 'spec'
|
6
10
|
require 'spec/rake/spectask'
|
7
11
|
rescue LoadError
|
8
12
|
begin
|
9
|
-
|
10
|
-
require '
|
13
|
+
require 'rspec/core/rake_task.rb'
|
14
|
+
require 'rspec/core/version'
|
11
15
|
rescue LoadError
|
12
16
|
puts "[formtastic:] RSpec - or one of it's dependencies - is not available. Install it with: sudo gem install rspec-rails"
|
13
17
|
end
|
@@ -55,8 +59,8 @@ begin
|
|
55
59
|
|
56
60
|
# Runtime dependencies: When installing Formtastic these will be checked if they are installed.
|
57
61
|
# Will be offered to install these if they are not already installed.
|
58
|
-
s.add_dependency 'activesupport', '>=
|
59
|
-
s.add_dependency 'actionpack', '>=
|
62
|
+
s.add_dependency 'activesupport', '>= 3.0.0beta3'
|
63
|
+
s.add_dependency 'actionpack', '>= 3.0.0beta3'
|
60
64
|
|
61
65
|
# Development dependencies. Not installed by default.
|
62
66
|
# Install with: sudo gem install formtastic --development
|
@@ -101,3 +105,22 @@ if defined?(Spec)
|
|
101
105
|
t.rcov_opts = ['--exclude', 'spec,Library']
|
102
106
|
end
|
103
107
|
end
|
108
|
+
|
109
|
+
if defined?(Rspec)
|
110
|
+
desc 'Test the formtastic plugin.'
|
111
|
+
Rspec::Core::RakeTask.new('spec') do |t|
|
112
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'Test the formtastic plugin with specdoc formatting and colors'
|
116
|
+
Rspec::Core::RakeTask.new('specdoc') do |t|
|
117
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
118
|
+
end
|
119
|
+
|
120
|
+
desc "Run all examples with RCov"
|
121
|
+
Rspec::Core::RakeTask.new('examples_with_rcov') do |t|
|
122
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
123
|
+
t.rcov = true
|
124
|
+
t.rcov_opts = ['--exclude', 'spec,Library']
|
125
|
+
end
|
126
|
+
end
|
@@ -19,7 +19,7 @@ form.formtastic ol, form.formtastic ul { list-style:none; }
|
|
19
19
|
form.formtastic abbr, form.formtastic acronym { border:0; font-variant:normal; }
|
20
20
|
form.formtastic input, form.formtastic textarea, form.formtastic select { font-family:inherit; font-size:inherit; font-weight:inherit; }
|
21
21
|
form.formtastic input, form.formtastic textarea, form.formtastic select { font-size:100%; }
|
22
|
-
form.formtastic legend { color:#000; }
|
22
|
+
form.formtastic legend { white-space:normal; color:#000; }
|
23
23
|
|
24
24
|
|
25
25
|
/* SEMANTIC ERRORS
|
@@ -68,7 +68,7 @@ form.formtastic fieldset > ol > li > li label input { line-height:100%; vertical
|
|
68
68
|
/* NESTED FIELDSETS AND LEGENDS (radio, check boxes and date/time inputs use nested fieldsets)
|
69
69
|
--------------------------------------------------------------------------------------------------*/
|
70
70
|
form.formtastic fieldset > ol > li fieldset { position:relative; }
|
71
|
-
form.formtastic fieldset > ol > li fieldset legend { position:absolute; width:
|
71
|
+
form.formtastic fieldset > ol > li fieldset legend { position:absolute; width:95%; padding-top:0.1em; left: 0px; }
|
72
72
|
form.formtastic fieldset > ol > li fieldset legend span { position:absolute; }
|
73
73
|
form.formtastic fieldset > ol > li fieldset legend.label label { position:absolute; }
|
74
74
|
form.formtastic fieldset > ol > li fieldset ol { float:left; width:74%; margin:0; padding:0 0 0 25%; }
|
@@ -89,9 +89,9 @@ form.formtastic fieldset > ol > li ul.errors li { padding:0; border:none; displa
|
|
89
89
|
|
90
90
|
/* STRING & NUMERIC OVERRIDES
|
91
91
|
--------------------------------------------------------------------------------------------------*/
|
92
|
-
form.formtastic fieldset > ol > li.string input { width:74%; }
|
93
|
-
form.formtastic fieldset > ol > li.password input { width:
|
94
|
-
form.formtastic fieldset > ol > li.numeric input { width:74%; }
|
92
|
+
form.formtastic fieldset > ol > li.string input { max-width:74%; }
|
93
|
+
form.formtastic fieldset > ol > li.password input { max-width: 13em; }
|
94
|
+
form.formtastic fieldset > ol > li.numeric input { max-width:74%; }
|
95
95
|
|
96
96
|
|
97
97
|
/* TEXTAREA OVERRIDES
|
@@ -100,9 +100,11 @@ form.formtastic fieldset > ol > li.text textarea { width:74%; }
|
|
100
100
|
|
101
101
|
|
102
102
|
/* HIDDEN OVERRIDES
|
103
|
+
The dual declarations are required because of our clearfix display hack on the LIs, which is more
|
104
|
+
specific than the more general rule below. TODO: Revist the clearing hack and this rule.
|
103
105
|
--------------------------------------------------------------------------------------------------*/
|
104
|
-
form.formtastic fieldset
|
105
|
-
|
106
|
+
form.formtastic fieldset ol li.hidden,
|
107
|
+
html[xmlns] form.formtastic fieldset ol li.hidden { display:none; }
|
106
108
|
|
107
109
|
/* BOOLEAN OVERRIDES
|
108
110
|
--------------------------------------------------------------------------------------------------*/
|
@@ -28,7 +28,7 @@
|
|
28
28
|
# Formtastic::SemanticFormBuilder.inline_errors = :sentence
|
29
29
|
|
30
30
|
# Set the method to call on label text to transform or format it for human-friendly
|
31
|
-
# reading when formtastic is
|
31
|
+
# reading when formtastic is used without object. Defaults to :humanize.
|
32
32
|
# Formtastic::SemanticFormBuilder.label_str_method = :humanize
|
33
33
|
|
34
34
|
# Set the array of methods to try calling on parent objects in :select and :radio inputs
|
@@ -7,4 +7,8 @@ For example, to make the inline hint paragraphs a little darker in color than th
|
|
7
7
|
|
8
8
|
form.formtastic fieldset > ol > li p.inline-hints { color:#333; }
|
9
9
|
|
10
|
+
HINT:
|
11
|
+
The following style may be *conditionally* included for improved support on older versions of IE(<8)
|
12
|
+
form.formtastic fieldset ol li fieldset legend { margin-left: -6px;}
|
13
|
+
|
10
14
|
--------------------------------------------------------------------------------------------------*/
|
data/lib/formtastic.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require File.join(File.dirname(__FILE__), *%w[formtastic i18n])
|
3
|
+
require File.join(File.dirname(__FILE__), *%w[formtastic util])
|
4
|
+
require File.join(File.dirname(__FILE__), *%w[formtastic railtie]) if defined?(::Rails::Railtie)
|
3
5
|
|
4
6
|
module Formtastic #:nodoc:
|
5
7
|
|
@@ -9,7 +11,7 @@ module Formtastic #:nodoc:
|
|
9
11
|
@@default_text_area_height = 20
|
10
12
|
@@all_fields_required_by_default = true
|
11
13
|
@@include_blank_for_select_by_default = true
|
12
|
-
@@required_string = proc { %{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>} }
|
14
|
+
@@required_string = proc { ::Formtastic::Util.html_safe(%{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>}) }
|
13
15
|
@@optional_string = ''
|
14
16
|
@@inline_errors = :sentence
|
15
17
|
@@label_str_method = :humanize
|
@@ -17,13 +19,12 @@ module Formtastic #:nodoc:
|
|
17
19
|
@@inline_order = [ :input, :hints, :errors ]
|
18
20
|
@@file_methods = [ :file?, :public_filename, :filename ]
|
19
21
|
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
20
|
-
@@priority_currencies = ["US Dollar", "Euro"]
|
21
22
|
@@i18n_lookups_by_default = false
|
22
|
-
@@default_commit_button_accesskey = nil
|
23
|
+
@@default_commit_button_accesskey = nil
|
23
24
|
|
24
25
|
cattr_accessor :default_text_field_size, :default_text_area_height, :all_fields_required_by_default, :include_blank_for_select_by_default,
|
25
26
|
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
26
|
-
:inline_order, :file_methods, :priority_countries, :
|
27
|
+
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :default_commit_button_accesskey
|
27
28
|
|
28
29
|
RESERVED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
|
29
30
|
|
@@ -77,6 +78,13 @@ module Formtastic #:nodoc:
|
|
77
78
|
# <% end %>
|
78
79
|
#
|
79
80
|
def input(method, options = {})
|
81
|
+
if options.key?(:selected) || options.key?(:checked) || options.key?(:default)
|
82
|
+
::ActiveSupport::Deprecation.warn(
|
83
|
+
"The :selected, :checked (and :default) options are deprecated in Formtastic and will be removed from 1.0. " <<
|
84
|
+
"Please set default values in your models (using an after_initialize callback) or in your controller set-up. " <<
|
85
|
+
"See http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html for more information.", caller)
|
86
|
+
end
|
87
|
+
|
80
88
|
options[:required] = method_required?(method) unless options.key?(:required)
|
81
89
|
options[:as] ||= default_input_type(method, options)
|
82
90
|
|
@@ -93,13 +101,13 @@ module Formtastic #:nodoc:
|
|
93
101
|
end
|
94
102
|
|
95
103
|
input_parts = @@inline_order.dup
|
96
|
-
input_parts
|
104
|
+
input_parts = input_parts - [:errors, :hints] if options[:as] == :hidden
|
97
105
|
|
98
106
|
list_item_content = input_parts.map do |type|
|
99
107
|
send(:"inline_#{type}_for", method, options)
|
100
108
|
end.compact.join("\n")
|
101
109
|
|
102
|
-
return template.content_tag(:li, list_item_content, wrapper_html)
|
110
|
+
return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html)
|
103
111
|
end
|
104
112
|
|
105
113
|
# Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
|
@@ -253,7 +261,7 @@ module Formtastic #:nodoc:
|
|
253
261
|
html_options = args.extract_options!
|
254
262
|
html_options[:class] ||= "inputs"
|
255
263
|
html_options[:name] = title
|
256
|
-
|
264
|
+
|
257
265
|
if html_options[:for] # Nested form
|
258
266
|
inputs_for_nested_attributes(*(args << html_options), &block)
|
259
267
|
elsif block_given?
|
@@ -268,7 +276,7 @@ module Formtastic #:nodoc:
|
|
268
276
|
legend = args.shift if args.first.is_a?(::String)
|
269
277
|
contents = args.collect { |method| input(method.to_sym) }
|
270
278
|
args.unshift(legend) if legend.present?
|
271
|
-
|
279
|
+
|
272
280
|
field_set_and_list_wrapping(*((args << html_options) << contents))
|
273
281
|
end
|
274
282
|
end
|
@@ -319,10 +327,14 @@ module Formtastic #:nodoc:
|
|
319
327
|
# ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost")
|
320
328
|
# if there's no i18n, which is pretty crappy. In this circumstance we want to detect this
|
321
329
|
# fall back (human_name == name.humanize) and do our own thing name.underscore.humanize ("User Post")
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
330
|
+
if @object.class.model_name.respond_to?(:human)
|
331
|
+
object_name = @object.class.model_name.human
|
332
|
+
else
|
333
|
+
object_human_name = @object.class.human_name # default is UserPost => "Userpost", but i18n may do better ("User post")
|
334
|
+
crappy_human_name = @object.class.name.humanize # UserPost => "Userpost"
|
335
|
+
decent_human_name = @object.class.name.underscore.humanize # UserPost => "User post"
|
336
|
+
object_name = (object_human_name == crappy_human_name) ? decent_human_name : object_human_name
|
337
|
+
end
|
326
338
|
else
|
327
339
|
key = :submit
|
328
340
|
object_name = @object_name.to_s.send(@@label_str_method)
|
@@ -335,8 +347,8 @@ module Formtastic #:nodoc:
|
|
335
347
|
button_html.merge!(:class => [button_html[:class], key].compact.join(' '))
|
336
348
|
element_class = ['commit', options.delete(:class)].compact.join(' ') # TODO: Add class reflecting on form action.
|
337
349
|
accesskey = (options.delete(:accesskey) || @@default_commit_button_accesskey) unless button_html.has_key?(:accesskey)
|
338
|
-
button_html = button_html.merge(:accesskey => accesskey) if accesskey
|
339
|
-
template.content_tag(:li, self.submit(text, button_html), :class => element_class)
|
350
|
+
button_html = button_html.merge(:accesskey => accesskey) if accesskey
|
351
|
+
template.content_tag(:li, Formtastic::Util.html_safe(self.submit(text, button_html)), :class => element_class)
|
340
352
|
end
|
341
353
|
|
342
354
|
# A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
|
@@ -393,11 +405,15 @@ module Formtastic #:nodoc:
|
|
393
405
|
text = options_or_text
|
394
406
|
options ||= {}
|
395
407
|
end
|
408
|
+
|
396
409
|
text = localized_string(method, text, :label) || humanized_attribute_name(method)
|
397
410
|
text += required_or_optional_string(options.delete(:required))
|
411
|
+
text = Formtastic::Util.html_safe(text)
|
398
412
|
|
399
413
|
# special case for boolean (checkbox) labels, which have a nested input
|
400
|
-
|
414
|
+
if options.key?(:label_prefix_for_nested_input)
|
415
|
+
text = options.delete(:label_prefix_for_nested_input) + text
|
416
|
+
end
|
401
417
|
|
402
418
|
input_name = options.delete(:input_name) || method
|
403
419
|
super(input_name, text, options)
|
@@ -416,8 +432,10 @@ module Formtastic #:nodoc:
|
|
416
432
|
#
|
417
433
|
def inline_errors_for(method, options = nil) #:nodoc:
|
418
434
|
if render_inline_errors?
|
419
|
-
errors = @object.errors[method.to_sym]
|
420
|
-
|
435
|
+
errors = [@object.errors[method.to_sym]]
|
436
|
+
errors << [@object.errors[association_primary_key(method)]] if association_macro_for_method(method) == :belongs_to
|
437
|
+
errors = errors.flatten.compact.uniq
|
438
|
+
send(:"error_#{@@inline_errors}", [*errors]) if errors.any?
|
421
439
|
else
|
422
440
|
nil
|
423
441
|
end
|
@@ -440,13 +458,13 @@ module Formtastic #:nodoc:
|
|
440
458
|
errors = Array(@object.errors[method.to_sym]).to_sentence
|
441
459
|
errors.present? ? array << [attribute, errors].join(" ") : array ||= []
|
442
460
|
end
|
443
|
-
full_errors << @object.errors
|
461
|
+
full_errors << @object.errors[:base]
|
444
462
|
full_errors.flatten!
|
445
463
|
full_errors.compact!
|
446
464
|
return nil if full_errors.blank?
|
447
465
|
html_options[:class] ||= "errors"
|
448
466
|
template.content_tag(:ul, html_options) do
|
449
|
-
full_errors.map { |error| template.content_tag(:li, error) }.join
|
467
|
+
Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join)
|
450
468
|
end
|
451
469
|
end
|
452
470
|
|
@@ -477,6 +495,18 @@ module Formtastic #:nodoc:
|
|
477
495
|
[]
|
478
496
|
end
|
479
497
|
end
|
498
|
+
|
499
|
+
# Returns nil, or a symbol like :belongs_to or :has_many
|
500
|
+
def association_macro_for_method(method) #:nodoc:
|
501
|
+
reflection = self.reflection_for(method)
|
502
|
+
reflection.macro if reflection
|
503
|
+
end
|
504
|
+
|
505
|
+
def association_primary_key(method)
|
506
|
+
reflection = self.reflection_for(method)
|
507
|
+
reflection.options[:foreign_key] if reflection && !reflection.options[:foreign_key].blank?
|
508
|
+
:"#{method}_id"
|
509
|
+
end
|
480
510
|
|
481
511
|
# Prepare options to be sent to label
|
482
512
|
#
|
@@ -498,9 +528,9 @@ module Formtastic #:nodoc:
|
|
498
528
|
raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
|
499
529
|
'but the block does not accept any argument.' if block.arity <= 0
|
500
530
|
|
501
|
-
proc { |f| f.inputs(*args){ block.call(f) } }
|
531
|
+
proc { |f| return f.inputs(*args){ block.call(f) } }
|
502
532
|
else
|
503
|
-
proc { |f| f.inputs(*args) }
|
533
|
+
proc { |f| return f.inputs(*args) }
|
504
534
|
end
|
505
535
|
|
506
536
|
fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten
|
@@ -519,23 +549,31 @@ module Formtastic #:nodoc:
|
|
519
549
|
# * if the :required option was provided in the options hash, the true/false value will be
|
520
550
|
# returned immediately, allowing the view to override any guesswork that follows:
|
521
551
|
#
|
522
|
-
# * if the :required option isn't provided in the options hash,
|
523
|
-
#
|
552
|
+
# * if the :required option isn't provided in the options hash, and the ValidationReflection
|
553
|
+
# plugin is installed (http://github.com/redinger/validation_reflection), or the object is
|
554
|
+
# an ActiveModel, true is returned
|
555
|
+
# if the validates_presence_of macro has been used in the class for this attribute, or false
|
556
|
+
# otherwise.
|
524
557
|
#
|
525
|
-
# * if the :required option isn't provided, and
|
558
|
+
# * if the :required option isn't provided, and validates_presence_of can't be determined, the
|
526
559
|
# configuration option @@all_fields_required_by_default is used.
|
527
560
|
#
|
528
561
|
def method_required?(attribute) #:nodoc:
|
529
|
-
if @object && @object.class.respond_to?(:
|
562
|
+
if @object && @object.class.respond_to?(:reflect_on_validations_for)
|
530
563
|
attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
|
531
564
|
|
532
|
-
@object.class.
|
533
|
-
validation.
|
534
|
-
validation.
|
565
|
+
@object.class.reflect_on_validations_for(attribute_sym).any? do |validation|
|
566
|
+
validation.macro == :validates_presence_of &&
|
567
|
+
validation.name == attribute_sym &&
|
535
568
|
(validation.options.present? ? options_require_validation?(validation.options) : true)
|
536
569
|
end
|
537
570
|
else
|
538
|
-
|
571
|
+
if @object && @object.class.respond_to?(:validators_on)
|
572
|
+
attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
|
573
|
+
!@object.class.validators_on(attribute_sym).find{|validator| (validator.kind == :presence) && (validator.options.present? ? options_require_validation?(validator.options) : true)}.nil?
|
574
|
+
else
|
575
|
+
@@all_fields_required_by_default
|
576
|
+
end
|
539
577
|
end
|
540
578
|
end
|
541
579
|
|
@@ -639,9 +677,9 @@ module Formtastic #:nodoc:
|
|
639
677
|
# </select>
|
640
678
|
#
|
641
679
|
#
|
642
|
-
# You can customize the options available in the select by passing in a collection (an Array or
|
643
|
-
# Hash) through the :collection option. If not provided, the choices are found by inferring the
|
644
|
-
# parent's class name from the method name and simply calling find(:all) on it
|
680
|
+
# You can customize the options available in the select by passing in a collection (an Array or
|
681
|
+
# Hash) through the :collection option. If not provided, the choices are found by inferring the
|
682
|
+
# parent's class name from the method name and simply calling find(:all) on it
|
645
683
|
# (VehicleOwner.find(:all) in the example above).
|
646
684
|
#
|
647
685
|
# Examples:
|
@@ -673,9 +711,9 @@ module Formtastic #:nodoc:
|
|
673
711
|
# f.input :author, :value_method => Proc.new { |a| "author_#{a.login}" }
|
674
712
|
#
|
675
713
|
# You can pre-select a specific option value by passing in the :selected option.
|
676
|
-
#
|
714
|
+
#
|
677
715
|
# Examples:
|
678
|
-
#
|
716
|
+
#
|
679
717
|
# f.input :author, :selected => current_user.id
|
680
718
|
# f.input :author, :value_method => :login, :selected => current_user.login
|
681
719
|
# f.input :authors, :value_method => :login, :selected => Author.most_popular.collect(&:id)
|
@@ -691,14 +729,14 @@ module Formtastic #:nodoc:
|
|
691
729
|
# a prompt with the :prompt option, or disable the blank option with :include_blank => false.
|
692
730
|
#
|
693
731
|
#
|
694
|
-
# You can group the options in optgroup elements by passing the :group_by option
|
732
|
+
# You can group the options in optgroup elements by passing the :group_by option
|
695
733
|
# (Note: only tested for belongs_to relations)
|
696
|
-
#
|
734
|
+
#
|
697
735
|
# Examples:
|
698
736
|
#
|
699
737
|
# f.input :author, :group_by => :continent
|
700
|
-
#
|
701
|
-
# All the other options should work as expected. If you want to call a custom method on the
|
738
|
+
#
|
739
|
+
# All the other options should work as expected. If you want to call a custom method on the
|
702
740
|
# group item. You can include the option:group_label_method
|
703
741
|
# Examples:
|
704
742
|
#
|
@@ -720,7 +758,7 @@ module Formtastic #:nodoc:
|
|
720
758
|
input_name = generate_association_input_name(method)
|
721
759
|
|
722
760
|
select_html = if options[:group_by]
|
723
|
-
# The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
|
761
|
+
# The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
|
724
762
|
# The formtastic user however shouldn't notice this too much.
|
725
763
|
raw_collection = find_raw_collection_for_column(method, options.reverse_merge(:find_options => { :include => options[:group_by] }))
|
726
764
|
label, value = detect_label_and_value_method!(raw_collection)
|
@@ -732,7 +770,7 @@ module Formtastic #:nodoc:
|
|
732
770
|
# Here comes the monster with 8 arguments
|
733
771
|
self.grouped_collection_select(input_name, group_collection,
|
734
772
|
group_association, group_label_method,
|
735
|
-
value, label,
|
773
|
+
value, label,
|
736
774
|
strip_formtastic_options(options), html_options)
|
737
775
|
else
|
738
776
|
collection = find_collection_for_column(method, options)
|
@@ -754,9 +792,9 @@ module Formtastic #:nodoc:
|
|
754
792
|
# You can pre-select a specific option value by passing in the :selected option.
|
755
793
|
# Note: Right now only works if the form object attribute value is not set (nil),
|
756
794
|
# because of how the core helper is implemented.
|
757
|
-
#
|
795
|
+
#
|
758
796
|
# Examples:
|
759
|
-
#
|
797
|
+
#
|
760
798
|
# f.input :my_favorite_time_zone, :as => :time_zone, :selected => 'Singapore'
|
761
799
|
#
|
762
800
|
def time_zone_input(method, options)
|
@@ -790,7 +828,7 @@ module Formtastic #:nodoc:
|
|
790
828
|
# </ol>
|
791
829
|
# </fieldset>
|
792
830
|
#
|
793
|
-
# You can customize the choices available in the radio button set by passing in a collection (an Array or
|
831
|
+
# You can customize the choices available in the radio button set by passing in a collection (an Array or
|
794
832
|
# Hash) through the :collection option. If not provided, the choices are found by reflecting on the association
|
795
833
|
# (Author.find(:all) in the example above).
|
796
834
|
#
|
@@ -820,7 +858,7 @@ module Formtastic #:nodoc:
|
|
820
858
|
# f.input :author, :as => :radio, :value_method => :full_name
|
821
859
|
# f.input :author, :as => :radio, :value_method => :login
|
822
860
|
# f.input :author, :as => :radio, :value_method => Proc.new { |a| "author_#{a.login}" }
|
823
|
-
#
|
861
|
+
#
|
824
862
|
# You can force a particular radio button in the collection to be checked with the :selected option.
|
825
863
|
#
|
826
864
|
# Examples:
|
@@ -828,7 +866,7 @@ module Formtastic #:nodoc:
|
|
828
866
|
# f.input :subscribe_to_newsletter, :as => :radio, :selected => true
|
829
867
|
# f.input :subscribe_to_newsletter, :as => :radio, :collection => ["Yeah!", "Nope!"], :selected => "Nope!"
|
830
868
|
#
|
831
|
-
# Finally, you can set :value_as_class => true if you want the li wrapper around each radio
|
869
|
+
# Finally, you can set :value_as_class => true if you want the li wrapper around each radio
|
832
870
|
# button / label combination to contain a class with the value of the radio button (useful for
|
833
871
|
# applying specific CSS or Javascript to a particular radio button).
|
834
872
|
#
|
@@ -851,12 +889,12 @@ module Formtastic #:nodoc:
|
|
851
889
|
html_options[:checked] = selected_value == value if selected_option_is_present
|
852
890
|
|
853
891
|
li_content = template.content_tag(:label,
|
854
|
-
"#{self.radio_button(input_name, value, html_options)} #{label}",
|
892
|
+
Formtastic::Util.html_safe("#{self.radio_button(input_name, value, html_options)} #{label}"),
|
855
893
|
:for => input_id
|
856
894
|
)
|
857
895
|
|
858
896
|
li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {}
|
859
|
-
template.content_tag(:li, li_content, li_options)
|
897
|
+
template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options)
|
860
898
|
end
|
861
899
|
|
862
900
|
field_set_and_list_wrapping_for_method(method, options, list_item_content)
|
@@ -870,14 +908,14 @@ module Formtastic #:nodoc:
|
|
870
908
|
# See date_or_datetime_input for a more detailed output example.
|
871
909
|
#
|
872
910
|
# You can pre-select a specific option value by passing in the :selected option.
|
873
|
-
#
|
911
|
+
#
|
874
912
|
# Examples:
|
875
|
-
#
|
913
|
+
#
|
876
914
|
# f.input :created_at, :as => :date, :selected => 1.day.ago
|
877
915
|
# f.input :created_at, :as => :date, :selected => nil # override any defaults: select none
|
878
916
|
# f.input :created_at, :as => :date, :labels => { :year => "Year", :month => "Month", :day => "Day" }
|
879
917
|
#
|
880
|
-
# Some of Rails' options for select_date are supported, but not everything yet, see
|
918
|
+
# Some of Rails' options for select_date are supported, but not everything yet, see
|
881
919
|
# documentation of date_or_datetime_input() for more information.
|
882
920
|
def date_input(method, options)
|
883
921
|
options = set_include_blank(options)
|
@@ -891,15 +929,15 @@ module Formtastic #:nodoc:
|
|
891
929
|
# text as value. See date_or_datetime_input for a more detailed output example.
|
892
930
|
#
|
893
931
|
# You can pre-select a specific option value by passing in the :selected option.
|
894
|
-
#
|
932
|
+
#
|
895
933
|
# Examples:
|
896
|
-
#
|
934
|
+
#
|
897
935
|
# f.input :created_at, :as => :datetime, :selected => 1.day.ago
|
898
936
|
# f.input :created_at, :as => :datetime, :selected => nil # override any defaults: select none
|
899
937
|
# f.input :created_at, :as => :date, :labels => { :year => "Year", :month => "Month", :day => "Day",
|
900
938
|
# :hour => "Hour", :minute => "Minute" }
|
901
939
|
#
|
902
|
-
# Some of Rails' options for select_date are supported, but not everything yet, see
|
940
|
+
# Some of Rails' options for select_date are supported, but not everything yet, see
|
903
941
|
# documentation of date_or_datetime_input() for more information.
|
904
942
|
def datetime_input(method, options)
|
905
943
|
options = set_include_blank(options)
|
@@ -913,22 +951,22 @@ module Formtastic #:nodoc:
|
|
913
951
|
# See date_or_datetime_input for a more detailed output example.
|
914
952
|
#
|
915
953
|
# You can pre-select a specific option value by passing in the :selected option.
|
916
|
-
#
|
954
|
+
#
|
917
955
|
# Examples:
|
918
|
-
#
|
956
|
+
#
|
919
957
|
# f.input :created_at, :as => :time, :selected => 1.hour.ago
|
920
958
|
# f.input :created_at, :as => :time, :selected => nil # override any defaults: select none
|
921
959
|
# f.input :created_at, :as => :date, :labels => { :hour => "Hour", :minute => "Minute" }
|
922
960
|
#
|
923
|
-
# Some of Rails' options for select_time are supported, but not everything yet, see
|
961
|
+
# Some of Rails' options for select_time are supported, but not everything yet, see
|
924
962
|
# documentation of date_or_datetime_input() for more information.
|
925
963
|
def time_input(method, options)
|
926
964
|
options = set_include_blank(options)
|
927
965
|
date_or_datetime_input(method, options.merge(:discard_year => true, :discard_month => true, :discard_day => true))
|
928
966
|
end
|
929
|
-
|
930
|
-
# Helper method used by :as => (:date|:datetime|:time). Generates a fieldset containing a
|
931
|
-
# legend (for what would normally be considered the label), and an ordered list of list items
|
967
|
+
|
968
|
+
# Helper method used by :as => (:date|:datetime|:time). Generates a fieldset containing a
|
969
|
+
# legend (for what would normally be considered the label), and an ordered list of list items
|
932
970
|
# for year, month, day, hour, etc, each containing a label and a select. Example:
|
933
971
|
#
|
934
972
|
# <fieldset>
|
@@ -974,11 +1012,6 @@ module Formtastic #:nodoc:
|
|
974
1012
|
# * @:include_blank => true@
|
975
1013
|
# * @:labels => {}@
|
976
1014
|
def date_or_datetime_input(method, options)
|
977
|
-
if options.key?(:selected)
|
978
|
-
::ActiveSupport::Deprecation.warn(":selected is deprecated (and may still have changed behavior) in #{options[:as]} inputs, use :default instead, see commit 09fc6b4 and issue #152 on github.com/justinfrench/formtastic")
|
979
|
-
options[:default] = options[:selected]
|
980
|
-
end
|
981
|
-
|
982
1015
|
position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
|
983
1016
|
i18n_date_order = ::I18n.t(:order, :scope => [:date])
|
984
1017
|
i18n_date_order = nil unless i18n_date_order.is_a?(Array)
|
@@ -986,13 +1019,13 @@ module Formtastic #:nodoc:
|
|
986
1019
|
labels = options.delete(:labels) || {}
|
987
1020
|
|
988
1021
|
time_inputs = [:hour, :minute]
|
989
|
-
time_inputs <<
|
1022
|
+
time_inputs << :second if options[:include_seconds]
|
990
1023
|
|
991
1024
|
list_items_capture = ""
|
992
1025
|
hidden_fields_capture = ""
|
993
1026
|
|
994
|
-
datetime = options
|
995
|
-
datetime = @object.send(method) if @object && @object.send(method) # object trumps :
|
1027
|
+
datetime = options[:selected]
|
1028
|
+
datetime = @object.send(method) if @object && @object.send(method) # object value trumps :selected value
|
996
1029
|
|
997
1030
|
html_options = options.delete(:input_html) || {}
|
998
1031
|
input_ids = []
|
@@ -1010,10 +1043,10 @@ module Formtastic #:nodoc:
|
|
1010
1043
|
opts = strip_formtastic_options(options).merge(:prefix => @object_name, :field_name => field_name, :default => datetime)
|
1011
1044
|
item_label_text = labels[input] || ::I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
|
1012
1045
|
|
1013
|
-
list_items_capture << template.content_tag(:li, [
|
1014
|
-
!item_label_text.blank? ? template.content_tag(:label, item_label_text, :for => input_id) : "",
|
1046
|
+
list_items_capture << template.content_tag(:li, Formtastic::Util.html_safe([
|
1047
|
+
!item_label_text.blank? ? template.content_tag(:label, Formtastic::Util.html_safe(item_label_text), :for => input_id) : "",
|
1015
1048
|
template.send(:"select_#{input}", datetime, opts, html_options.merge(:id => input_id))
|
1016
|
-
].join("")
|
1049
|
+
].join(""))
|
1017
1050
|
)
|
1018
1051
|
end
|
1019
1052
|
end
|
@@ -1083,15 +1116,15 @@ module Formtastic #:nodoc:
|
|
1083
1116
|
# f.input :author, :as => :check_boxes, :value_method => Proc.new { |a| "author_#{a.login}" }
|
1084
1117
|
#
|
1085
1118
|
# You can pre-select/check a specific checkbox value by passing in the :selected option (alias :checked works as well).
|
1086
|
-
#
|
1119
|
+
#
|
1087
1120
|
# Examples:
|
1088
|
-
#
|
1121
|
+
#
|
1089
1122
|
# f.input :authors, :as => :check_boxes, :selected => @justin
|
1090
1123
|
# f.input :authors, :as => :check_boxes, :selected => Author.most_popular.collect(&:id)
|
1091
1124
|
# f.input :authors, :as => :check_boxes, :selected => nil # override any defaults: select none
|
1092
1125
|
#
|
1093
|
-
# Finally, you can set :value_as_class => true if you want the li wrapper around each checkbox / label
|
1094
|
-
# combination to contain a class with the value of the radio button (useful for applying specific
|
1126
|
+
# Finally, you can set :value_as_class => true if you want the li wrapper around each checkbox / label
|
1127
|
+
# combination to contain a class with the value of the radio button (useful for applying specific
|
1095
1128
|
# CSS or Javascript to a particular checkbox).
|
1096
1129
|
#
|
1097
1130
|
def check_boxes_input(method, options)
|
@@ -1108,6 +1141,9 @@ module Formtastic #:nodoc:
|
|
1108
1141
|
selected_values = (options.key?(:checked) ? options[:checked] : options[:selected]) if selected_option_is_present
|
1109
1142
|
selected_values = [*selected_values].compact
|
1110
1143
|
|
1144
|
+
disabled_option_is_present = options.key?(:disabled)
|
1145
|
+
disabled_values = [*options[:disabled]] if disabled_option_is_present
|
1146
|
+
|
1111
1147
|
list_item_content = collection.map do |c|
|
1112
1148
|
label = c.is_a?(Array) ? c.first : c
|
1113
1149
|
value = c.is_a?(Array) ? c.last : c
|
@@ -1115,28 +1151,29 @@ module Formtastic #:nodoc:
|
|
1115
1151
|
input_ids << input_id
|
1116
1152
|
|
1117
1153
|
html_options[:checked] = selected_values.include?(value) if selected_option_is_present
|
1154
|
+
html_options[:disabled] = disabled_values.include?(value) if disabled_option_is_present
|
1118
1155
|
html_options[:id] = input_id
|
1119
1156
|
|
1120
1157
|
li_content = template.content_tag(:label,
|
1121
|
-
"#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}",
|
1158
|
+
Formtastic::Util.html_safe("#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}"),
|
1122
1159
|
:for => input_id
|
1123
1160
|
)
|
1124
1161
|
|
1125
1162
|
li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {}
|
1126
|
-
template.content_tag(:li, li_content, li_options)
|
1163
|
+
template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options)
|
1127
1164
|
end
|
1128
1165
|
|
1129
1166
|
field_set_and_list_wrapping_for_method(method, options, list_item_content)
|
1130
1167
|
end
|
1131
1168
|
|
1132
|
-
# Outputs a country select input, wrapping around a regular country_select helper.
|
1169
|
+
# Outputs a country select input, wrapping around a regular country_select helper.
|
1133
1170
|
# Rails doesn't come with a country_select helper by default any more, so you'll need to install
|
1134
1171
|
# the "official" plugin, or, if you wish, any other country_select plugin that behaves in the
|
1135
1172
|
# same way.
|
1136
1173
|
#
|
1137
1174
|
# The Rails plugin iso-3166-country-select plugin can be found "here":http://github.com/rails/iso-3166-country-select.
|
1138
1175
|
#
|
1139
|
-
# By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
|
1176
|
+
# By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
|
1140
1177
|
# which you can change to suit your market and user base (see README for more info on config).
|
1141
1178
|
#
|
1142
1179
|
# Examples:
|
@@ -1145,7 +1182,7 @@ module Formtastic #:nodoc:
|
|
1145
1182
|
#
|
1146
1183
|
def country_input(method, options)
|
1147
1184
|
raise "To use the :country input, please install a country_select plugin, like this one: http://github.com/rails/iso-3166-country-select" unless self.respond_to?(:country_select)
|
1148
|
-
|
1185
|
+
|
1149
1186
|
html_options = options.delete(:input_html) || {}
|
1150
1187
|
priority_countries = options.delete(:priority_countries) || @@priority_countries
|
1151
1188
|
|
@@ -1153,34 +1190,14 @@ module Formtastic #:nodoc:
|
|
1153
1190
|
self.country_select(method, priority_countries, strip_formtastic_options(options), html_options)
|
1154
1191
|
end
|
1155
1192
|
|
1156
|
-
# Outputs a currency select input, wrapping around a regular curency_select helper.
|
1157
|
-
# You need to install the currency_select plugin "here":http://github.com/gavinlaking/currency_select
|
1158
|
-
#
|
1159
|
-
# By default, Formtastic includes a handfull of common currencies as "priority currencies",
|
1160
|
-
# which you can change to suit your market and user base (see README for more info on config).
|
1161
|
-
#
|
1162
|
-
# Examples:
|
1163
|
-
# f.input :ticket_currency, :as => :currency # use Formtastic::SemanticFormBuilder.priority_curencies array for the priority currencies
|
1164
|
-
# f.input :ticket_currency, :as => :currency, :priority_currencies => /US Dollar/ # set your own
|
1165
|
-
#
|
1166
|
-
def currency_input(method, options)
|
1167
|
-
raise "To use the :currency input, please install the currency_select plugin http://github.com/gavinlaking/currency_select" unless self.respond_to?(:currency_select)
|
1168
|
-
|
1169
|
-
html_options = options.delete(:input_html) || {}
|
1170
|
-
priority_currencies = options.delete(:priority_currencies) || @@priority_currencies
|
1171
|
-
|
1172
|
-
self.label(method, options_for_label(options)) <<
|
1173
|
-
self.currency_select(method, priority_currencies, strip_formtastic_options(options), html_options)
|
1174
|
-
end
|
1175
|
-
|
1176
1193
|
# Outputs a label containing a checkbox and the label text. The label defaults
|
1177
1194
|
# to the column name (method name) and can be altered with the :label option.
|
1178
1195
|
# :checked_value and :unchecked_value options are also available.
|
1179
1196
|
#
|
1180
1197
|
# You can pre-select/check the boolean checkbox by passing in the :selected option (alias :checked works as well).
|
1181
|
-
#
|
1198
|
+
#
|
1182
1199
|
# Examples:
|
1183
|
-
#
|
1200
|
+
#
|
1184
1201
|
# f.input :allow_comments, :as => :boolean, :selected => true # override any default value: selected/checked
|
1185
1202
|
#
|
1186
1203
|
def boolean_input(method, options)
|
@@ -1208,13 +1225,13 @@ module Formtastic #:nodoc:
|
|
1208
1225
|
def inline_hints_for(method, options) #:nodoc:
|
1209
1226
|
options[:hint] = localized_string(method, options[:hint], :hint)
|
1210
1227
|
return if options[:hint].blank?
|
1211
|
-
template.content_tag(:p, options[:hint], :class => 'inline-hints')
|
1228
|
+
template.content_tag(:p, Formtastic::Util.html_safe(options[:hint]), :class => 'inline-hints')
|
1212
1229
|
end
|
1213
1230
|
|
1214
1231
|
# Creates an error sentence by calling to_sentence on the errors array.
|
1215
1232
|
#
|
1216
1233
|
def error_sentence(errors) #:nodoc:
|
1217
|
-
template.content_tag(:p, errors.to_sentence.untaint, :class => 'inline-errors')
|
1234
|
+
template.content_tag(:p, Formtastic::Util.html_safe(errors.to_sentence.untaint), :class => 'inline-errors')
|
1218
1235
|
end
|
1219
1236
|
|
1220
1237
|
# Creates an error li list.
|
@@ -1222,15 +1239,15 @@ module Formtastic #:nodoc:
|
|
1222
1239
|
def error_list(errors) #:nodoc:
|
1223
1240
|
list_elements = []
|
1224
1241
|
errors.each do |error|
|
1225
|
-
list_elements << template.content_tag(:li, error.untaint)
|
1242
|
+
list_elements << template.content_tag(:li, Formtastic::Util.html_safe(error.untaint))
|
1226
1243
|
end
|
1227
|
-
template.content_tag(:ul, list_elements.join("\n"), :class => 'errors')
|
1244
|
+
template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class => 'errors')
|
1228
1245
|
end
|
1229
1246
|
|
1230
1247
|
# Creates an error sentence containing only the first error
|
1231
1248
|
#
|
1232
1249
|
def error_first(errors) #:nodoc:
|
1233
|
-
template.content_tag(:p, errors.first.untaint, :class => 'inline-errors')
|
1250
|
+
template.content_tag(:p, Formtastic::Util.html_safe(errors.first.untaint), :class => 'inline-errors')
|
1234
1251
|
end
|
1235
1252
|
|
1236
1253
|
# Generates the required or optional string. If the value set is a proc,
|
@@ -1277,7 +1294,7 @@ module Formtastic #:nodoc:
|
|
1277
1294
|
|
1278
1295
|
legend = html_options.delete(:name).to_s
|
1279
1296
|
legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
|
1280
|
-
legend = template.content_tag(:legend, template.content_tag(:span, legend)) unless legend.blank?
|
1297
|
+
legend = template.content_tag(:legend, template.content_tag(:span, Formtastic::Util.html_safe(legend))) unless legend.blank?
|
1281
1298
|
|
1282
1299
|
if block_given?
|
1283
1300
|
contents = if template.respond_to?(:is_haml?) && template.is_haml?
|
@@ -1290,11 +1307,11 @@ module Formtastic #:nodoc:
|
|
1290
1307
|
# Ruby 1.9: String#to_s behavior changed, need to make an explicit join.
|
1291
1308
|
contents = contents.join if contents.respond_to?(:join)
|
1292
1309
|
fieldset = template.content_tag(:fieldset,
|
1293
|
-
legend << template.content_tag(:ol, contents),
|
1310
|
+
Formtastic::Util.html_safe(legend) << template.content_tag(:ol, Formtastic::Util.html_safe(contents)),
|
1294
1311
|
html_options.except(:builder, :parent)
|
1295
1312
|
)
|
1296
1313
|
|
1297
|
-
template.concat(fieldset) if block_given?
|
1314
|
+
template.concat(fieldset) if block_given? && (!defined?(Rails::VERSION) || Rails::VERSION::MAJOR == 2)
|
1298
1315
|
fieldset
|
1299
1316
|
end
|
1300
1317
|
|
@@ -1322,7 +1339,7 @@ module Formtastic #:nodoc:
|
|
1322
1339
|
template.content_tag(:legend,
|
1323
1340
|
self.label(method, options_for_label(options).merge(:for => options.delete(:label_for))), :class => 'label'
|
1324
1341
|
) <<
|
1325
|
-
template.content_tag(:ol, contents)
|
1342
|
+
template.content_tag(:ol, Formtastic::Util.html_safe(contents))
|
1326
1343
|
)
|
1327
1344
|
end
|
1328
1345
|
|
@@ -1340,8 +1357,7 @@ module Formtastic #:nodoc:
|
|
1340
1357
|
case column.type
|
1341
1358
|
when :string
|
1342
1359
|
return :password if method.to_s =~ /password/
|
1343
|
-
return :country if method.to_s =~ /country
|
1344
|
-
return :currency if method.to_s =~ /currency/
|
1360
|
+
return :country if method.to_s =~ /country$/
|
1345
1361
|
return :time_zone if method.to_s =~ /time_zone/
|
1346
1362
|
when :integer
|
1347
1363
|
return :select if method.to_s =~ /_id$/
|
@@ -1351,7 +1367,7 @@ module Formtastic #:nodoc:
|
|
1351
1367
|
when :timestamp
|
1352
1368
|
return :datetime
|
1353
1369
|
end
|
1354
|
-
|
1370
|
+
|
1355
1371
|
# Try look for hints in options hash. Quite common senario: Enum keys stored as string in the database.
|
1356
1372
|
return :select if column.type == :string && options.key?(:collection)
|
1357
1373
|
# Try 3: Assume the input name will be the same as the column type (e.g. string_input).
|
@@ -1398,7 +1414,13 @@ module Formtastic #:nodoc:
|
|
1398
1414
|
collection = if options[:collection]
|
1399
1415
|
options.delete(:collection)
|
1400
1416
|
elsif reflection = self.reflection_for(column)
|
1401
|
-
|
1417
|
+
options[:find_options] ||= {}
|
1418
|
+
|
1419
|
+
if conditions = reflection.options[:conditions]
|
1420
|
+
options[:find_options][:conditions] = reflection.klass.merge_conditions(conditions, options[:find_options][:conditions])
|
1421
|
+
end
|
1422
|
+
|
1423
|
+
reflection.klass.find(:all, options[:find_options])
|
1402
1424
|
else
|
1403
1425
|
create_boolean_collection(options)
|
1404
1426
|
end
|
@@ -1407,7 +1429,7 @@ module Formtastic #:nodoc:
|
|
1407
1429
|
collection
|
1408
1430
|
end
|
1409
1431
|
|
1410
|
-
# Detects the label and value methods from a collection values set in
|
1432
|
+
# Detects the label and value methods from a collection values set in
|
1411
1433
|
# @@collection_label_methods. It will use and delete
|
1412
1434
|
# the options :label_method and :value_methods when present
|
1413
1435
|
#
|
@@ -1429,16 +1451,16 @@ module Formtastic #:nodoc:
|
|
1429
1451
|
def detect_group_association(method, group_by)
|
1430
1452
|
object_to_method_reflection = self.reflection_for(method)
|
1431
1453
|
method_class = object_to_method_reflection.klass
|
1432
|
-
|
1454
|
+
|
1433
1455
|
method_to_group_association = method_class.reflect_on_association(group_by)
|
1434
1456
|
group_class = method_to_group_association.klass
|
1435
|
-
|
1457
|
+
|
1436
1458
|
# This will return in the normal case
|
1437
1459
|
return method.to_s.pluralize.to_sym if group_class.reflect_on_association(method.to_s.pluralize)
|
1438
|
-
|
1460
|
+
|
1439
1461
|
# This is for belongs_to associations named differently than their class
|
1440
1462
|
# form.input :parent, :group_by => :customer
|
1441
|
-
# eg.
|
1463
|
+
# eg.
|
1442
1464
|
# class Project
|
1443
1465
|
# belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id'
|
1444
1466
|
# belongs_to :customer
|
@@ -1448,9 +1470,9 @@ module Formtastic #:nodoc:
|
|
1448
1470
|
# end
|
1449
1471
|
group_method = method_class.to_s.underscore.pluralize.to_sym
|
1450
1472
|
return group_method if group_class.reflect_on_association(group_method) # :projects
|
1451
|
-
|
1473
|
+
|
1452
1474
|
# This is for has_many associations named differently than their class
|
1453
|
-
# eg.
|
1475
|
+
# eg.
|
1454
1476
|
# class Project
|
1455
1477
|
# belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id'
|
1456
1478
|
# belongs_to :customer
|
@@ -1460,9 +1482,9 @@ module Formtastic #:nodoc:
|
|
1460
1482
|
# end
|
1461
1483
|
possible_associations = group_class.reflect_on_all_associations(:has_many).find_all{|assoc| assoc.klass == object_class}
|
1462
1484
|
return possible_associations.first.name.to_sym if possible_associations.count == 1
|
1463
|
-
|
1485
|
+
|
1464
1486
|
raise "Cannot infer group association for #{method} grouped by #{group_by}, there were #{possible_associations.empty? ? 'no' : possible_associations.size} possible associations. Please specify using :group_association"
|
1465
|
-
|
1487
|
+
|
1466
1488
|
end
|
1467
1489
|
|
1468
1490
|
# Returns a hash to be used by radio and select inputs when a boolean field
|
@@ -1489,7 +1511,7 @@ module Formtastic #:nodoc:
|
|
1489
1511
|
if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
|
1490
1512
|
"#{method.to_s.singularize}_ids"
|
1491
1513
|
else
|
1492
|
-
reflection.options[:foreign_key] ||
|
1514
|
+
reflection.options[:foreign_key] || "#{method}_id"
|
1493
1515
|
end
|
1494
1516
|
else
|
1495
1517
|
method
|
@@ -1577,23 +1599,23 @@ module Formtastic #:nodoc:
|
|
1577
1599
|
|
1578
1600
|
# Internal generic method for looking up localized values within Formtastic
|
1579
1601
|
# using I18n, if no explicit value is set and I18n-lookups are enabled.
|
1580
|
-
#
|
1602
|
+
#
|
1581
1603
|
# Enabled/Disable this by setting:
|
1582
1604
|
#
|
1583
1605
|
# Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true/false
|
1584
1606
|
#
|
1585
1607
|
# Lookup priority:
|
1586
1608
|
#
|
1587
|
-
# 'formtastic
|
1588
|
-
# 'formtastic
|
1589
|
-
# 'formtastic
|
1590
|
-
#
|
1609
|
+
# 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
|
1610
|
+
# 'formtastic.%{type}.%{model}.%{attribute}'
|
1611
|
+
# 'formtastic.%{type}.%{attribute}'
|
1612
|
+
#
|
1591
1613
|
# Example:
|
1592
|
-
#
|
1614
|
+
#
|
1593
1615
|
# 'formtastic.labels.post.edit.title'
|
1594
1616
|
# 'formtastic.labels.post.title'
|
1595
1617
|
# 'formtastic.labels.title'
|
1596
|
-
#
|
1618
|
+
#
|
1597
1619
|
# NOTE: Generic, but only used for form input titles/labels/hints/actions (titles = legends, actions = buttons).
|
1598
1620
|
#
|
1599
1621
|
def localized_string(key, value, type, options = {}) #:nodoc:
|
@@ -1605,15 +1627,16 @@ module Formtastic #:nodoc:
|
|
1605
1627
|
use_i18n = value.nil? ? @@i18n_lookups_by_default : (value != false)
|
1606
1628
|
|
1607
1629
|
if use_i18n
|
1608
|
-
model_name = self.model_name.underscore
|
1630
|
+
model_name, nested_model_name = normalize_model_name(self.model_name.underscore)
|
1609
1631
|
action_name = template.params[:action].to_s rescue ''
|
1610
1632
|
attribute_name = key.to_s
|
1611
1633
|
|
1612
1634
|
defaults = ::Formtastic::I18n::SCOPES.collect do |i18n_scope|
|
1613
1635
|
i18n_path = i18n_scope.dup
|
1614
|
-
i18n_path.gsub!('{
|
1615
|
-
i18n_path.gsub!('{
|
1616
|
-
i18n_path.gsub!('{
|
1636
|
+
i18n_path.gsub!('%{action}', action_name)
|
1637
|
+
i18n_path.gsub!('%{model}', model_name)
|
1638
|
+
i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
|
1639
|
+
i18n_path.gsub!('%{attribute}', attribute_name)
|
1617
1640
|
i18n_path.gsub!('..', '.')
|
1618
1641
|
i18n_path.to_sym
|
1619
1642
|
end
|
@@ -1630,6 +1653,14 @@ module Formtastic #:nodoc:
|
|
1630
1653
|
@object.present? ? @object.class.name : @object_name.to_s.classify
|
1631
1654
|
end
|
1632
1655
|
|
1656
|
+
def normalize_model_name(name)
|
1657
|
+
if name =~ /(.+)\[(.+)\]/
|
1658
|
+
[$1, $2]
|
1659
|
+
else
|
1660
|
+
[name]
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
|
1633
1664
|
def send_or_call(duck, object)
|
1634
1665
|
if duck.is_a?(Proc)
|
1635
1666
|
duck.call(object)
|
@@ -1651,11 +1682,15 @@ module Formtastic #:nodoc:
|
|
1651
1682
|
#
|
1652
1683
|
# * semantic_form_for(@post)
|
1653
1684
|
# * semantic_fields_for(@post)
|
1685
|
+
# * semantic_form_remote_for(@post)
|
1686
|
+
# * semantic_remote_form_for(@post)
|
1654
1687
|
#
|
1655
1688
|
# Each of which are the equivalent of:
|
1656
1689
|
#
|
1657
1690
|
# * form_for(@post, :builder => Formtastic::SemanticFormBuilder))
|
1658
1691
|
# * fields_for(@post, :builder => Formtastic::SemanticFormBuilder))
|
1692
|
+
# * form_remote_for(@post, :builder => Formtastic::SemanticFormBuilder))
|
1693
|
+
# * remote_form_for(@post, :builder => Formtastic::SemanticFormBuilder))
|
1659
1694
|
#
|
1660
1695
|
# Example Usage:
|
1661
1696
|
#
|
@@ -1665,7 +1700,7 @@ module Formtastic #:nodoc:
|
|
1665
1700
|
# <% end %>
|
1666
1701
|
#
|
1667
1702
|
# The above examples use a resource-oriented style of form_for() helper where only the @post
|
1668
|
-
# object is given as an argument, but the generic style is also supported, as are forms with
|
1703
|
+
# object is given as an argument, but the generic style is also supported, as are forms with
|
1669
1704
|
# inline objects (Post.new) rather than objects with instance variables (@post):
|
1670
1705
|
#
|
1671
1706
|
# <% semantic_form_for :post, @post, :url => posts_path do |f| %>
|
@@ -1678,9 +1713,9 @@ module Formtastic #:nodoc:
|
|
1678
1713
|
module SemanticFormHelper
|
1679
1714
|
@@builder = ::Formtastic::SemanticFormBuilder
|
1680
1715
|
mattr_accessor :builder
|
1681
|
-
|
1716
|
+
|
1682
1717
|
@@default_field_error_proc = nil
|
1683
|
-
|
1718
|
+
|
1684
1719
|
# Override the default ActiveRecordHelper behaviour of wrapping the input.
|
1685
1720
|
# This gets taken care of semantically by adding an error class to the LI tag
|
1686
1721
|
# containing the input.
|
@@ -1688,7 +1723,7 @@ module Formtastic #:nodoc:
|
|
1688
1723
|
FIELD_ERROR_PROC = proc do |html_tag, instance_tag|
|
1689
1724
|
html_tag
|
1690
1725
|
end
|
1691
|
-
|
1726
|
+
|
1692
1727
|
def with_custom_field_error_proc(&block)
|
1693
1728
|
@@default_field_error_proc = ::ActionView::Base.field_error_proc
|
1694
1729
|
::ActionView::Base.field_error_proc = FIELD_ERROR_PROC
|
@@ -1696,8 +1731,18 @@ module Formtastic #:nodoc:
|
|
1696
1731
|
::ActionView::Base.field_error_proc = @@default_field_error_proc
|
1697
1732
|
result
|
1698
1733
|
end
|
1734
|
+
|
1735
|
+
def semantic_remote_form_for_wrapper(record_or_name_or_array, *args, &proc)
|
1736
|
+
options = args.extract_options!
|
1737
|
+
if self.respond_to? :remote_form_for
|
1738
|
+
semantic_remote_form_for_real(record_or_name_or_array, *(args << options), &proc)
|
1739
|
+
else
|
1740
|
+
options[:remote] = true
|
1741
|
+
semantic_form_for(record_or_name_or_array, *(args << options), &proc)
|
1742
|
+
end
|
1743
|
+
end
|
1699
1744
|
|
1700
|
-
[:form_for, :fields_for].each do |meth|
|
1745
|
+
[:form_for, :fields_for, :remote_form_for].each do |meth|
|
1701
1746
|
module_eval <<-END_SRC, __FILE__, __LINE__ + 1
|
1702
1747
|
def semantic_#{meth}(record_or_name_or_array, *args, &proc)
|
1703
1748
|
options = args.extract_options!
|
@@ -1712,13 +1757,16 @@ module Formtastic #:nodoc:
|
|
1712
1757
|
else ActionController::RecordIdentifier.singular_class_name(record_or_name_or_array.class) # @post => "post"
|
1713
1758
|
end
|
1714
1759
|
options[:html][:class] = class_names.join(" ")
|
1715
|
-
|
1760
|
+
|
1716
1761
|
with_custom_field_error_proc do
|
1717
1762
|
#{meth}(record_or_name_or_array, *(args << options), &proc)
|
1718
1763
|
end
|
1719
1764
|
end
|
1720
1765
|
END_SRC
|
1721
1766
|
end
|
1722
|
-
|
1767
|
+
alias :semantic_remote_form_for_real :semantic_remote_form_for
|
1768
|
+
alias :semantic_remote_form_for :semantic_remote_form_for_wrapper
|
1769
|
+
alias :semantic_form_remote_for :semantic_remote_form_for
|
1770
|
+
|
1723
1771
|
end
|
1724
1772
|
end
|