formtastic-rails3 0.9.7 → 0.9.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|