formtastic 1.1.0 → 1.2.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +1 -1
- data/README.textile +80 -66
- data/generators/form/form_generator.rb +37 -46
- data/generators/formtastic/formtastic_generator.rb +10 -8
- data/lib/formtastic.rb +318 -227
- data/lib/formtastic/i18n.rb +8 -6
- data/lib/formtastic/layout_helper.rb +6 -4
- data/lib/formtastic/railtie.rb +3 -1
- data/lib/formtastic/util.rb +2 -0
- data/lib/generators/formtastic/form/form_generator.rb +5 -7
- data/lib/generators/formtastic/install/install_generator.rb +2 -9
- data/lib/generators/templates/_form.html.erb +5 -0
- data/lib/generators/templates/_form.html.haml +4 -0
- data/{generators/formtastic → lib/generators}/templates/formtastic.css +25 -11
- data/{generators/formtastic → lib/generators}/templates/formtastic.rb +19 -2
- data/{generators/formtastic → lib/generators}/templates/formtastic_changes.css +0 -0
- metadata +58 -98
- data/Rakefile +0 -127
- data/generators/form/templates/view__form.html.erb +0 -5
- data/generators/form/templates/view__form.html.haml +0 -4
- data/generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb +0 -16
- data/init.rb +0 -5
- data/rails/init.rb +0 -2
- data/spec/buttons_spec.rb +0 -166
- data/spec/commit_button_spec.rb +0 -401
- data/spec/custom_builder_spec.rb +0 -98
- data/spec/defaults_spec.rb +0 -20
- data/spec/error_proc_spec.rb +0 -27
- data/spec/errors_spec.rb +0 -105
- data/spec/form_helper_spec.rb +0 -142
- data/spec/helpers/layout_helper_spec.rb +0 -21
- data/spec/i18n_spec.rb +0 -152
- data/spec/include_blank_spec.rb +0 -74
- data/spec/input_spec.rb +0 -786
- data/spec/inputs/boolean_input_spec.rb +0 -104
- data/spec/inputs/check_boxes_input_spec.rb +0 -426
- data/spec/inputs/country_input_spec.rb +0 -118
- data/spec/inputs/date_input_spec.rb +0 -168
- data/spec/inputs/datetime_input_spec.rb +0 -310
- data/spec/inputs/file_input_spec.rb +0 -34
- data/spec/inputs/hidden_input_spec.rb +0 -78
- data/spec/inputs/numeric_input_spec.rb +0 -44
- data/spec/inputs/password_input_spec.rb +0 -46
- data/spec/inputs/radio_input_spec.rb +0 -243
- data/spec/inputs/select_input_spec.rb +0 -546
- data/spec/inputs/string_input_spec.rb +0 -64
- data/spec/inputs/text_input_spec.rb +0 -34
- data/spec/inputs/time_input_spec.rb +0 -206
- data/spec/inputs/time_zone_input_spec.rb +0 -110
- data/spec/inputs_spec.rb +0 -476
- data/spec/label_spec.rb +0 -89
- data/spec/semantic_errors_spec.rb +0 -98
- data/spec/semantic_fields_for_spec.rb +0 -45
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -289
- data/spec/support/custom_macros.rb +0 -494
- data/spec/support/output_buffer.rb +0 -4
- data/spec/support/test_environment.rb +0 -45
data/lib/formtastic.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
require File.join(File.dirname(__FILE__), *%w[formtastic i18n])
|
3
3
|
require File.join(File.dirname(__FILE__), *%w[formtastic util])
|
4
4
|
require File.join(File.dirname(__FILE__), *%w[formtastic railtie]) if defined?(::Rails::Railtie)
|
@@ -6,13 +6,16 @@ require File.join(File.dirname(__FILE__), *%w[formtastic railtie]) if defined?(:
|
|
6
6
|
module Formtastic #:nodoc:
|
7
7
|
|
8
8
|
class SemanticFormBuilder < ActionView::Helpers::FormBuilder
|
9
|
-
class_inheritable_accessor :default_text_field_size, :default_text_area_height, :all_fields_required_by_default, :include_blank_for_select_by_default,
|
10
|
-
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_value_methods, :collection_label_methods,
|
11
|
-
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :escape_html_entities_in_hints_and_labels,
|
12
|
-
:instance_reader => false
|
9
|
+
class_inheritable_accessor :default_text_field_size, :default_text_area_height, :default_text_area_width, :all_fields_required_by_default, :include_blank_for_select_by_default,
|
10
|
+
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_value_methods, :collection_label_methods, :file_metadata_suffixes,
|
11
|
+
:inline_order, :custom_inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :escape_html_entities_in_hints_and_labels,
|
12
|
+
:default_commit_button_accesskey, :default_inline_error_class, :default_hint_class, :default_error_list_class, :instance_reader => false
|
13
13
|
|
14
|
-
|
14
|
+
cattr_accessor :custom_namespace
|
15
|
+
|
16
|
+
self.default_text_field_size = nil
|
15
17
|
self.default_text_area_height = 20
|
18
|
+
self.default_text_area_width = 50
|
16
19
|
self.all_fields_required_by_default = true
|
17
20
|
self.include_blank_for_select_by_default = true
|
18
21
|
self.required_string = proc { ::Formtastic::Util.html_safe(%{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>}) }
|
@@ -22,11 +25,16 @@ module Formtastic #:nodoc:
|
|
22
25
|
self.collection_label_methods = %w[to_label display_name full_name name title username login value to_s]
|
23
26
|
self.collection_value_methods = %w[id to_s]
|
24
27
|
self.inline_order = [ :input, :hints, :errors ]
|
28
|
+
self.custom_inline_order = {}
|
25
29
|
self.file_methods = [ :file?, :public_filename, :filename ]
|
30
|
+
self.file_metadata_suffixes = ['content_type', 'file_name', 'file_size']
|
26
31
|
self.priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
27
32
|
self.i18n_lookups_by_default = false
|
28
33
|
self.escape_html_entities_in_hints_and_labels = true
|
29
34
|
self.default_commit_button_accesskey = nil
|
35
|
+
self.default_inline_error_class = 'inline-errors'
|
36
|
+
self.default_error_list_class = 'errors'
|
37
|
+
self.default_hint_class = 'inline-hints'
|
30
38
|
|
31
39
|
RESERVED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
|
32
40
|
|
@@ -64,6 +72,15 @@ module Formtastic #:nodoc:
|
|
64
72
|
# * :boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
|
65
73
|
# * :string (a text field) - default for :string column types
|
66
74
|
# * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
|
75
|
+
# * :email (an email input) - default for :string column types with 'email' as the method name.
|
76
|
+
# * :url (a url input) - default for :string column types with 'url' as the method name.
|
77
|
+
# * :phone (a tel input) - default for :string column types with 'phone' or 'fax' in the method name.
|
78
|
+
# * :search (a search input) - default for :string column types with 'search' as the method name.
|
79
|
+
# * :country (a select menu of country names) - requires a country_select plugin to be installed
|
80
|
+
# * :email (an email input) - New in HTML5 - needs to be explicitly provided with :as => :email
|
81
|
+
# * :url (a url input) - New in HTML5 - needs to be explicitly provided with :as => :url
|
82
|
+
# * :phone (a tel input) - New in HTML5 - needs to be explicitly provided with :as => :phone
|
83
|
+
# * :search (a search input) - New in HTML5 - needs to be explicity provided with :as => :search
|
67
84
|
# * :country (a select menu of country names) - requires a country_select plugin to be installed
|
68
85
|
# * :hidden (a hidden field) - creates a hidden field (added for compatibility)
|
69
86
|
#
|
@@ -76,22 +93,17 @@ module Formtastic #:nodoc:
|
|
76
93
|
# <%= form.input :manager_id, :as => :radio %>
|
77
94
|
# <%= form.input :hired_at, :as => :date, :label => "Date Hired" %>
|
78
95
|
# <%= form.input :phone, :required => false, :hint => "Eg: +1 555 1234" %>
|
96
|
+
# <%= form.input :email %>
|
97
|
+
# <%= form.input :website, :as => :url, :hint => "You may wish to omit the http://" %>
|
79
98
|
# <% end %>
|
80
99
|
# <% end %>
|
81
100
|
#
|
82
101
|
def input(method, options = {})
|
83
|
-
if options.key?(:selected) || options.key?(:checked) || options.key?(:default)
|
84
|
-
::ActiveSupport::Deprecation.warn(
|
85
|
-
"The :selected, :checked (and :default) options are deprecated in Formtastic and will be removed from 1.0. " <<
|
86
|
-
"Please set default values in your models (using an after_initialize callback) or in your controller set-up. " <<
|
87
|
-
"See http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html for more information.", caller)
|
88
|
-
end
|
89
|
-
|
90
102
|
options[:required] = method_required?(method) unless options.key?(:required)
|
91
103
|
options[:as] ||= default_input_type(method, options)
|
92
104
|
|
93
105
|
html_class = [ options[:as], (options[:required] ? :required : :optional) ]
|
94
|
-
html_class << 'error' if
|
106
|
+
html_class << 'error' if has_errors?(method, options)
|
95
107
|
|
96
108
|
wrapper_html = options.delete(:wrapper_html) || {}
|
97
109
|
wrapper_html[:id] ||= generate_html_id(method)
|
@@ -102,7 +114,7 @@ module Formtastic #:nodoc:
|
|
102
114
|
options[:label_html][:for] ||= options[:input_html][:id]
|
103
115
|
end
|
104
116
|
|
105
|
-
input_parts = self.class.inline_order.dup
|
117
|
+
input_parts = (self.class.custom_inline_order[options[:as]] || self.class.inline_order).dup
|
106
118
|
input_parts = input_parts - [:errors, :hints] if options[:as] == :hidden
|
107
119
|
|
108
120
|
list_item_content = input_parts.map do |type|
|
@@ -263,7 +275,7 @@ module Formtastic #:nodoc:
|
|
263
275
|
html_options = args.extract_options!
|
264
276
|
html_options[:class] ||= "inputs"
|
265
277
|
html_options[:name] = title
|
266
|
-
|
278
|
+
|
267
279
|
if html_options[:for] # Nested form
|
268
280
|
inputs_for_nested_attributes(*(args << html_options), &block)
|
269
281
|
elsif block_given?
|
@@ -278,11 +290,15 @@ module Formtastic #:nodoc:
|
|
278
290
|
legend = args.shift if args.first.is_a?(::String)
|
279
291
|
contents = args.collect { |method| input(method.to_sym) }
|
280
292
|
args.unshift(legend) if legend.present?
|
281
|
-
|
293
|
+
|
282
294
|
field_set_and_list_wrapping(*((args << html_options) << contents))
|
283
295
|
end
|
284
296
|
end
|
285
|
-
|
297
|
+
|
298
|
+
def input_field_set(*args, &block)
|
299
|
+
::ActiveSupport::Deprecation.warn("input_field_set() is deprecated and will be removed in Formtastic 1.3 or later, use inputs() instead.", caller)
|
300
|
+
inputs(args, &block)
|
301
|
+
end
|
286
302
|
|
287
303
|
# Creates a fieldset and ol tag wrapping for form buttons / actions as list items.
|
288
304
|
# See inputs documentation for a full example. The fieldset's default class attriute
|
@@ -301,7 +317,11 @@ module Formtastic #:nodoc:
|
|
301
317
|
field_set_and_list_wrapping(html_options, contents)
|
302
318
|
end
|
303
319
|
end
|
304
|
-
|
320
|
+
|
321
|
+
def button_field_set(*args, &block)
|
322
|
+
::ActiveSupport::Deprecation.warn("button_field_set() is deprecated and will be removed in Formtastic 1.3 or later, use inputs() instead.", caller)
|
323
|
+
buttons(args, &block)
|
324
|
+
end
|
305
325
|
|
306
326
|
# Creates a submit input tag with the value "Save [model name]" (for existing records) or
|
307
327
|
# "Create [model name]" (for new records) by default:
|
@@ -322,11 +342,15 @@ module Formtastic #:nodoc:
|
|
322
342
|
options = args.extract_options!
|
323
343
|
text = options.delete(:label) || args.shift
|
324
344
|
|
325
|
-
if @object && @object.respond_to?(:new_record?)
|
326
|
-
|
327
|
-
|
345
|
+
if @object && (@object.respond_to?(:persisted?) || @object.respond_to?(:new_record?))
|
346
|
+
if @object.respond_to?(:persisted?) # ActiveModel
|
347
|
+
key = @object.persisted? ? :update : :create
|
348
|
+
else # Rails 2
|
349
|
+
key = @object.new_record? ? :create : :update
|
350
|
+
end
|
351
|
+
|
328
352
|
# Deal with some complications with ActiveRecord::Base.human_name and two name models (eg UserPost)
|
329
|
-
# ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost")
|
353
|
+
# ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost")
|
330
354
|
# if there's no i18n, which is pretty crappy. In this circumstance we want to detect this
|
331
355
|
# fall back (human_name == name.humanize) and do our own thing name.underscore.humanize ("User Post")
|
332
356
|
if @object.class.model_name.respond_to?(:human)
|
@@ -432,12 +456,10 @@ module Formtastic #:nodoc:
|
|
432
456
|
# f.errors_on(:body)
|
433
457
|
# end
|
434
458
|
#
|
435
|
-
def inline_errors_for(method, options =
|
459
|
+
def inline_errors_for(method, options = {}) #:nodoc:
|
436
460
|
if render_inline_errors?
|
437
|
-
errors =
|
438
|
-
|
439
|
-
errors = errors.flatten.compact.uniq
|
440
|
-
send(:"error_#{self.class.inline_errors}", [*errors]) if errors.any?
|
461
|
+
errors = error_keys(method, options).map{|x| @object.errors[x] }.flatten.compact.uniq
|
462
|
+
send(:"error_#{self.class.inline_errors}", [*errors], options) if errors.any?
|
441
463
|
else
|
442
464
|
nil
|
443
465
|
end
|
@@ -472,6 +494,21 @@ module Formtastic #:nodoc:
|
|
472
494
|
|
473
495
|
protected
|
474
496
|
|
497
|
+
def error_keys(method, options)
|
498
|
+
@methods_for_error ||= {}
|
499
|
+
@methods_for_error[method] ||= begin
|
500
|
+
methods_for_error = [method.to_sym]
|
501
|
+
methods_for_error << self.class.file_metadata_suffixes.map{|suffix| "#{method}_#{suffix}".to_sym} if is_file?(method, options)
|
502
|
+
methods_for_error << [association_primary_key(method)] if association_macro_for_method(method) == :belongs_to
|
503
|
+
methods_for_error.flatten.compact.uniq
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def has_errors?(method, options)
|
508
|
+
methods_for_error = error_keys(method,options)
|
509
|
+
@object && @object.respond_to?(:errors) && methods_for_error.any?{|error| !@object.errors[error].blank?}
|
510
|
+
end
|
511
|
+
|
475
512
|
def render_inline_errors?
|
476
513
|
@object && @object.respond_to?(:errors) && INLINE_ERROR_TYPES.include?(self.class.inline_errors)
|
477
514
|
end
|
@@ -486,9 +523,9 @@ module Formtastic #:nodoc:
|
|
486
523
|
#
|
487
524
|
def association_columns(*by_associations) #:nodoc:
|
488
525
|
if @object.present? && @object.class.respond_to?(:reflections)
|
489
|
-
@object.class.reflections.collect do |name,
|
526
|
+
@object.class.reflections.collect do |name, association_reflection|
|
490
527
|
if by_associations.present?
|
491
|
-
name if by_associations.include?(
|
528
|
+
name if by_associations.include?(association_reflection.macro)
|
492
529
|
else
|
493
530
|
name
|
494
531
|
end
|
@@ -497,13 +534,13 @@ module Formtastic #:nodoc:
|
|
497
534
|
[]
|
498
535
|
end
|
499
536
|
end
|
500
|
-
|
537
|
+
|
501
538
|
# Returns nil, or a symbol like :belongs_to or :has_many
|
502
539
|
def association_macro_for_method(method) #:nodoc:
|
503
540
|
reflection = self.reflection_for(method)
|
504
541
|
reflection.macro if reflection
|
505
542
|
end
|
506
|
-
|
543
|
+
|
507
544
|
def association_primary_key(method)
|
508
545
|
reflection = self.reflection_for(method)
|
509
546
|
reflection.options[:foreign_key] if reflection && !reflection.options[:foreign_key].blank?
|
@@ -569,18 +606,17 @@ module Formtastic #:nodoc:
|
|
569
606
|
# configuration option all_fields_required_by_default is used.
|
570
607
|
#
|
571
608
|
def method_required?(attribute) #:nodoc:
|
572
|
-
|
573
|
-
attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
|
609
|
+
attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
|
574
610
|
|
611
|
+
if @object && @object.class.respond_to?(:reflect_on_validations_for)
|
575
612
|
@object.class.reflect_on_validations_for(attribute_sym).any? do |validation|
|
576
|
-
validation.macro == :validates_presence_of &&
|
613
|
+
(validation.macro == :validates_presence_of || validation.macro == :validates_inclusion_of) &&
|
577
614
|
validation.name == attribute_sym &&
|
578
615
|
(validation.options.present? ? options_require_validation?(validation.options) : true)
|
579
616
|
end
|
580
617
|
else
|
581
618
|
if @object && @object.class.respond_to?(:validators_on)
|
582
|
-
attribute_sym
|
583
|
-
!@object.class.validators_on(attribute_sym).find{|validator| (validator.kind == :presence) && (validator.options.present? ? options_require_validation?(validator.options) : true)}.nil?
|
619
|
+
!@object.class.validators_on(attribute_sym).find{|validator| (validator.kind == :presence || validator.kind == :inclusion) && (validator.options.present? ? options_require_validation?(validator.options) : true)}.nil?
|
584
620
|
else
|
585
621
|
self.class.all_fields_required_by_default
|
586
622
|
end
|
@@ -605,10 +641,13 @@ module Formtastic #:nodoc:
|
|
605
641
|
|
606
642
|
def basic_input_helper(form_helper_method, type, method, options) #:nodoc:
|
607
643
|
html_options = options.delete(:input_html) || {}
|
608
|
-
html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password, :text].include?(type)
|
609
|
-
|
610
|
-
|
611
|
-
|
644
|
+
html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password, :text, :phone, :search, :url, :email].include?(type)
|
645
|
+
field_id = generate_html_id(method, "")
|
646
|
+
html_options[:id] ||= field_id
|
647
|
+
label_options = options_for_label(options)
|
648
|
+
label_options[:for] ||= html_options[:id]
|
649
|
+
self.label(method, label_options) <<
|
650
|
+
self.send(self.respond_to?(form_helper_method) ? form_helper_method : :text_field, method, html_options)
|
612
651
|
end
|
613
652
|
|
614
653
|
# Outputs a label and standard Rails text field inside the wrapper.
|
@@ -636,6 +675,26 @@ module Formtastic #:nodoc:
|
|
636
675
|
basic_input_helper(:file_field, :file, method, options)
|
637
676
|
end
|
638
677
|
|
678
|
+
# Outputs a label and a standard Rails email field inside the wrapper.
|
679
|
+
def email_input(method, options)
|
680
|
+
basic_input_helper(:email_field, :email, method, options)
|
681
|
+
end
|
682
|
+
|
683
|
+
# Outputs a label and a standard Rails phone field inside the wrapper.
|
684
|
+
def phone_input(method, options)
|
685
|
+
basic_input_helper(:phone_field, :phone, method, options)
|
686
|
+
end
|
687
|
+
|
688
|
+
# Outputs a label and a standard Rails url field inside the wrapper.
|
689
|
+
def url_input(method, options)
|
690
|
+
basic_input_helper(:url_field, :url, method, options)
|
691
|
+
end
|
692
|
+
|
693
|
+
# Outputs a label and a standard Rails search field inside the wrapper.
|
694
|
+
def search_input(method, options)
|
695
|
+
basic_input_helper(:search_field, :search, method, options)
|
696
|
+
end
|
697
|
+
|
639
698
|
# Outputs a hidden field inside the wrapper, which should be hidden with CSS.
|
640
699
|
# Additionals options can be given using :input_hml. Should :input_html not be
|
641
700
|
# specified every option except for formtastic options will be sent straight
|
@@ -644,6 +703,7 @@ module Formtastic #:nodoc:
|
|
644
703
|
def hidden_input(method, options)
|
645
704
|
options ||= {}
|
646
705
|
html_options = options.delete(:input_html) || strip_formtastic_options(options)
|
706
|
+
html_options[:id] ||= generate_html_id(method, "")
|
647
707
|
self.hidden_field(method, html_options)
|
648
708
|
end
|
649
709
|
|
@@ -686,18 +746,19 @@ module Formtastic #:nodoc:
|
|
686
746
|
# </select>
|
687
747
|
#
|
688
748
|
#
|
689
|
-
# You can customize the options available in the select by passing in a collection
|
690
|
-
# Hash
|
691
|
-
# parent's class name from the method name and simply calling
|
692
|
-
# (VehicleOwner.
|
749
|
+
# You can customize the options available in the select by passing in a collection. A collection can be given
|
750
|
+
# as an Array, a Hash or as a String (containing pre-rendered HTML options). If not provided, the choices are
|
751
|
+
# found by inferring the parent's class name from the method name and simply calling all on it
|
752
|
+
# (VehicleOwner.all in the example above).
|
693
753
|
#
|
694
754
|
# Examples:
|
695
755
|
#
|
696
756
|
# f.input :author, :collection => @authors
|
697
|
-
# f.input :author, :collection => Author.
|
757
|
+
# f.input :author, :collection => Author.all
|
698
758
|
# f.input :author, :collection => [@justin, @kate]
|
699
759
|
# f.input :author, :collection => {@justin.name => @justin.id, @kate.name => @kate.id}
|
700
760
|
# f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
761
|
+
# f.input :author, :collection => grouped_options_for_select(["North America",[["United States","US"],["Canada","CA"]]])
|
701
762
|
#
|
702
763
|
# The :label_method option allows you to customize the text label inside each option tag two ways:
|
703
764
|
#
|
@@ -719,15 +780,6 @@ module Formtastic #:nodoc:
|
|
719
780
|
# f.input :author, :value_method => :login
|
720
781
|
# f.input :author, :value_method => Proc.new { |a| "author_#{a.login}" }
|
721
782
|
#
|
722
|
-
# You can pre-select a specific option value by passing in the :selected option.
|
723
|
-
#
|
724
|
-
# Examples:
|
725
|
-
#
|
726
|
-
# f.input :author, :selected => current_user.id
|
727
|
-
# f.input :author, :value_method => :login, :selected => current_user.login
|
728
|
-
# f.input :authors, :value_method => :login, :selected => Author.most_popular.collect(&:id)
|
729
|
-
# f.input :authors, :value_method => :login, :selected => nil # override any defaults: select none
|
730
|
-
#
|
731
783
|
# You can pass html_options to the select tag using :input_html => {}
|
732
784
|
#
|
733
785
|
# Examples:
|
@@ -738,14 +790,14 @@ module Formtastic #:nodoc:
|
|
738
790
|
# a prompt with the :prompt option, or disable the blank option with :include_blank => false.
|
739
791
|
#
|
740
792
|
#
|
741
|
-
# You can group the options in optgroup elements by passing the :group_by option
|
793
|
+
# You can group the options in optgroup elements by passing the :group_by option
|
742
794
|
# (Note: only tested for belongs_to relations)
|
743
|
-
#
|
795
|
+
#
|
744
796
|
# Examples:
|
745
797
|
#
|
746
798
|
# f.input :author, :group_by => :continent
|
747
|
-
#
|
748
|
-
# All the other options should work as expected. If you want to call a custom method on the
|
799
|
+
#
|
800
|
+
# All the other options should work as expected. If you want to call a custom method on the
|
749
801
|
# group item. You can include the option:group_label_method
|
750
802
|
# Examples:
|
751
803
|
#
|
@@ -753,24 +805,29 @@ module Formtastic #:nodoc:
|
|
753
805
|
#
|
754
806
|
def select_input(method, options)
|
755
807
|
html_options = options.delete(:input_html) || {}
|
756
|
-
options = set_include_blank(options)
|
757
808
|
html_options[:multiple] = html_options[:multiple] || options.delete(:multiple)
|
758
809
|
html_options.delete(:multiple) if html_options[:multiple].nil?
|
759
810
|
|
760
811
|
reflection = self.reflection_for(method)
|
761
812
|
if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
|
762
|
-
options[:include_blank] = false
|
763
813
|
html_options[:multiple] = true if html_options[:multiple].nil?
|
764
814
|
html_options[:size] ||= 5
|
815
|
+
|
816
|
+
if html_options[:multiple]
|
817
|
+
options[:include_blank] = false
|
818
|
+
else
|
819
|
+
options[:include_blank] ||= false
|
820
|
+
end
|
765
821
|
end
|
766
|
-
options
|
822
|
+
options = set_include_blank(options)
|
767
823
|
input_name = generate_association_input_name(method)
|
824
|
+
html_options[:id] ||= generate_html_id(input_name, "")
|
768
825
|
|
769
826
|
select_html = if options[:group_by]
|
770
|
-
# The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
|
827
|
+
# The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
|
771
828
|
# The formtastic user however shouldn't notice this too much.
|
772
829
|
raw_collection = find_raw_collection_for_column(method, options.reverse_merge(:find_options => { :include => options[:group_by] }))
|
773
|
-
label, value = detect_label_and_value_method!(raw_collection)
|
830
|
+
label, value = detect_label_and_value_method!(raw_collection, options)
|
774
831
|
group_collection = raw_collection.map { |option| option.send(options[:group_by]) }.uniq
|
775
832
|
group_label_method = options[:group_label_method] || detect_label_method(group_collection)
|
776
833
|
group_collection = group_collection.sort_by { |group_item| group_item.send(group_label_method) }
|
@@ -779,7 +836,7 @@ module Formtastic #:nodoc:
|
|
779
836
|
# Here comes the monster with 8 arguments
|
780
837
|
self.grouped_collection_select(input_name, group_collection,
|
781
838
|
group_association, group_label_method,
|
782
|
-
value, label,
|
839
|
+
value, label,
|
783
840
|
strip_formtastic_options(options), html_options)
|
784
841
|
else
|
785
842
|
collection = find_collection_for_column(method, options)
|
@@ -787,9 +844,15 @@ module Formtastic #:nodoc:
|
|
787
844
|
self.select(input_name, collection, strip_formtastic_options(options), html_options)
|
788
845
|
end
|
789
846
|
|
790
|
-
|
847
|
+
label_options = options_for_label(options).merge(:input_name => input_name)
|
848
|
+
label_options[:for] ||= html_options[:id]
|
849
|
+
self.label(method, label_options) << select_html
|
850
|
+
end
|
851
|
+
|
852
|
+
def boolean_select_input(method, options)
|
853
|
+
::ActiveSupport::Deprecation.warn(":as => :boolean_select is deprecated and will be removed in Formtastic 1.3 or later. Use :as => :select instead.", caller)
|
854
|
+
select_input(method, options)
|
791
855
|
end
|
792
|
-
alias :boolean_select_input :select_input
|
793
856
|
|
794
857
|
# Outputs a timezone select input as Rails' time_zone_select helper. You
|
795
858
|
# can give priority zones as option.
|
@@ -797,22 +860,15 @@ module Formtastic #:nodoc:
|
|
797
860
|
# Examples:
|
798
861
|
#
|
799
862
|
# f.input :time_zone, :as => :time_zone, :priority_zones => /Australia/
|
800
|
-
#
|
801
|
-
# You can pre-select a specific option value by passing in the :selected option.
|
802
|
-
# Note: Right now only works if the form object attribute value is not set (nil),
|
803
|
-
# because of how the core helper is implemented.
|
804
|
-
#
|
805
|
-
# Examples:
|
806
|
-
#
|
807
|
-
# f.input :my_favorite_time_zone, :as => :time_zone, :selected => 'Singapore'
|
808
|
-
#
|
809
863
|
def time_zone_input(method, options)
|
810
864
|
html_options = options.delete(:input_html) || {}
|
811
|
-
|
812
|
-
|
813
|
-
|
865
|
+
field_id = generate_html_id(method, "")
|
866
|
+
html_options[:id] ||= field_id
|
867
|
+
label_options = options_for_label(options)
|
868
|
+
label_options[:for] ||= html_options[:id]
|
869
|
+
self.label(method, label_options) <<
|
814
870
|
self.time_zone_select(method, options.delete(:priority_zones),
|
815
|
-
strip_formtastic_options(options)
|
871
|
+
strip_formtastic_options(options), html_options)
|
816
872
|
end
|
817
873
|
|
818
874
|
# Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
|
@@ -837,14 +893,14 @@ module Formtastic #:nodoc:
|
|
837
893
|
# </ol>
|
838
894
|
# </fieldset>
|
839
895
|
#
|
840
|
-
# You can customize the choices available in the radio button set by passing in a collection (an Array or
|
896
|
+
# You can customize the choices available in the radio button set by passing in a collection (an Array or
|
841
897
|
# Hash) through the :collection option. If not provided, the choices are found by reflecting on the association
|
842
|
-
# (Author.
|
898
|
+
# (Author.all in the example above).
|
843
899
|
#
|
844
900
|
# Examples:
|
845
901
|
#
|
846
902
|
# f.input :author, :as => :radio, :collection => @authors
|
847
|
-
# f.input :author, :as => :radio, :collection => Author.
|
903
|
+
# f.input :author, :as => :radio, :collection => Author.all
|
848
904
|
# f.input :author, :as => :radio, :collection => [@justin, @kate]
|
849
905
|
# f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
850
906
|
#
|
@@ -867,18 +923,10 @@ module Formtastic #:nodoc:
|
|
867
923
|
# f.input :author, :as => :radio, :value_method => :full_name
|
868
924
|
# f.input :author, :as => :radio, :value_method => :login
|
869
925
|
# f.input :author, :as => :radio, :value_method => Proc.new { |a| "author_#{a.login}" }
|
870
|
-
#
|
871
|
-
# You can force a particular radio button in the collection to be checked with the :selected option.
|
872
926
|
#
|
873
|
-
#
|
874
|
-
#
|
875
|
-
# f.input :subscribe_to_newsletter, :as => :radio, :selected => true
|
876
|
-
# f.input :subscribe_to_newsletter, :as => :radio, :collection => ["Yeah!", "Nope!"], :selected => "Nope!"
|
877
|
-
#
|
878
|
-
# Finally, you can set :value_as_class => true if you want the li wrapper around each radio
|
927
|
+
# Finally, you can set :value_as_class => true if you want the li wrapper around each radio
|
879
928
|
# button / label combination to contain a class with the value of the radio button (useful for
|
880
929
|
# applying specific CSS or Javascript to a particular radio button).
|
881
|
-
#
|
882
930
|
def radio_input(method, options)
|
883
931
|
collection = find_collection_for_column(method, options)
|
884
932
|
html_options = strip_formtastic_options(options).merge(options.delete(:input_html) || {})
|
@@ -886,8 +934,6 @@ module Formtastic #:nodoc:
|
|
886
934
|
input_name = generate_association_input_name(method)
|
887
935
|
value_as_class = options.delete(:value_as_class)
|
888
936
|
input_ids = []
|
889
|
-
selected_option_is_present = [:selected, :checked].any? { |k| options.key?(k) }
|
890
|
-
selected_value = (options.key?(:checked) ? options[:checked] : options[:selected]) if selected_option_is_present
|
891
937
|
|
892
938
|
list_item_content = collection.map do |c|
|
893
939
|
label = c.is_a?(Array) ? c.first : c
|
@@ -895,7 +941,7 @@ module Formtastic #:nodoc:
|
|
895
941
|
input_id = generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase)
|
896
942
|
input_ids << input_id
|
897
943
|
|
898
|
-
html_options[:
|
944
|
+
html_options[:id] = input_id
|
899
945
|
|
900
946
|
li_content = template.content_tag(:label,
|
901
947
|
Formtastic::Util.html_safe("#{self.radio_button(input_name, value, html_options)} #{escape_html_entities(label)}"),
|
@@ -905,12 +951,16 @@ module Formtastic #:nodoc:
|
|
905
951
|
li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {}
|
906
952
|
template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options)
|
907
953
|
end
|
908
|
-
|
954
|
+
|
909
955
|
template.content_tag(:fieldset,
|
910
956
|
legend_tag(method, options) << template.content_tag(:ol, Formtastic::Util.html_safe(list_item_content.join))
|
911
957
|
)
|
912
958
|
end
|
913
|
-
|
959
|
+
|
960
|
+
def boolean_radio_input(method, options)
|
961
|
+
::ActiveSupport::Deprecation.warn(":as => :boolean_radio is deprecated and will be removed in Formtastic 1.3 or later. Use :as => :radio instead.", caller)
|
962
|
+
radio_input(method, options)
|
963
|
+
end
|
914
964
|
|
915
965
|
# Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list
|
916
966
|
# items (li), one for each fragment for the date (year, month, day). Each li contains a label
|
@@ -918,15 +968,7 @@ module Formtastic #:nodoc:
|
|
918
968
|
# :labels should be a hash with the field (e.g. day) as key and the label text as value.
|
919
969
|
# See date_or_datetime_input for a more detailed output example.
|
920
970
|
#
|
921
|
-
#
|
922
|
-
#
|
923
|
-
# Examples:
|
924
|
-
#
|
925
|
-
# f.input :created_at, :as => :date, :selected => 1.day.ago
|
926
|
-
# f.input :created_at, :as => :date, :selected => nil # override any defaults: select none
|
927
|
-
# f.input :created_at, :as => :date, :labels => { :year => "Year", :month => "Month", :day => "Day" }
|
928
|
-
#
|
929
|
-
# Some of Rails' options for select_date are supported, but not everything yet, see
|
971
|
+
# Some of Rails' options for select_date are supported, but not everything yet, see
|
930
972
|
# documentation of date_or_datetime_input() for more information.
|
931
973
|
def date_input(method, options)
|
932
974
|
options = set_include_blank(options)
|
@@ -939,16 +981,7 @@ module Formtastic #:nodoc:
|
|
939
981
|
# the :labels option. :labels should be a hash with the field (e.g. day) as key and the label
|
940
982
|
# text as value. See date_or_datetime_input for a more detailed output example.
|
941
983
|
#
|
942
|
-
#
|
943
|
-
#
|
944
|
-
# Examples:
|
945
|
-
#
|
946
|
-
# f.input :created_at, :as => :datetime, :selected => 1.day.ago
|
947
|
-
# f.input :created_at, :as => :datetime, :selected => nil # override any defaults: select none
|
948
|
-
# f.input :created_at, :as => :date, :labels => { :year => "Year", :month => "Month", :day => "Day",
|
949
|
-
# :hour => "Hour", :minute => "Minute" }
|
950
|
-
#
|
951
|
-
# Some of Rails' options for select_date are supported, but not everything yet, see
|
984
|
+
# Some of Rails' options for select_date are supported, but not everything yet, see
|
952
985
|
# documentation of date_or_datetime_input() for more information.
|
953
986
|
def datetime_input(method, options)
|
954
987
|
options = set_include_blank(options)
|
@@ -961,23 +994,15 @@ module Formtastic #:nodoc:
|
|
961
994
|
# :labels should be a hash with the field (e.g. day) as key and the label text as value.
|
962
995
|
# See date_or_datetime_input for a more detailed output example.
|
963
996
|
#
|
964
|
-
#
|
965
|
-
#
|
966
|
-
# Examples:
|
967
|
-
#
|
968
|
-
# f.input :created_at, :as => :time, :selected => 1.hour.ago
|
969
|
-
# f.input :created_at, :as => :time, :selected => nil # override any defaults: select none
|
970
|
-
# f.input :created_at, :as => :date, :labels => { :hour => "Hour", :minute => "Minute" }
|
971
|
-
#
|
972
|
-
# Some of Rails' options for select_time are supported, but not everything yet, see
|
997
|
+
# Some of Rails' options for select_time are supported, but not everything yet, see
|
973
998
|
# documentation of date_or_datetime_input() for more information.
|
974
999
|
def time_input(method, options)
|
975
1000
|
options = set_include_blank(options)
|
976
1001
|
date_or_datetime_input(method, options.merge(:discard_year => true, :discard_month => true, :discard_day => true))
|
977
1002
|
end
|
978
|
-
|
979
|
-
# Helper method used by :as => (:date|:datetime|:time). Generates a fieldset containing a
|
980
|
-
# legend (for what would normally be considered the label), and an ordered list of list items
|
1003
|
+
|
1004
|
+
# Helper method used by :as => (:date|:datetime|:time). Generates a fieldset containing a
|
1005
|
+
# legend (for what would normally be considered the label), and an ordered list of list items
|
981
1006
|
# for year, month, day, hour, etc, each containing a label and a select. Example:
|
982
1007
|
#
|
983
1008
|
# <fieldset>
|
@@ -1016,9 +1041,6 @@ module Formtastic #:nodoc:
|
|
1016
1041
|
#
|
1017
1042
|
# * @:order => [:month, :day, :year]@
|
1018
1043
|
# * @:include_seconds@ => true@
|
1019
|
-
# * @:selected => Time.mktime(2008)@
|
1020
|
-
# * @:selected => Date.new(2008)@
|
1021
|
-
# * @:selected => nil@
|
1022
1044
|
# * @:discard_(year|month|day|hour|minute) => true@
|
1023
1045
|
# * @:include_blank => true@
|
1024
1046
|
# * @:labels => {}@
|
@@ -1036,8 +1058,7 @@ module Formtastic #:nodoc:
|
|
1036
1058
|
list_items_capture = ""
|
1037
1059
|
hidden_fields_capture = ""
|
1038
1060
|
|
1039
|
-
datetime =
|
1040
|
-
datetime = @object.send(method) if @object && @object.send(method) # object value trumps :selected value
|
1061
|
+
datetime = @object.send(method) if @object && @object.send(method)
|
1041
1062
|
|
1042
1063
|
html_options = options.delete(:input_html) || {}
|
1043
1064
|
input_ids = []
|
@@ -1098,13 +1119,13 @@ module Formtastic #:nodoc:
|
|
1098
1119
|
#
|
1099
1120
|
# You can customize the options available in the set by passing in a collection (Array) of
|
1100
1121
|
# ActiveRecord objects through the :collection option. If not provided, the choices are found
|
1101
|
-
# by inferring the parent's class name from the method name and simply calling
|
1102
|
-
# it (Author.
|
1122
|
+
# by inferring the parent's class name from the method name and simply calling all on
|
1123
|
+
# it (Author.all in the example above).
|
1103
1124
|
#
|
1104
1125
|
# Examples:
|
1105
1126
|
#
|
1106
1127
|
# f.input :author, :as => :check_boxes, :collection => @authors
|
1107
|
-
# f.input :author, :as => :check_boxes, :collection => Author.
|
1128
|
+
# f.input :author, :as => :check_boxes, :collection => Author.all
|
1108
1129
|
# f.input :author, :as => :check_boxes, :collection => [@justin, @kate]
|
1109
1130
|
#
|
1110
1131
|
# The :label_method option allows you to customize the label for each checkbox two ways:
|
@@ -1127,25 +1148,16 @@ module Formtastic #:nodoc:
|
|
1127
1148
|
# f.input :author, :as => :check_boxes, :value_method => :login
|
1128
1149
|
# f.input :author, :as => :check_boxes, :value_method => Proc.new { |a| "author_#{a.login}" }
|
1129
1150
|
#
|
1130
|
-
# You can pre-select/check a specific checkbox value by passing in the :selected option (alias :checked works as well).
|
1131
|
-
#
|
1132
|
-
# Examples:
|
1133
|
-
#
|
1134
|
-
# f.input :authors, :as => :check_boxes, :selected => @justin
|
1135
|
-
# f.input :authors, :as => :check_boxes, :selected => Author.most_popular.collect(&:id)
|
1136
|
-
# f.input :authors, :as => :check_boxes, :selected => nil # override any defaults: select none
|
1137
|
-
#
|
1138
|
-
#
|
1139
1151
|
# Formtastic works around a bug in rails handling of check box collections by
|
1140
|
-
# not generating the hidden fields for state checking of the checkboxes
|
1152
|
+
# not generating the hidden fields for state checking of the checkboxes
|
1141
1153
|
# The :hidden_fields option provides a way to re-enable these hidden inputs by
|
1142
1154
|
# setting it to true.
|
1143
1155
|
#
|
1144
1156
|
# f.input :authors, :as => :check_boxes, :hidden_fields => false
|
1145
1157
|
# f.input :authors, :as => :check_boxes, :hidden_fields => true
|
1146
1158
|
#
|
1147
|
-
# Finally, you can set :value_as_class => true if you want the li wrapper around each checkbox / label
|
1148
|
-
# combination to contain a class with the value of the radio button (useful for applying specific
|
1159
|
+
# Finally, you can set :value_as_class => true if you want the li wrapper around each checkbox / label
|
1160
|
+
# combination to contain a class with the value of the radio button (useful for applying specific
|
1149
1161
|
# CSS or Javascript to a particular checkbox).
|
1150
1162
|
#
|
1151
1163
|
def check_boxes_input(method, options)
|
@@ -1190,27 +1202,21 @@ module Formtastic #:nodoc:
|
|
1190
1202
|
template.content_tag(:fieldset, fieldset_content)
|
1191
1203
|
end
|
1192
1204
|
|
1193
|
-
# Used by check_boxes input. The selected values will be set
|
1194
|
-
#
|
1195
|
-
# * Explicitly provided through :selected or :checked
|
1196
|
-
# * Values retrieved through an association
|
1205
|
+
# Used by check_boxes input. The selected values will be set by retrieving the value
|
1206
|
+
# through the association.
|
1197
1207
|
#
|
1198
1208
|
# If the collection is not a hash or an array of strings, fixnums or symbols,
|
1199
1209
|
# we use value_method to retrieve an array with the values
|
1200
|
-
#
|
1201
1210
|
def find_selected_values_for_column(method, options)
|
1202
|
-
|
1203
|
-
if selected_option_is_present
|
1204
|
-
selected_values = (options.key?(:checked) ? options[:checked] : options[:selected])
|
1205
|
-
elsif object.respond_to?(method)
|
1211
|
+
if object.respond_to?(method)
|
1206
1212
|
collection = [object.send(method)].compact.flatten
|
1207
1213
|
label, value = detect_label_and_value_method!(collection, options)
|
1208
|
-
|
1214
|
+
[*collection.map { |o| send_or_call(value, o) }].compact
|
1215
|
+
else
|
1216
|
+
[]
|
1209
1217
|
end
|
1210
|
-
selected_values = [*selected_values].compact
|
1211
|
-
selected_values
|
1212
1218
|
end
|
1213
|
-
|
1219
|
+
|
1214
1220
|
# Outputs a custom hidden field for check_boxes
|
1215
1221
|
def create_hidden_field_for_check_boxes(method, value_as_class) #:nodoc:
|
1216
1222
|
options = value_as_class ? { :class => [method.to_s.singularize, 'default'].join('_') } : {}
|
@@ -1225,14 +1231,14 @@ module Formtastic #:nodoc:
|
|
1225
1231
|
self.check_box(input_name, html_options, checked_value, unchecked_value)
|
1226
1232
|
end
|
1227
1233
|
|
1228
|
-
# Outputs a country select input, wrapping around a regular country_select helper.
|
1234
|
+
# Outputs a country select input, wrapping around a regular country_select helper.
|
1229
1235
|
# Rails doesn't come with a country_select helper by default any more, so you'll need to install
|
1230
1236
|
# the "official" plugin, or, if you wish, any other country_select plugin that behaves in the
|
1231
1237
|
# same way.
|
1232
1238
|
#
|
1233
1239
|
# The Rails plugin iso-3166-country-select plugin can be found "here":http://github.com/rails/iso-3166-country-select.
|
1234
1240
|
#
|
1235
|
-
# By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
|
1241
|
+
# By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
|
1236
1242
|
# which you can change to suit your market and user base (see README for more info on config).
|
1237
1243
|
#
|
1238
1244
|
# Examples:
|
@@ -1245,35 +1251,39 @@ module Formtastic #:nodoc:
|
|
1245
1251
|
html_options = options.delete(:input_html) || {}
|
1246
1252
|
priority_countries = options.delete(:priority_countries) || self.class.priority_countries
|
1247
1253
|
|
1248
|
-
|
1254
|
+
field_id = generate_html_id(method, "")
|
1255
|
+
html_options[:id] ||= field_id
|
1256
|
+
label_options = options_for_label(options)
|
1257
|
+
label_options[:for] ||= html_options[:id]
|
1258
|
+
|
1259
|
+
self.label(method, label_options) <<
|
1249
1260
|
self.country_select(method, priority_countries, strip_formtastic_options(options), html_options)
|
1250
1261
|
end
|
1251
1262
|
|
1252
1263
|
# Outputs a label containing a checkbox and the label text. The label defaults
|
1253
1264
|
# to the column name (method name) and can be altered with the :label option.
|
1254
1265
|
# :checked_value and :unchecked_value options are also available.
|
1255
|
-
#
|
1256
|
-
# You can pre-select/check the boolean checkbox by passing in the :selected option (alias :checked works as well).
|
1257
|
-
#
|
1258
|
-
# Examples:
|
1259
|
-
#
|
1260
|
-
# f.input :allow_comments, :as => :boolean, :selected => true # override any default value: selected/checked
|
1261
|
-
#
|
1262
1266
|
def boolean_input(method, options)
|
1263
1267
|
html_options = options.delete(:input_html) || {}
|
1264
|
-
checked = options.key?(:checked) ? options[:checked] : options[:selected]
|
1265
|
-
html_options[:checked] = checked == true if [:selected, :checked].any? { |k| options.key?(k) }
|
1266
1268
|
checked_value = options.delete(:checked_value) || '1'
|
1267
1269
|
unchecked_value = options.delete(:unchecked_value) || '0'
|
1268
1270
|
|
1269
|
-
input = self.check_box(method, strip_formtastic_options(options).merge(html_options),
|
1270
|
-
|
1271
|
+
#input = self.check_box(method, strip_formtastic_options(options).merge(html_options),
|
1272
|
+
# checked_value, unchecked_value)
|
1273
|
+
field_id = [@@custom_namespace,@object_name,method].reject{|x|x.blank?}.join("_")
|
1274
|
+
input = template.check_box_tag(
|
1275
|
+
"#{@object_name}[#{method}]",
|
1276
|
+
checked_value,
|
1277
|
+
(@object && @object.send(:"#{method}") == checked_value),
|
1278
|
+
:id => field_id
|
1279
|
+
)
|
1271
1280
|
options = options_for_label(options)
|
1281
|
+
options[:for] ||= field_id
|
1272
1282
|
|
1273
1283
|
# the label() method will insert this nested input into the label at the last minute
|
1274
1284
|
options[:label_prefix_for_nested_input] = input
|
1275
1285
|
|
1276
|
-
self.label(method, options)
|
1286
|
+
template.hidden_field_tag(method, unchecked_value) << self.label(method, options)
|
1277
1287
|
end
|
1278
1288
|
|
1279
1289
|
# Generates an input for the given method using the type supplied with :as.
|
@@ -1286,29 +1296,33 @@ module Formtastic #:nodoc:
|
|
1286
1296
|
def inline_hints_for(method, options) #:nodoc:
|
1287
1297
|
options[:hint] = localized_string(method, options[:hint], :hint)
|
1288
1298
|
return if options[:hint].blank? or options[:hint].kind_of? Hash
|
1289
|
-
|
1299
|
+
hint_class = options[:hint_class] || self.class.default_hint_class
|
1300
|
+
template.content_tag(:p, Formtastic::Util.html_safe(options[:hint]), :class => hint_class)
|
1290
1301
|
end
|
1291
1302
|
|
1292
1303
|
# Creates an error sentence by calling to_sentence on the errors array.
|
1293
1304
|
#
|
1294
|
-
def error_sentence(errors) #:nodoc:
|
1295
|
-
|
1305
|
+
def error_sentence(errors, options = {}) #:nodoc:
|
1306
|
+
error_class = options[:error_class] || self.class.default_inline_error_class
|
1307
|
+
template.content_tag(:p, Formtastic::Util.html_safe(errors.to_sentence.untaint), :class => error_class)
|
1296
1308
|
end
|
1297
1309
|
|
1298
1310
|
# Creates an error li list.
|
1299
1311
|
#
|
1300
|
-
def error_list(errors) #:nodoc:
|
1312
|
+
def error_list(errors, options = {}) #:nodoc:
|
1313
|
+
error_class = options[:error_class] || self.class.default_error_list_class
|
1301
1314
|
list_elements = []
|
1302
1315
|
errors.each do |error|
|
1303
1316
|
list_elements << template.content_tag(:li, Formtastic::Util.html_safe(error.untaint))
|
1304
1317
|
end
|
1305
|
-
template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class =>
|
1318
|
+
template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class => error_class)
|
1306
1319
|
end
|
1307
1320
|
|
1308
1321
|
# Creates an error sentence containing only the first error
|
1309
1322
|
#
|
1310
|
-
def error_first(errors) #:nodoc:
|
1311
|
-
|
1323
|
+
def error_first(errors, options = {}) #:nodoc:
|
1324
|
+
error_class = options[:error_class] || self.class.default_inline_error_class
|
1325
|
+
template.content_tag(:p, Formtastic::Util.html_safe(errors.first.untaint), :class => error_class)
|
1312
1326
|
end
|
1313
1327
|
|
1314
1328
|
# Generates the required or optional string. If the value set is a proc,
|
@@ -1406,9 +1420,14 @@ module Formtastic #:nodoc:
|
|
1406
1420
|
|
1407
1421
|
# Generates the legend for radiobuttons and checkboxes
|
1408
1422
|
def legend_tag(method, options = {})
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1423
|
+
if options[:label] == false
|
1424
|
+
Formtastic::Util.html_safe("")
|
1425
|
+
else
|
1426
|
+
text = localized_string(method, options[:label], :label) || humanized_attribute_name(method)
|
1427
|
+
text += required_or_optional_string(options.delete(:required))
|
1428
|
+
text = Formtastic::Util.html_safe(text)
|
1429
|
+
template.content_tag :legend, template.label_tag(nil, text, :for => nil), :class => :label
|
1430
|
+
end
|
1412
1431
|
end
|
1413
1432
|
|
1414
1433
|
# For methods that have a database column, take a best guess as to what the input method
|
@@ -1427,15 +1446,19 @@ module Formtastic #:nodoc:
|
|
1427
1446
|
return :password if method.to_s =~ /password/
|
1428
1447
|
return :country if method.to_s =~ /country$/
|
1429
1448
|
return :time_zone if method.to_s =~ /time_zone/
|
1449
|
+
return :email if method.to_s =~ /email/
|
1450
|
+
return :url if method.to_s =~ /^url$|^website$|_url$/
|
1451
|
+
return :phone if method.to_s =~ /(phone|fax)/
|
1452
|
+
return :search if method.to_s =~ /^search$/
|
1430
1453
|
when :integer
|
1431
|
-
return :select if method
|
1454
|
+
return :select if self.reflection_for(method)
|
1432
1455
|
return :numeric
|
1433
1456
|
when :float, :decimal
|
1434
1457
|
return :numeric
|
1435
1458
|
when :timestamp
|
1436
1459
|
return :datetime
|
1437
1460
|
end
|
1438
|
-
|
1461
|
+
|
1439
1462
|
# Try look for hints in options hash. Quite common senario: Enum keys stored as string in the database.
|
1440
1463
|
return :select if column.type == :string && options.key?(:collection)
|
1441
1464
|
# Try 3: Assume the input name will be the same as the column type (e.g. string_input).
|
@@ -1444,8 +1467,7 @@ module Formtastic #:nodoc:
|
|
1444
1467
|
if @object
|
1445
1468
|
return :select if self.reflection_for(method)
|
1446
1469
|
|
1447
|
-
file
|
1448
|
-
return :file if file && self.class.file_methods.any? { |m| file.respond_to?(m) }
|
1470
|
+
return :file if is_file?(method, options)
|
1449
1471
|
end
|
1450
1472
|
|
1451
1473
|
return :select if options.key?(:collection)
|
@@ -1454,6 +1476,14 @@ module Formtastic #:nodoc:
|
|
1454
1476
|
end
|
1455
1477
|
end
|
1456
1478
|
|
1479
|
+
def is_file?(method, options = {})
|
1480
|
+
@files ||= {}
|
1481
|
+
@files[method] ||= (options[:as].present? && options[:as] == :file) || begin
|
1482
|
+
file = @object.send(method) if @object && @object.respond_to?(method)
|
1483
|
+
file && self.class.file_methods.any?{|m| file.respond_to?(m)}
|
1484
|
+
end
|
1485
|
+
end
|
1486
|
+
|
1457
1487
|
# Used by select and radio inputs. The collection can be retrieved by
|
1458
1488
|
# three ways:
|
1459
1489
|
#
|
@@ -1468,6 +1498,9 @@ module Formtastic #:nodoc:
|
|
1468
1498
|
def find_collection_for_column(column, options) #:nodoc:
|
1469
1499
|
collection = find_raw_collection_for_column(column, options)
|
1470
1500
|
|
1501
|
+
# Return if we have a plain string
|
1502
|
+
return collection if collection.instance_of?(String) || collection.instance_of?(::Formtastic::Util.rails_safe_buffer_class)
|
1503
|
+
|
1471
1504
|
# Return if we have an Array of strings, fixnums or arrays
|
1472
1505
|
return collection if (collection.instance_of?(Array) || collection.instance_of?(Range)) &&
|
1473
1506
|
[Array, Fixnum, String, Symbol].include?(collection.first.class) &&
|
@@ -1539,16 +1572,16 @@ module Formtastic #:nodoc:
|
|
1539
1572
|
def detect_group_association(method, group_by)
|
1540
1573
|
object_to_method_reflection = self.reflection_for(method)
|
1541
1574
|
method_class = object_to_method_reflection.klass
|
1542
|
-
|
1575
|
+
|
1543
1576
|
method_to_group_association = method_class.reflect_on_association(group_by)
|
1544
1577
|
group_class = method_to_group_association.klass
|
1545
|
-
|
1578
|
+
|
1546
1579
|
# This will return in the normal case
|
1547
1580
|
return method.to_s.pluralize.to_sym if group_class.reflect_on_association(method.to_s.pluralize)
|
1548
|
-
|
1581
|
+
|
1549
1582
|
# This is for belongs_to associations named differently than their class
|
1550
1583
|
# form.input :parent, :group_by => :customer
|
1551
|
-
# eg.
|
1584
|
+
# eg.
|
1552
1585
|
# class Project
|
1553
1586
|
# belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id'
|
1554
1587
|
# belongs_to :customer
|
@@ -1558,9 +1591,9 @@ module Formtastic #:nodoc:
|
|
1558
1591
|
# end
|
1559
1592
|
group_method = method_class.to_s.underscore.pluralize.to_sym
|
1560
1593
|
return group_method if group_class.reflect_on_association(group_method) # :projects
|
1561
|
-
|
1594
|
+
|
1562
1595
|
# This is for has_many associations named differently than their class
|
1563
|
-
# eg.
|
1596
|
+
# eg.
|
1564
1597
|
# class Project
|
1565
1598
|
# belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id'
|
1566
1599
|
# belongs_to :customer
|
@@ -1570,9 +1603,9 @@ module Formtastic #:nodoc:
|
|
1570
1603
|
# end
|
1571
1604
|
possible_associations = group_class.reflect_on_all_associations(:has_many).find_all{|assoc| assoc.klass == object_class}
|
1572
1605
|
return possible_associations.first.name.to_sym if possible_associations.count == 1
|
1573
|
-
|
1606
|
+
|
1574
1607
|
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"
|
1575
|
-
|
1608
|
+
|
1576
1609
|
end
|
1577
1610
|
|
1578
1611
|
# Returns a hash to be used by radio and select inputs when a boolean field
|
@@ -1619,19 +1652,64 @@ module Formtastic #:nodoc:
|
|
1619
1652
|
@object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
|
1620
1653
|
end
|
1621
1654
|
|
1655
|
+
# Returns the active validations for the given method or an empty Array if no validations are
|
1656
|
+
# found for the method.
|
1657
|
+
#
|
1658
|
+
# By default, the if/unless options of the validations are evaluated and only the validations
|
1659
|
+
# that should be run for the current object state are returned. Pass :all to the last
|
1660
|
+
# parameter to return :all validations regardless of if/unless options.
|
1661
|
+
#
|
1662
|
+
# Requires the ValidationReflection plugin to be present or an ActiveModel. Returns an epmty
|
1663
|
+
# Array if neither is the case.
|
1664
|
+
#
|
1665
|
+
def validations_for(method, mode = :active)
|
1666
|
+
# ActiveModel?
|
1667
|
+
validations = if @object && @object.class.respond_to?(:validators_on)
|
1668
|
+
@object.class.validators_on(method)
|
1669
|
+
else
|
1670
|
+
# ValidationReflection plugin?
|
1671
|
+
if @object && @object.class.respond_to?(:reflect_on_validations_for)
|
1672
|
+
@object.class.reflect_on_validations_for(method)
|
1673
|
+
else
|
1674
|
+
[]
|
1675
|
+
end
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
validations = validations.select do |validation|
|
1679
|
+
(validation.options.present? ? options_require_validation?(validation.options) : true)
|
1680
|
+
end unless mode == :all
|
1681
|
+
|
1682
|
+
return validations
|
1683
|
+
end
|
1684
|
+
|
1622
1685
|
# Generates default_string_options by retrieving column information from
|
1623
1686
|
# the database.
|
1624
1687
|
#
|
1625
1688
|
def default_string_options(method, type) #:nodoc:
|
1689
|
+
def get_maxlength_for(method)
|
1690
|
+
validation = validations_for(method).find do |validation|
|
1691
|
+
(validation.respond_to?(:macro) && validation.macro == :validates_length_of) || # Rails 2 validation
|
1692
|
+
(validation.respond_to?(:kind) && validation.kind == :length) # Rails 3 validator
|
1693
|
+
end
|
1694
|
+
|
1695
|
+
if validation
|
1696
|
+
validation.options[:maximum] || (validation.options[:within].present? ? validation.options[:within].max : nil)
|
1697
|
+
else
|
1698
|
+
nil
|
1699
|
+
end
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
validation_max_limit = get_maxlength_for(method)
|
1626
1703
|
column = self.column_for(method)
|
1627
1704
|
|
1628
1705
|
if type == :text
|
1629
|
-
{ :
|
1706
|
+
{ :rows => self.class.default_text_area_height, :cols => self.class.default_text_area_width }
|
1630
1707
|
elsif type == :numeric || column.nil? || column.limit.nil?
|
1631
|
-
{ :
|
1708
|
+
{ :maxlength => validation_max_limit,
|
1709
|
+
:size => self.class.default_text_field_size }
|
1632
1710
|
else
|
1633
|
-
{ :maxlength => column.limit,
|
1634
|
-
:size => self.class.default_text_field_size
|
1711
|
+
{ :maxlength => validation_max_limit || column.limit,
|
1712
|
+
:size => self.class.default_text_field_size }
|
1635
1713
|
end
|
1636
1714
|
end
|
1637
1715
|
|
@@ -1641,16 +1719,16 @@ module Formtastic #:nodoc:
|
|
1641
1719
|
# and method names.
|
1642
1720
|
#
|
1643
1721
|
def generate_html_id(method_name, value='input') #:nodoc:
|
1644
|
-
if options.has_key?(:index)
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1722
|
+
index = if options.has_key?(:index)
|
1723
|
+
options[:index]
|
1724
|
+
elsif defined?(@auto_index)
|
1725
|
+
@auto_index
|
1726
|
+
else
|
1727
|
+
""
|
1728
|
+
end
|
1651
1729
|
sanitized_method_name = method_name.to_s.gsub(/[\?\/\-]$/, '')
|
1652
1730
|
|
1653
|
-
|
1731
|
+
[@@custom_namespace, sanitized_object_name, index, sanitized_method_name, value].reject{|x|x.blank?}.join('_')
|
1654
1732
|
end
|
1655
1733
|
|
1656
1734
|
# Gets the nested_child_index value from the parent builder. In Rails 2.3
|
@@ -1698,13 +1776,13 @@ module Formtastic #:nodoc:
|
|
1698
1776
|
# 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
|
1699
1777
|
# 'formtastic.%{type}.%{model}.%{attribute}'
|
1700
1778
|
# 'formtastic.%{type}.%{attribute}'
|
1701
|
-
#
|
1779
|
+
#
|
1702
1780
|
# Example:
|
1703
|
-
#
|
1781
|
+
#
|
1704
1782
|
# 'formtastic.labels.post.edit.title'
|
1705
1783
|
# 'formtastic.labels.post.title'
|
1706
1784
|
# 'formtastic.labels.title'
|
1707
|
-
#
|
1785
|
+
#
|
1708
1786
|
# NOTE: Generic, but only used for form input titles/labels/hints/actions (titles = legends, actions = buttons).
|
1709
1787
|
#
|
1710
1788
|
def localized_string(key, value, type, options = {}) #:nodoc:
|
@@ -1720,7 +1798,9 @@ module Formtastic #:nodoc:
|
|
1720
1798
|
action_name = template.params[:action].to_s rescue ''
|
1721
1799
|
attribute_name = key.to_s
|
1722
1800
|
|
1723
|
-
defaults = ::Formtastic::I18n::SCOPES.
|
1801
|
+
defaults = ::Formtastic::I18n::SCOPES.reject do |i18n_scope|
|
1802
|
+
nested_model_name.nil? && i18n_scope.match(/nested_model/)
|
1803
|
+
end.collect do |i18n_scope|
|
1724
1804
|
i18n_path = i18n_scope.dup
|
1725
1805
|
i18n_path.gsub!('%{action}', action_name)
|
1726
1806
|
i18n_path.gsub!('%{model}', model_name)
|
@@ -1731,8 +1811,17 @@ module Formtastic #:nodoc:
|
|
1731
1811
|
end
|
1732
1812
|
defaults << ''
|
1733
1813
|
|
1734
|
-
|
1814
|
+
defaults.uniq!
|
1815
|
+
|
1816
|
+
default_key = defaults.shift
|
1817
|
+
i18n_value = ::Formtastic::I18n.t(default_key,
|
1735
1818
|
options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
|
1819
|
+
if i18n_value.blank? && type == :label
|
1820
|
+
# This is effectively what Rails label helper does for i18n lookup
|
1821
|
+
options[:scope] = [:helpers, type]
|
1822
|
+
options[:default] = defaults
|
1823
|
+
i18n_value = ::I18n.t(default_key, options)
|
1824
|
+
end
|
1736
1825
|
i18n_value = escape_html_entities(i18n_value) if i18n_value.is_a?(::String)
|
1737
1826
|
i18n_value.blank? ? nil : i18n_value
|
1738
1827
|
end
|
@@ -1798,7 +1887,7 @@ module Formtastic #:nodoc:
|
|
1798
1887
|
# <% end %>
|
1799
1888
|
#
|
1800
1889
|
# The above examples use a resource-oriented style of form_for() helper where only the @post
|
1801
|
-
# object is given as an argument, but the generic style is also supported, as are forms with
|
1890
|
+
# object is given as an argument, but the generic style is also supported, as are forms with
|
1802
1891
|
# inline objects (Post.new) rather than objects with instance variables (@post):
|
1803
1892
|
#
|
1804
1893
|
# <% semantic_form_for :post, @post, :url => posts_path do |f| %>
|
@@ -1810,8 +1899,9 @@ module Formtastic #:nodoc:
|
|
1810
1899
|
# <% end %>
|
1811
1900
|
module SemanticFormHelper
|
1812
1901
|
@@builder = ::Formtastic::SemanticFormBuilder
|
1813
|
-
|
1814
|
-
|
1902
|
+
@@default_form_class = 'formtastic'
|
1903
|
+
mattr_accessor :builder, :default_form_class
|
1904
|
+
|
1815
1905
|
# Override the default ActiveRecordHelper behaviour of wrapping the input.
|
1816
1906
|
# This gets taken care of semantically by adding an error class to the LI tag
|
1817
1907
|
# containing the input.
|
@@ -1819,7 +1909,7 @@ module Formtastic #:nodoc:
|
|
1819
1909
|
FIELD_ERROR_PROC = proc do |html_tag, instance_tag|
|
1820
1910
|
html_tag
|
1821
1911
|
end
|
1822
|
-
|
1912
|
+
|
1823
1913
|
def with_custom_field_error_proc(&block)
|
1824
1914
|
default_field_error_proc = ::ActionView::Base.field_error_proc
|
1825
1915
|
::ActionView::Base.field_error_proc = FIELD_ERROR_PROC
|
@@ -1827,7 +1917,7 @@ module Formtastic #:nodoc:
|
|
1827
1917
|
ensure
|
1828
1918
|
::ActionView::Base.field_error_proc = default_field_error_proc
|
1829
1919
|
end
|
1830
|
-
|
1920
|
+
|
1831
1921
|
def semantic_remote_form_for_wrapper(record_or_name_or_array, *args, &proc)
|
1832
1922
|
options = args.extract_options!
|
1833
1923
|
if self.respond_to? :remote_form_for
|
@@ -1844,18 +1934,19 @@ module Formtastic #:nodoc:
|
|
1844
1934
|
options = args.extract_options!
|
1845
1935
|
options[:builder] ||= @@builder
|
1846
1936
|
options[:html] ||= {}
|
1937
|
+
@@builder.custom_namespace = options[:namespace].to_s
|
1847
1938
|
|
1848
1939
|
singularizer = defined?(ActiveModel::Naming.singular) ? ActiveModel::Naming.method(:singular) : ActionController::RecordIdentifier.method(:singular_class_name)
|
1849
1940
|
|
1850
1941
|
class_names = options[:html][:class] ? options[:html][:class].split(" ") : []
|
1851
|
-
class_names <<
|
1942
|
+
class_names << @@default_form_class
|
1852
1943
|
class_names << case record_or_name_or_array
|
1853
|
-
when String, Symbol then record_or_name_or_array.to_s
|
1854
|
-
when Array then singularizer.call(record_or_name_or_array.last.class) # [@post, @comment] # => "comment"
|
1855
|
-
else singularizer.call(record_or_name_or_array.class) # @post => "post"
|
1944
|
+
when String, Symbol then record_or_name_or_array.to_s # :post => "post"
|
1945
|
+
when Array then options[:as] || singularizer.call(record_or_name_or_array.last.class) # [@post, @comment] # => "comment"
|
1946
|
+
else options[:as] || singularizer.call(record_or_name_or_array.class) # @post => "post"
|
1856
1947
|
end
|
1857
1948
|
options[:html][:class] = class_names.join(" ")
|
1858
|
-
|
1949
|
+
|
1859
1950
|
with_custom_field_error_proc do
|
1860
1951
|
#{meth}(record_or_name_or_array, *(args << options), &proc)
|
1861
1952
|
end
|
@@ -1863,8 +1954,8 @@ module Formtastic #:nodoc:
|
|
1863
1954
|
END_SRC
|
1864
1955
|
end
|
1865
1956
|
alias :semantic_remote_form_for_real :semantic_remote_form_for
|
1866
|
-
alias :semantic_remote_form_for :semantic_remote_form_for_wrapper
|
1957
|
+
alias :semantic_remote_form_for :semantic_remote_form_for_wrapper
|
1867
1958
|
alias :semantic_form_remote_for :semantic_remote_form_for
|
1868
|
-
|
1959
|
+
|
1869
1960
|
end
|
1870
1961
|
end
|