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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Placeholder
|
|
5
|
+
|
|
6
|
+
def input_html_options
|
|
7
|
+
{:placeholder => placeholder_text}.merge(super)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def placeholder_text
|
|
11
|
+
localized_string(method, options[:placeholder], :placeholder)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Stringish
|
|
5
|
+
|
|
6
|
+
# @abstract Override this method in your input class to describe how the input should render itself.
|
|
7
|
+
def to_html
|
|
8
|
+
input_wrapping do
|
|
9
|
+
label_html <<
|
|
10
|
+
builder.text_field(method, input_html_options)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Overrides standard `input_html_options` to provide a `maxlength` and `size` attribute.
|
|
15
|
+
def input_html_options
|
|
16
|
+
{
|
|
17
|
+
:maxlength => maxlength,
|
|
18
|
+
:size => size
|
|
19
|
+
}.merge(super)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def size
|
|
23
|
+
builder.default_text_field_size
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def maxlength
|
|
27
|
+
options[:input_html].try(:[], :maxlength) || limit
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def wrapper_html_options
|
|
31
|
+
new_class = [super[:class], "stringish"].compact.join(" ")
|
|
32
|
+
super.merge(:class => new_class)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
# Timeish inputs (`:date_select`, `:datetime_select`, `:time_select`) are similar to the Rails date and time
|
|
5
|
+
# helpers (`date_select`, `datetime_select`, `time_select`), rendering a series of `<select>`
|
|
6
|
+
# tags for each fragment (year, month, day, hour, minute, seconds). The fragments are then
|
|
7
|
+
# re-combined to a date by ActiveRecord through multi-parameter assignment.
|
|
8
|
+
#
|
|
9
|
+
# The mark-up produced by Rails is simple but far from ideal, with no way to label the
|
|
10
|
+
# individual fragments for accessibility, no fieldset to group the related fields, and no
|
|
11
|
+
# legend describing the group. Formtastic addresses this within the standard `<li>` wrapper
|
|
12
|
+
# with a `<fieldset>` with a `<legend>` as a label, followed by an ordered list (`<ol>`) of
|
|
13
|
+
# list items (`<li>`), one for each fragment (year, month, ...). Each `<li>` fragment contains
|
|
14
|
+
# a `<label>` (eg "Year") for the fragment, and a `<select>` containing `<option>`s (eg a
|
|
15
|
+
# range of years).
|
|
16
|
+
#
|
|
17
|
+
# In the supplied formtastic.css file, the resulting mark-up is styled to appear a lot like a
|
|
18
|
+
# standard Rails date time select by:
|
|
19
|
+
#
|
|
20
|
+
# * styling the legend to look like the other labels (to the left hand side of the selects)
|
|
21
|
+
# * floating the `<li>` fragments against each other as a single line
|
|
22
|
+
# * hiding the `<label>` of each fragment with `display:none`
|
|
23
|
+
#
|
|
24
|
+
# @example `:date_select` input with full form context and sample HTMl output
|
|
25
|
+
#
|
|
26
|
+
# <%= semantic_form_for(@post) do |f| %>
|
|
27
|
+
# <%= f.inputs do %>
|
|
28
|
+
# ...
|
|
29
|
+
# <%= f.input :publish_at, :as => :date_select %>
|
|
30
|
+
# <% end %>
|
|
31
|
+
# <% end %>
|
|
32
|
+
#
|
|
33
|
+
# <form...>
|
|
34
|
+
# <fieldset class="inputs">
|
|
35
|
+
# <ol>
|
|
36
|
+
# <li class="date">
|
|
37
|
+
# <fieldset class="fragments">
|
|
38
|
+
# <ol class="fragments-group">
|
|
39
|
+
# <li class="fragment">
|
|
40
|
+
# <label for="post_publish_at_1i">Year</label>
|
|
41
|
+
# <select id="post_publish_at_1i" name="post[publish_at_1i]">...</select>
|
|
42
|
+
# </li>
|
|
43
|
+
# <li class="fragment">
|
|
44
|
+
# <label for="post_publish_at_2i">Month</label>
|
|
45
|
+
# <select id="post_publish_at_2i" name="post[publish_at_2i]">...</select>
|
|
46
|
+
# </li>
|
|
47
|
+
# <li class="fragment">
|
|
48
|
+
# <label for="post_publish_at_3i">Day</label>
|
|
49
|
+
# <select id="post_publish_at_3i" name="post[publish_at_3i]">...</select>
|
|
50
|
+
# </li>
|
|
51
|
+
# </ol>
|
|
52
|
+
# </fieldset>
|
|
53
|
+
# </li>
|
|
54
|
+
# </ol>
|
|
55
|
+
# </fieldset>
|
|
56
|
+
# </form>
|
|
57
|
+
#
|
|
58
|
+
#
|
|
59
|
+
# @example `:time_select` input
|
|
60
|
+
# <%= f.input :publish_at, :as => :time_select %>
|
|
61
|
+
#
|
|
62
|
+
# @example `:datetime_select` input
|
|
63
|
+
# <%= f.input :publish_at, :as => :datetime_select %>
|
|
64
|
+
#
|
|
65
|
+
# @example Change the labels for each fragment
|
|
66
|
+
# <%= f.input :publish_at, :as => :date_select, :labels => { :year => "Y", :month => "M", :day => "D" } %>
|
|
67
|
+
#
|
|
68
|
+
# @example Suppress the labels for all fragments
|
|
69
|
+
# <%= f.input :publish_at, :as => :date_select, :labels => false %>
|
|
70
|
+
#
|
|
71
|
+
# @example Skip a fragment (defaults to 1, skips all following fragments)
|
|
72
|
+
# <%= f.input :publish_at, :as => :datetime_select, :discard_minute => true %>
|
|
73
|
+
# <%= f.input :publish_at, :as => :datetime_select, :discard_hour => true %>
|
|
74
|
+
# <%= f.input :publish_at, :as => :datetime_select, :discard_day => true %>
|
|
75
|
+
# <%= f.input :publish_at, :as => :datetime_select, :discard_month => true %>
|
|
76
|
+
# <%= f.input :publish_at, :as => :datetime_select, :discard_year => true %>
|
|
77
|
+
#
|
|
78
|
+
# @example Change the order
|
|
79
|
+
# <%= f.input :publish_at, :as => :date_select, :order => [:month, :day, :year] %>
|
|
80
|
+
#
|
|
81
|
+
# @example Include seconds with times (excluded by default)
|
|
82
|
+
# <%= f.input :publish_at, :as => :time_select, :include_seconds => true %>
|
|
83
|
+
#
|
|
84
|
+
# @example Specify if there should be a blank option at the start of each select or not. Note that, unlike select inputs, :include_blank does not accept a string value.
|
|
85
|
+
# <%= f.input :publish_at, :as => :time_select, :include_blank => true %>
|
|
86
|
+
# <%= f.input :publish_at, :as => :time_select, :include_blank => false %>
|
|
87
|
+
#
|
|
88
|
+
# @todo Document i18n
|
|
89
|
+
# @todo Check what other Rails options are supported (`start_year`, `end_year`, `use_month_numbers`, `use_short_month`, `add_month_numbers`, `prompt`), write tests for them, and otherwise support them
|
|
90
|
+
# @todo Could we take the rendering from Rails' helpers and inject better HTML in and around it rather than re-inventing the whee?
|
|
91
|
+
module Timeish
|
|
92
|
+
|
|
93
|
+
def to_html
|
|
94
|
+
input_wrapping do
|
|
95
|
+
fragments_wrapping do
|
|
96
|
+
hidden_fragments <<
|
|
97
|
+
fragments_label <<
|
|
98
|
+
template.content_tag(:ol,
|
|
99
|
+
fragments.map do |fragment|
|
|
100
|
+
fragment_wrapping do
|
|
101
|
+
fragment_label_html(fragment) <<
|
|
102
|
+
fragment_input_html(fragment)
|
|
103
|
+
end
|
|
104
|
+
end.join.html_safe, # TODO is this safe?
|
|
105
|
+
{ :class => 'fragments-group' } # TODO refactor to fragments_group_wrapping
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def fragments
|
|
112
|
+
date_fragments + time_fragments
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def time_fragments
|
|
116
|
+
options[:include_seconds] ? [:hour, :minute, :second] : [:hour, :minute]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def date_fragments
|
|
120
|
+
options[:order] || i18n_date_fragments || default_date_fragments
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def default_date_fragments
|
|
124
|
+
[:year, :month, :day]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def fragment_wrapping(&block)
|
|
128
|
+
template.content_tag(:li, template.capture(&block), fragment_wrapping_html_options)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def fragment_wrapping_html_options
|
|
132
|
+
{ :class => 'fragment' }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def fragment_label(fragment)
|
|
136
|
+
labels_from_options = options.key?(:labels) ? options[:labels] : {}
|
|
137
|
+
if !labels_from_options
|
|
138
|
+
''
|
|
139
|
+
elsif labels_from_options.key?(fragment)
|
|
140
|
+
labels_from_options[fragment]
|
|
141
|
+
else
|
|
142
|
+
::I18n.t(fragment.to_s, :default => fragment.to_s.humanize, :scope => [:datetime, :prompts])
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def fragment_id(fragment)
|
|
147
|
+
"#{input_html_options[:id]}_#{position(fragment)}i"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def fragment_name(fragment)
|
|
151
|
+
"#{method}(#{position(fragment)}i)"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def fragment_label_html(fragment)
|
|
155
|
+
text = fragment_label(fragment)
|
|
156
|
+
text.blank? ? "".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def value
|
|
160
|
+
object.send(method) if object && object.respond_to?(method)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def fragment_input_html(fragment)
|
|
164
|
+
opts = input_options.merge(:prefix => fragment_prefix, :field_name => fragment_name(fragment), :default => value, :include_blank => include_blank?)
|
|
165
|
+
template.send(:"select_#{fragment}", value, opts, input_html_options.merge(:id => fragment_id(fragment)))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def fragment_prefix
|
|
169
|
+
if builder.options.key?(:index)
|
|
170
|
+
object_name + "[#{builder.options[:index]}]"
|
|
171
|
+
else
|
|
172
|
+
object_name
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# TODO extract to BlankOptions or similar -- Select uses similar code
|
|
177
|
+
def include_blank?
|
|
178
|
+
options.key?(:include_blank) ? options[:include_blank] : builder.include_blank_for_select_by_default
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def positions
|
|
182
|
+
{ :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def position(fragment)
|
|
186
|
+
positions[fragment]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def i18n_date_fragments
|
|
190
|
+
order = ::I18n.t(:order, :scope => [:date])
|
|
191
|
+
if order.is_a?(Array)
|
|
192
|
+
order.map &:to_sym
|
|
193
|
+
else
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def fragments_wrapping(&block)
|
|
199
|
+
template.content_tag(:fieldset,
|
|
200
|
+
template.capture(&block).html_safe,
|
|
201
|
+
fragments_wrapping_html_options
|
|
202
|
+
)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def fragments_wrapping_html_options
|
|
206
|
+
{ :class => "fragments" }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def fragments_label
|
|
210
|
+
if render_label?
|
|
211
|
+
template.content_tag(:legend,
|
|
212
|
+
builder.label(method, label_text, :for => fragment_id(fragments.first)),
|
|
213
|
+
:class => "label"
|
|
214
|
+
)
|
|
215
|
+
else
|
|
216
|
+
"".html_safe
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def fragments_inner_wrapping(&block)
|
|
221
|
+
template.content_tag(:ol,
|
|
222
|
+
template.capture(&block)
|
|
223
|
+
)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def hidden_fragments
|
|
227
|
+
"".html_safe
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def hidden_field_name(fragment)
|
|
231
|
+
if builder.options.key?(:index)
|
|
232
|
+
"#{object_name}[#{builder.options[:index]}][#{fragment_name(fragment)}]"
|
|
233
|
+
else
|
|
234
|
+
"#{object_name}[#{fragment_name(fragment)}]"
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
module Inputs
|
|
3
|
+
module Base
|
|
4
|
+
module Validations
|
|
5
|
+
|
|
6
|
+
class IndeterminableMinimumAttributeError < ArgumentError
|
|
7
|
+
def message
|
|
8
|
+
[
|
|
9
|
+
"A minimum value can not be determined when the validation uses :greater_than on a :decimal or :float column type.",
|
|
10
|
+
"Please alter the validation to use :greater_than_or_equal_to, or provide a value for this attribute explicitly with the :min option on input()."
|
|
11
|
+
].join("\n")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class IndeterminableMaximumAttributeError < ArgumentError
|
|
16
|
+
def message
|
|
17
|
+
[
|
|
18
|
+
"A maximum value can not be determined when the validation uses :less_than on a :decimal or :float column type.",
|
|
19
|
+
"Please alter the validation to use :less_than_or_equal_to, or provide a value for this attribute explicitly with the :max option on input()."
|
|
20
|
+
].join("\n")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validations
|
|
25
|
+
@validations ||= if object && object.class.respond_to?(:validators_on)
|
|
26
|
+
object.class.validators_on(attributized_method_name).select do |validator|
|
|
27
|
+
validator_relevant?(validator)
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validator_relevant?(validator)
|
|
35
|
+
return true unless validator.options.key?(:if) || validator.options.key?(:unless)
|
|
36
|
+
conditional = validator.options.key?(:if) ? validator.options[:if] : validator.options[:unless]
|
|
37
|
+
|
|
38
|
+
result = if conditional.respond_to?(:call) && conditional.arity > 0
|
|
39
|
+
conditional.call(object)
|
|
40
|
+
elsif conditional.respond_to?(:call) && conditional.arity == 0
|
|
41
|
+
object.instance_exec(&conditional)
|
|
42
|
+
elsif conditional.is_a?(::Symbol) && object.respond_to?(conditional)
|
|
43
|
+
object.send(conditional)
|
|
44
|
+
else
|
|
45
|
+
conditional
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
result = validator.options.key?(:unless) ? !result : !!result
|
|
49
|
+
not_required_through_negated_validation! if !result && [:presence, :inclusion, :length].include?(validator.kind)
|
|
50
|
+
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validation_limit
|
|
55
|
+
validation = validations? && validations.find do |validation|
|
|
56
|
+
validation.kind == :length
|
|
57
|
+
end
|
|
58
|
+
if validation
|
|
59
|
+
validation.options[:maximum] || (validation.options[:within].present? ? validation.options[:within].max : nil)
|
|
60
|
+
else
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Prefer :greater_than_or_equal_to over :greater_than, for no particular reason.
|
|
66
|
+
def validation_min
|
|
67
|
+
validation = validations? && validations.find do |validation|
|
|
68
|
+
validation.kind == :numericality
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if validation
|
|
72
|
+
# We can't determine an appropriate value for :greater_than with a float/decimal column
|
|
73
|
+
raise IndeterminableMinimumAttributeError if validation.options[:greater_than] && column? && [:float, :decimal].include?(column.type)
|
|
74
|
+
|
|
75
|
+
if validation.options[:greater_than_or_equal_to]
|
|
76
|
+
return (validation.options[:greater_than_or_equal_to].call(object)) if validation.options[:greater_than_or_equal_to].kind_of?(Proc)
|
|
77
|
+
return (validation.options[:greater_than_or_equal_to])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if validation.options[:greater_than]
|
|
81
|
+
return (validation.options[:greater_than].call(object) + 1) if validation.options[:greater_than].kind_of?(Proc)
|
|
82
|
+
return (validation.options[:greater_than] + 1)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Prefer :less_than_or_equal_to over :less_than, for no particular reason.
|
|
88
|
+
def validation_max
|
|
89
|
+
validation = validations? && validations.find do |validation|
|
|
90
|
+
validation.kind == :numericality
|
|
91
|
+
end
|
|
92
|
+
if validation
|
|
93
|
+
# We can't determine an appropriate value for :greater_than with a float/decimal column
|
|
94
|
+
raise IndeterminableMaximumAttributeError if validation.options[:less_than] && column? && [:float, :decimal].include?(column.type)
|
|
95
|
+
|
|
96
|
+
if validation.options[:less_than_or_equal_to]
|
|
97
|
+
return (validation.options[:less_than_or_equal_to].call(object)) if validation.options[:less_than_or_equal_to].kind_of?(Proc)
|
|
98
|
+
return (validation.options[:less_than_or_equal_to])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if validation.options[:less_than]
|
|
102
|
+
return ((validation.options[:less_than].call(object)) - 1) if validation.options[:less_than].kind_of?(Proc)
|
|
103
|
+
return (validation.options[:less_than] - 1)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def validation_step
|
|
109
|
+
validation = validations? && validations.find do |validation|
|
|
110
|
+
validation.kind == :numericality
|
|
111
|
+
end
|
|
112
|
+
if validation
|
|
113
|
+
validation.options[:step] || (1 if validation_integer_only?)
|
|
114
|
+
else
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def validation_integer_only?
|
|
120
|
+
validation = validations? && validations.find do |validation|
|
|
121
|
+
validation.kind == :numericality
|
|
122
|
+
end
|
|
123
|
+
if validation
|
|
124
|
+
validation.options[:only_integer]
|
|
125
|
+
else
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def validations?
|
|
131
|
+
validations != nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def required?
|
|
135
|
+
return false if options[:required] == false
|
|
136
|
+
return true if options[:required] == true
|
|
137
|
+
return false if not_required_through_negated_validation?
|
|
138
|
+
if validations?
|
|
139
|
+
validations.any? { |validator|
|
|
140
|
+
if validator.options.key?(:on)
|
|
141
|
+
validator_on = Array(validator.options[:on])
|
|
142
|
+
next false if (validator_on.exclude?(:save)) && ((object.new_record? && validator_on.exclude?(:create)) || (!object.new_record? && validator_on.exclude?(:update)))
|
|
143
|
+
end
|
|
144
|
+
case validator.kind
|
|
145
|
+
when :presence
|
|
146
|
+
true
|
|
147
|
+
when :inclusion
|
|
148
|
+
validator.options[:allow_blank] != true
|
|
149
|
+
when :length
|
|
150
|
+
validator.options[:allow_blank] != true &&
|
|
151
|
+
validator.options[:minimum].to_i > 0 ||
|
|
152
|
+
validator.options[:within].try(:first).to_i > 0
|
|
153
|
+
else
|
|
154
|
+
false
|
|
155
|
+
end
|
|
156
|
+
}
|
|
157
|
+
else
|
|
158
|
+
return responds_to_global_required? && !!builder.all_fields_required_by_default
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def required_attribute?
|
|
163
|
+
required? && builder.use_required_attribute
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def not_required_through_negated_validation?
|
|
167
|
+
@not_required_through_negated_validation
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def not_required_through_negated_validation!
|
|
171
|
+
@not_required_through_negated_validation = true
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def responds_to_global_required?
|
|
175
|
+
true
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def optional?
|
|
179
|
+
!required?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def autofocus?
|
|
183
|
+
opt_autofocus = options[:input_html] && options[:input_html][:autofocus]
|
|
184
|
+
|
|
185
|
+
!!opt_autofocus
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def column_limit
|
|
189
|
+
column.limit if column? && column.respond_to?(:limit)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def limit
|
|
193
|
+
validation_limit || column_limit
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def readonly?
|
|
197
|
+
readonly_from_options? || readonly_attribute?
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def readonly_attribute?
|
|
201
|
+
object_class = self.object.class
|
|
202
|
+
object_class.respond_to?(:readonly_attributes) &&
|
|
203
|
+
self.object.persisted? &&
|
|
204
|
+
column.respond_to?(:name) &&
|
|
205
|
+
object_class.readonly_attributes.include?(column.name.to_s)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def readonly_from_options?
|
|
209
|
+
options[:input_html] && options[:input_html][:readonly]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|