formtastic 1.1.0 → 1.2.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|