formtastic 1.2.4 → 3.1.5
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +46 -0
- data/.yardopts +1 -0
- data/Appraisals +43 -0
- data/CHANGELOG +54 -0
- data/DEPRECATIONS +52 -0
- data/Gemfile +3 -0
- data/README.md +629 -0
- data/RELEASE_PROCESS +6 -0
- data/Rakefile +35 -0
- data/app/assets/stylesheets/formtastic.css +289 -0
- data/app/assets/stylesheets/formtastic_ie6.css +33 -0
- data/app/assets/stylesheets/formtastic_ie7.css +23 -0
- data/formtastic.gemspec +42 -0
- data/gemfiles/rails_3.2.gemfile +9 -0
- data/gemfiles/rails_4.0.4.gemfile +8 -0
- data/gemfiles/rails_4.1.gemfile +8 -0
- data/gemfiles/rails_4.2.gemfile +8 -0
- data/gemfiles/rails_4.gemfile +8 -0
- data/gemfiles/rails_5.0.gemfile +8 -0
- data/gemfiles/rails_edge.gemfile +15 -0
- data/lib/formtastic.rb +40 -1945
- data/lib/formtastic/action_class_finder.rb +18 -0
- data/lib/formtastic/actions.rb +11 -0
- data/lib/formtastic/actions/base.rb +156 -0
- data/lib/formtastic/actions/button_action.rb +67 -0
- data/lib/formtastic/actions/buttonish.rb +17 -0
- data/lib/formtastic/actions/input_action.rb +70 -0
- data/lib/formtastic/actions/link_action.rb +88 -0
- data/lib/formtastic/deprecation.rb +42 -0
- data/lib/formtastic/engine.rb +11 -0
- data/lib/formtastic/form_builder.rb +124 -0
- data/lib/formtastic/helpers.rb +16 -0
- data/lib/formtastic/helpers/action_helper.rb +162 -0
- data/lib/formtastic/helpers/actions_helper.rb +168 -0
- data/lib/formtastic/helpers/enum.rb +13 -0
- data/lib/formtastic/helpers/errors_helper.rb +81 -0
- data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
- data/lib/formtastic/helpers/file_column_detection.rb +16 -0
- data/lib/formtastic/helpers/form_helper.rb +203 -0
- data/lib/formtastic/helpers/input_helper.rb +407 -0
- data/lib/formtastic/helpers/inputs_helper.rb +411 -0
- data/lib/formtastic/helpers/reflection.rb +37 -0
- data/lib/formtastic/html_attributes.rb +32 -0
- data/lib/formtastic/i18n.rb +4 -2
- data/lib/formtastic/input_class_finder.rb +18 -0
- data/lib/formtastic/inputs.rb +39 -0
- data/lib/formtastic/inputs/base.rb +76 -0
- data/lib/formtastic/inputs/base/associations.rb +31 -0
- data/lib/formtastic/inputs/base/choices.rb +108 -0
- data/lib/formtastic/inputs/base/collections.rb +159 -0
- data/lib/formtastic/inputs/base/database.rb +22 -0
- data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
- data/lib/formtastic/inputs/base/errors.rb +58 -0
- data/lib/formtastic/inputs/base/fileish.rb +23 -0
- data/lib/formtastic/inputs/base/hints.rb +31 -0
- data/lib/formtastic/inputs/base/html.rb +53 -0
- data/lib/formtastic/inputs/base/labelling.rb +52 -0
- data/lib/formtastic/inputs/base/naming.rb +42 -0
- data/lib/formtastic/inputs/base/numeric.rb +50 -0
- data/lib/formtastic/inputs/base/options.rb +17 -0
- data/lib/formtastic/inputs/base/placeholder.rb +17 -0
- data/lib/formtastic/inputs/base/stringish.rb +38 -0
- data/lib/formtastic/inputs/base/timeish.rb +241 -0
- data/lib/formtastic/inputs/base/validations.rb +215 -0
- data/lib/formtastic/inputs/base/wrapping.rb +50 -0
- data/lib/formtastic/inputs/boolean_input.rb +118 -0
- data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
- data/lib/formtastic/inputs/color_input.rb +42 -0
- data/lib/formtastic/inputs/country_input.rb +86 -0
- data/lib/formtastic/inputs/datalist_input.rb +41 -0
- data/lib/formtastic/inputs/date_picker_input.rb +93 -0
- data/lib/formtastic/inputs/date_select_input.rb +34 -0
- data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
- data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
- data/lib/formtastic/inputs/email_input.rb +41 -0
- data/lib/formtastic/inputs/file_input.rb +42 -0
- data/lib/formtastic/inputs/hidden_input.rb +62 -0
- data/lib/formtastic/inputs/number_input.rb +88 -0
- data/lib/formtastic/inputs/password_input.rb +41 -0
- data/lib/formtastic/inputs/phone_input.rb +42 -0
- data/lib/formtastic/inputs/radio_input.rb +163 -0
- data/lib/formtastic/inputs/range_input.rb +95 -0
- data/lib/formtastic/inputs/search_input.rb +41 -0
- data/lib/formtastic/inputs/select_input.rb +235 -0
- data/lib/formtastic/inputs/string_input.rb +36 -0
- data/lib/formtastic/inputs/text_input.rb +48 -0
- data/lib/formtastic/inputs/time_picker_input.rb +99 -0
- data/lib/formtastic/inputs/time_select_input.rb +38 -0
- data/lib/formtastic/inputs/time_zone_input.rb +58 -0
- data/lib/formtastic/inputs/url_input.rb +41 -0
- data/lib/formtastic/localized_string.rb +17 -0
- data/lib/formtastic/localizer.rb +152 -0
- data/lib/formtastic/namespaced_class_finder.rb +99 -0
- data/lib/formtastic/util.rb +35 -16
- data/lib/formtastic/version.rb +3 -0
- data/lib/generators/formtastic/form/form_generator.rb +64 -37
- data/lib/generators/formtastic/input/input_generator.rb +46 -0
- data/lib/generators/formtastic/install/install_generator.rb +13 -5
- data/lib/generators/templates/_form.html.erb +10 -4
- data/lib/generators/templates/_form.html.haml +8 -4
- data/lib/generators/templates/_form.html.slim +8 -0
- data/lib/generators/templates/formtastic.rb +77 -44
- data/lib/generators/templates/input.rb +19 -0
- data/lib/locale/en.yml +3 -0
- data/sample/basic_inputs.html +224 -0
- data/sample/config.ru +69 -0
- data/sample/index.html +14 -0
- data/spec/action_class_finder_spec.rb +12 -0
- data/spec/actions/button_action_spec.rb +63 -0
- data/spec/actions/generic_action_spec.rb +521 -0
- data/spec/actions/input_action_spec.rb +59 -0
- data/spec/actions/link_action_spec.rb +92 -0
- data/spec/builder/custom_builder_spec.rb +116 -0
- data/spec/builder/error_proc_spec.rb +27 -0
- data/spec/builder/semantic_fields_for_spec.rb +142 -0
- data/spec/fast_spec_helper.rb +12 -0
- data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
- data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
- data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
- data/spec/helpers/action_helper_spec.rb +19 -0
- data/spec/helpers/actions_helper_spec.rb +143 -0
- data/spec/helpers/form_helper_spec.rb +218 -0
- data/spec/helpers/input_helper_spec.rb +6 -0
- data/spec/helpers/inputs_helper_spec.rb +655 -0
- data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
- data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
- data/spec/helpers/reflection_helper_spec.rb +32 -0
- data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
- data/spec/i18n_spec.rb +210 -0
- data/spec/input_class_finder_spec.rb +10 -0
- data/spec/inputs/base/collections_spec.rb +76 -0
- data/spec/inputs/base/validations_spec.rb +342 -0
- data/spec/inputs/boolean_input_spec.rb +254 -0
- data/spec/inputs/check_boxes_input_spec.rb +546 -0
- data/spec/inputs/color_input_spec.rb +97 -0
- data/spec/inputs/country_input_spec.rb +133 -0
- data/spec/inputs/custom_input_spec.rb +55 -0
- data/spec/inputs/datalist_input_spec.rb +61 -0
- data/spec/inputs/date_picker_input_spec.rb +449 -0
- data/spec/inputs/date_select_input_spec.rb +235 -0
- data/spec/inputs/datetime_picker_input_spec.rb +490 -0
- data/spec/inputs/datetime_select_input_spec.rb +193 -0
- data/spec/inputs/email_input_spec.rb +85 -0
- data/spec/inputs/file_input_spec.rb +89 -0
- data/spec/inputs/hidden_input_spec.rb +135 -0
- data/spec/inputs/include_blank_spec.rb +78 -0
- data/spec/inputs/label_spec.rb +149 -0
- data/spec/inputs/number_input_spec.rb +815 -0
- data/spec/inputs/password_input_spec.rb +99 -0
- data/spec/inputs/phone_input_spec.rb +85 -0
- data/spec/inputs/placeholder_spec.rb +71 -0
- data/spec/inputs/radio_input_spec.rb +328 -0
- data/spec/inputs/range_input_spec.rb +505 -0
- data/spec/inputs/readonly_spec.rb +50 -0
- data/spec/inputs/search_input_spec.rb +84 -0
- data/spec/inputs/select_input_spec.rb +615 -0
- data/spec/inputs/string_input_spec.rb +260 -0
- data/spec/inputs/text_input_spec.rb +187 -0
- data/spec/inputs/time_picker_input_spec.rb +455 -0
- data/spec/inputs/time_select_input_spec.rb +248 -0
- data/spec/inputs/time_zone_input_spec.rb +143 -0
- data/spec/inputs/url_input_spec.rb +85 -0
- data/spec/inputs/with_options_spec.rb +43 -0
- data/spec/localizer_spec.rb +130 -0
- data/spec/namespaced_class_finder_spec.rb +79 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +525 -0
- data/spec/support/custom_macros.rb +564 -0
- data/spec/support/deprecation.rb +6 -0
- data/spec/support/shared_examples.rb +1313 -0
- data/spec/support/specialized_class_finder_shared_example.rb +27 -0
- data/spec/support/test_environment.rb +31 -0
- data/spec/util_spec.rb +66 -0
- metadata +434 -161
- data/README.textile +0 -682
- data/generators/form/USAGE +0 -16
- data/generators/form/form_generator.rb +0 -111
- data/generators/formtastic/formtastic_generator.rb +0 -26
- data/init.rb +0 -5
- data/lib/formtastic/layout_helper.rb +0 -12
- data/lib/formtastic/railtie.rb +0 -14
- data/lib/generators/templates/formtastic.css +0 -145
- data/lib/generators/templates/formtastic_changes.css +0 -14
- data/lib/generators/templates/rails2/_form.html.erb +0 -5
- data/lib/generators/templates/rails2/_form.html.haml +0 -4
- data/rails/init.rb +0 -2
data/lib/formtastic/i18n.rb
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
module Formtastic
|
|
4
|
+
# @private
|
|
4
5
|
module I18n
|
|
5
6
|
|
|
6
7
|
DEFAULT_SCOPE = [:formtastic].freeze
|
|
7
8
|
DEFAULT_VALUES = YAML.load_file(File.expand_path("../../locale/en.yml", __FILE__))["en"]["formtastic"].freeze
|
|
8
9
|
SCOPES = [
|
|
9
10
|
'%{model}.%{nested_model}.%{action}.%{attribute}',
|
|
10
|
-
'%{model}.%{action}.%{attribute}',
|
|
11
11
|
'%{model}.%{nested_model}.%{attribute}',
|
|
12
|
-
'%{
|
|
12
|
+
'%{nested_model}.%{action}.%{attribute}',
|
|
13
13
|
'%{nested_model}.%{attribute}',
|
|
14
|
+
'%{model}.%{action}.%{attribute}',
|
|
15
|
+
'%{model}.%{attribute}',
|
|
14
16
|
'%{attribute}'
|
|
15
17
|
]
|
|
16
18
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
|
|
3
|
+
# Uses the {Formtastic::NamespacedClassFinder} to look up input class names.
|
|
4
|
+
#
|
|
5
|
+
# See {Formtastic::FormBuilder#namespaced_input_class} for details.
|
|
6
|
+
#
|
|
7
|
+
class InputClassFinder < NamespacedClassFinder
|
|
8
|
+
|
|
9
|
+
# @param builder [FormBuilder]
|
|
10
|
+
def initialize(builder)
|
|
11
|
+
super builder.input_namespaces
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def class_name(as)
|
|
15
|
+
"#{super}Input"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
extend ActiveSupport::Autoload
|
|
4
|
+
|
|
5
|
+
autoload :Base
|
|
6
|
+
autoload :Basic
|
|
7
|
+
autoload :BooleanInput
|
|
8
|
+
autoload :CheckBoxesInput
|
|
9
|
+
autoload :ColorInput
|
|
10
|
+
autoload :CountryInput
|
|
11
|
+
autoload :DatalistInput
|
|
12
|
+
autoload :DateInput
|
|
13
|
+
autoload :DatePickerInput
|
|
14
|
+
autoload :DatetimePickerInput
|
|
15
|
+
autoload :DateSelectInput
|
|
16
|
+
autoload :DatetimeInput
|
|
17
|
+
autoload :DatetimeSelectInput
|
|
18
|
+
autoload :EmailInput
|
|
19
|
+
autoload :FileInput
|
|
20
|
+
autoload :HiddenInput
|
|
21
|
+
autoload :NumberInput
|
|
22
|
+
autoload :NumericInput
|
|
23
|
+
autoload :PasswordInput
|
|
24
|
+
autoload :PhoneInput
|
|
25
|
+
autoload :RadioInput
|
|
26
|
+
autoload :RangeInput
|
|
27
|
+
autoload :SearchInput
|
|
28
|
+
autoload :SelectInput
|
|
29
|
+
autoload :StringInput
|
|
30
|
+
autoload :TextInput
|
|
31
|
+
autoload :TimeInput
|
|
32
|
+
autoload :TimePickerInput
|
|
33
|
+
autoload :TimeSelectInput
|
|
34
|
+
autoload :TimeZoneInput
|
|
35
|
+
autoload :Timeish
|
|
36
|
+
autoload :UrlInput
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
|
|
5
|
+
attr_accessor :builder, :template, :object, :object_name, :method, :options
|
|
6
|
+
|
|
7
|
+
def initialize(builder, template, object, object_name, method, options)
|
|
8
|
+
@builder = builder
|
|
9
|
+
@template = template
|
|
10
|
+
@object = object
|
|
11
|
+
@object_name = object_name
|
|
12
|
+
@method = method
|
|
13
|
+
@options = options.dup
|
|
14
|
+
|
|
15
|
+
# Deprecate :member_label and :member_value, remove v4.0
|
|
16
|
+
member_deprecation_message = "passing an Array of label/value pairs like [['Justin', 2], ['Kate', 3]] into :collection directly (consider building the array in your model using Model.pluck)"
|
|
17
|
+
warn_deprecated_option!(:member_label, member_deprecation_message)
|
|
18
|
+
warn_deprecated_option!(:member_value, member_deprecation_message)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Usefull for deprecating options.
|
|
22
|
+
def warn_and_correct_option!(old_option_name, new_option_name)
|
|
23
|
+
if options.key?(old_option_name)
|
|
24
|
+
::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version", caller(6))
|
|
25
|
+
options[new_option_name] = options.delete(old_option_name)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Usefull for deprecating options.
|
|
30
|
+
def warn_deprecated_option!(old_option_name, instructions)
|
|
31
|
+
if options.key?(old_option_name)
|
|
32
|
+
::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}`. :#{old_option_name} will be removed in the next version", caller(6))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Usefull for raising an error on previously supported option.
|
|
37
|
+
def removed_option!(old_option_name)
|
|
38
|
+
raise ArgumentError, ":#{old_option_name} is no longer available" if options.key?(old_option_name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
extend ActiveSupport::Autoload
|
|
42
|
+
|
|
43
|
+
autoload :DatetimePickerish
|
|
44
|
+
autoload :Associations
|
|
45
|
+
autoload :Collections
|
|
46
|
+
autoload :Choices
|
|
47
|
+
autoload :Database
|
|
48
|
+
autoload :Errors
|
|
49
|
+
autoload :Fileish
|
|
50
|
+
autoload :Hints
|
|
51
|
+
autoload :Html
|
|
52
|
+
autoload :Labelling
|
|
53
|
+
autoload :Naming
|
|
54
|
+
autoload :Numeric
|
|
55
|
+
autoload :Options
|
|
56
|
+
autoload :Placeholder
|
|
57
|
+
autoload :Stringish
|
|
58
|
+
autoload :Timeish
|
|
59
|
+
autoload :Validations
|
|
60
|
+
autoload :Wrapping
|
|
61
|
+
|
|
62
|
+
include Html
|
|
63
|
+
include Options
|
|
64
|
+
include Database
|
|
65
|
+
include Errors
|
|
66
|
+
include Hints
|
|
67
|
+
include Naming
|
|
68
|
+
include Validations
|
|
69
|
+
include Fileish
|
|
70
|
+
include Associations
|
|
71
|
+
include Labelling
|
|
72
|
+
include Wrapping
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Associations
|
|
5
|
+
include Formtastic::Helpers::Reflection
|
|
6
|
+
|
|
7
|
+
# :belongs_to, etc
|
|
8
|
+
def association
|
|
9
|
+
@association ||= association_macro_for_method(method)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def reflection
|
|
13
|
+
@reflection ||= reflection_for(method)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def belongs_to?
|
|
17
|
+
association == :belongs_to
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def has_many?
|
|
21
|
+
association == :has_many
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def association_primary_key
|
|
25
|
+
association_primary_key_for_method(method)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Choices
|
|
5
|
+
|
|
6
|
+
def choices_wrapping(&block)
|
|
7
|
+
template.content_tag(:fieldset,
|
|
8
|
+
template.capture(&block),
|
|
9
|
+
choices_wrapping_html_options
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def choices_wrapping_html_options
|
|
14
|
+
{ :class => "choices" }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def choices_group_wrapping(&block)
|
|
18
|
+
template.content_tag(:ol,
|
|
19
|
+
template.capture(&block),
|
|
20
|
+
choices_group_wrapping_html_options
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def choices_group_wrapping_html_options
|
|
25
|
+
{ :class => "choices-group" }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def choice_wrapping(html_options, &block)
|
|
29
|
+
template.content_tag(:li,
|
|
30
|
+
template.capture(&block),
|
|
31
|
+
html_options
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def choice_wrapping_html_options(choice)
|
|
36
|
+
classes = ['choice']
|
|
37
|
+
classes << "#{sanitized_method_name.singularize}_#{choice_html_safe_value(choice)}" if value_as_class?
|
|
38
|
+
|
|
39
|
+
{ :class => classes.join(" ") }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def choice_html(choice)
|
|
43
|
+
raise "choice_html() needs to be implemented when including Formtastic::Inputs::Base::Choices"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def choice_label(choice)
|
|
47
|
+
if choice.is_a?(Array)
|
|
48
|
+
choice.first
|
|
49
|
+
else
|
|
50
|
+
choice
|
|
51
|
+
end.to_s
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def choice_value(choice)
|
|
55
|
+
choice.is_a?(Array) ? choice[1] : choice
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def choice_html_options(choice)
|
|
59
|
+
custom_choice_html_options(choice).merge(default_choice_html_options(choice))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def default_choice_html_options(choice)
|
|
63
|
+
{ :id => choice_input_dom_id(choice) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def custom_choice_html_options(choice)
|
|
67
|
+
(choice.is_a?(Array) && choice.size > 2) ? choice[-1] : {}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def choice_html_safe_value(choice)
|
|
71
|
+
choice_value(choice).to_s.gsub(/\s/, '_').gsub(/[^\w-]/, '').downcase
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def choice_input_dom_id(choice)
|
|
75
|
+
[
|
|
76
|
+
builder.dom_id_namespace,
|
|
77
|
+
sanitized_object_name,
|
|
78
|
+
builder.options[:index],
|
|
79
|
+
association_primary_key || method,
|
|
80
|
+
choice_html_safe_value(choice)
|
|
81
|
+
].compact.reject { |i| i.blank? }.join("_")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def value_as_class?
|
|
85
|
+
options[:value_as_class]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def legend_html
|
|
89
|
+
if render_label?
|
|
90
|
+
template.content_tag(:legend,
|
|
91
|
+
template.content_tag(:label, label_text),
|
|
92
|
+
label_html_options.merge(:class => "label")
|
|
93
|
+
)
|
|
94
|
+
else
|
|
95
|
+
"".html_safe
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Override to remove the for attribute since this isn't associated with any element, as it's
|
|
100
|
+
# nested inside the legend.
|
|
101
|
+
def label_html_options
|
|
102
|
+
super.merge(:for => nil)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Collections
|
|
5
|
+
|
|
6
|
+
def label_method
|
|
7
|
+
@label_method ||= (label_method_from_options || label_and_value_method.first)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def label_method_from_options
|
|
11
|
+
options[:member_label]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def value_method
|
|
15
|
+
@value_method ||= (value_method_from_options || label_and_value_method[-1])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def value_method_from_options
|
|
19
|
+
options[:member_value]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def label_and_value_method
|
|
23
|
+
@label_and_value_method ||= label_and_value_method_from_collection(raw_collection)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def label_and_value_method_from_collection(_collection)
|
|
27
|
+
sample = _collection.first || _collection[-1]
|
|
28
|
+
|
|
29
|
+
case sample
|
|
30
|
+
when Array
|
|
31
|
+
label, value = :first, :last
|
|
32
|
+
when Integer
|
|
33
|
+
label, value = :to_s, :to_i
|
|
34
|
+
when Symbol, String, NilClass
|
|
35
|
+
label, value = :to_s, :to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Order of preference: user supplied method, class defaults, auto-detect
|
|
39
|
+
label ||= builder.collection_label_methods.find { |m| sample.respond_to?(m) }
|
|
40
|
+
value ||= builder.collection_value_methods.find { |m| sample.respond_to?(m) }
|
|
41
|
+
|
|
42
|
+
[label, value]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def raw_collection
|
|
46
|
+
@raw_collection ||= (collection_from_options || collection_from_enum || collection_from_association || collection_for_boolean)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def collection
|
|
50
|
+
# Return if we have a plain string
|
|
51
|
+
return raw_collection if raw_collection.is_a?(String)
|
|
52
|
+
|
|
53
|
+
# Return if we have an Array of strings, integers or arrays
|
|
54
|
+
return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
|
|
55
|
+
([Array, String].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
|
|
56
|
+
!(options.include?(:member_label) || options.include?(:member_value))
|
|
57
|
+
|
|
58
|
+
raw_collection.map { |o| [send_or_call(label_method, o), send_or_call(value_method, o)] }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def collection_from_options
|
|
62
|
+
items = options[:collection]
|
|
63
|
+
case items
|
|
64
|
+
when Hash
|
|
65
|
+
items.to_a
|
|
66
|
+
when Range
|
|
67
|
+
items.to_a.collect{ |c| [c.to_s, c] }
|
|
68
|
+
else
|
|
69
|
+
items
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def collection_from_association
|
|
74
|
+
if reflection
|
|
75
|
+
if reflection.respond_to?(:options)
|
|
76
|
+
raise PolymorphicInputWithoutCollectionError.new(
|
|
77
|
+
"A collection must be supplied for #{method} input. Collections cannot be guessed for polymorphic associations."
|
|
78
|
+
) if reflection.options[:polymorphic] == true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
conditions_from_reflection = (reflection.respond_to?(:options) && reflection.options[:conditions]) || {}
|
|
82
|
+
conditions_from_reflection = conditions_from_reflection.call if conditions_from_reflection.is_a?(Proc)
|
|
83
|
+
|
|
84
|
+
scope_conditions = conditions_from_reflection.empty? ? nil : {:conditions => conditions_from_reflection}
|
|
85
|
+
where_conditions = (scope_conditions && scope_conditions[:conditions]) || {}
|
|
86
|
+
|
|
87
|
+
if Util.rails3?
|
|
88
|
+
reflection.klass.scoped(scope_conditions).where({}) # where is uneccessary, but keeps the stubbing simpler while we support rails3
|
|
89
|
+
else
|
|
90
|
+
reflection.klass.where(where_conditions)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Assuming the following model:
|
|
96
|
+
#
|
|
97
|
+
# class Post < ActiveRecord::Base
|
|
98
|
+
# enum :status => [ :active, :archived ]
|
|
99
|
+
# end
|
|
100
|
+
#
|
|
101
|
+
# We would end up with a collection like this:
|
|
102
|
+
#
|
|
103
|
+
# [["Active", "active"], ["Archived", "archived"]
|
|
104
|
+
#
|
|
105
|
+
# The first element in each array uses String#humanize, but I18n
|
|
106
|
+
# translations are available too. Set them with the following structure.
|
|
107
|
+
#
|
|
108
|
+
# en:
|
|
109
|
+
# activerecord:
|
|
110
|
+
# attributes:
|
|
111
|
+
# post:
|
|
112
|
+
# statuses:
|
|
113
|
+
# active: Custom Active Label Here
|
|
114
|
+
# archived: Custom Archived Label Here
|
|
115
|
+
def collection_from_enum
|
|
116
|
+
if collection_from_enum?
|
|
117
|
+
method_name = method.to_s
|
|
118
|
+
|
|
119
|
+
enum_options_hash = object.defined_enums[method_name]
|
|
120
|
+
enum_options_hash.map do |name, value|
|
|
121
|
+
key = "activerecord.attributes.#{object_name}.#{method_name.pluralize}.#{name}"
|
|
122
|
+
label = ::I18n.translate(key, :default => name.humanize)
|
|
123
|
+
[label, name]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def collection_from_enum?
|
|
129
|
+
object.respond_to?(:defined_enums) && object.defined_enums.has_key?(method.to_s)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def collection_for_boolean
|
|
133
|
+
true_text = options[:true] || Formtastic::I18n.t(:yes)
|
|
134
|
+
false_text = options[:false] || Formtastic::I18n.t(:no)
|
|
135
|
+
|
|
136
|
+
# TODO options[:value_as_class] = true unless options.key?(:value_as_class)
|
|
137
|
+
|
|
138
|
+
[ [true_text, true], [false_text, false] ]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def send_or_call(duck, object)
|
|
142
|
+
if duck.respond_to?(:call)
|
|
143
|
+
duck.call(object)
|
|
144
|
+
elsif object.respond_to? duck.to_sym
|
|
145
|
+
object.send(duck)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Avoids an issue where `send_or_call` can be a String and duck can be something simple like
|
|
150
|
+
# `:first`, which obviously String responds to.
|
|
151
|
+
def send_or_call_or_object(duck, object)
|
|
152
|
+
return object if object.is_a?(String) || object.is_a?(Integer) || object.is_a?(Symbol) # TODO what about other classes etc?
|
|
153
|
+
send_or_call(duck, object)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|