formtastic 5.0.0 → 6.0.0
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 +4 -4
- data/.gitattributes +0 -1
- data/.github/workflows/integration.yaml +36 -0
- data/.github/workflows/test.yml +26 -20
- data/.gitignore +2 -1
- data/Appraisals +23 -0
- data/CONTRIBUTING.md +27 -0
- data/Gemfile +5 -0
- data/MIT-LICENSE +1 -1
- data/README.md +14 -46
- data/RELEASE_PROCESS +0 -1
- data/Rakefile +1 -1
- data/formtastic.gemspec +17 -10
- data/gemfiles/rails_72.gemfile +9 -0
- data/gemfiles/rails_80.gemfile +9 -0
- data/gemfiles/rails_81.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +9 -0
- data/lib/formtastic/actions/input_action.rb +1 -1
- data/lib/formtastic/actions/link_action.rb +1 -1
- data/lib/formtastic/form_builder.rb +27 -27
- data/lib/formtastic/helpers/action_helper.rb +0 -1
- data/lib/formtastic/helpers/actions_helper.rb +1 -1
- data/lib/formtastic/helpers/errors_helper.rb +71 -14
- data/lib/formtastic/helpers/fieldset_wrapper.rb +1 -1
- data/lib/formtastic/helpers/form_helper.rb +3 -3
- data/lib/formtastic/helpers/input_helper.rb +5 -3
- data/lib/formtastic/i18n.rb +0 -1
- data/lib/formtastic/inputs/base/aria.rb +26 -0
- data/lib/formtastic/inputs/base/collections.rb +1 -1
- data/lib/formtastic/inputs/base/errors.rb +1 -1
- data/lib/formtastic/inputs/base/html.rb +1 -1
- data/lib/formtastic/inputs/base/labelling.rb +1 -1
- data/lib/formtastic/inputs/base/validations.rb +32 -8
- data/lib/formtastic/inputs/base.rb +7 -5
- data/lib/formtastic/inputs/country_input.rb +9 -6
- data/lib/formtastic/inputs/datalist_input.rb +2 -2
- data/lib/formtastic/inputs/number_input.rb +1 -1
- data/lib/formtastic/inputs/range_input.rb +1 -1
- data/lib/formtastic/inputs/time_zone_input.rb +1 -1
- data/lib/formtastic/version.rb +1 -1
- data/lib/formtastic.rb +6 -2
- data/lib/generators/formtastic/form/form_generator.rb +2 -3
- data/lib/generators/formtastic/install/install_generator.rb +0 -1
- data/lib/generators/formtastic/stylesheets/stylesheets_generator.rb +15 -0
- data/{app/assets/stylesheets → lib/generators/templates}/formtastic.css +4 -4
- data/lib/generators/templates/formtastic.rb +7 -1
- data/script/integration-template.rb +14 -11
- data/script/integration.sh +17 -4
- data/spec/action_class_finder_spec.rb +0 -1
- data/spec/actions/button_action_spec.rb +0 -1
- data/spec/actions/generic_action_spec.rb +2 -3
- data/spec/actions/input_action_spec.rb +0 -1
- data/spec/actions/link_action_spec.rb +0 -1
- data/spec/builder/custom_builder_spec.rb +0 -1
- data/spec/builder/error_proc_spec.rb +0 -1
- data/spec/builder/semantic_fields_for_spec.rb +72 -2
- data/spec/fast_spec_helper.rb +0 -1
- data/spec/generators/formtastic/stylesheets/stylesheets_generator_spec.rb +22 -0
- data/spec/helpers/actions_helper_spec.rb +0 -1
- data/spec/helpers/form_helper_spec.rb +0 -1
- data/spec/helpers/input_helper_spec.rb +12 -1
- data/spec/helpers/inputs_helper_spec.rb +1 -2
- data/spec/helpers/reflection_helper_spec.rb +0 -1
- data/spec/helpers/semantic_errors_helper_spec.rb +114 -7
- data/spec/i18n_spec.rb +1 -2
- data/spec/input_class_finder_spec.rb +0 -1
- data/spec/inputs/base/collections_spec.rb +41 -0
- data/spec/inputs/boolean_input_spec.rb +0 -1
- data/spec/inputs/check_boxes_input_spec.rb +1 -2
- data/spec/inputs/color_input_spec.rb +0 -1
- data/spec/inputs/country_input_spec.rb +5 -6
- data/spec/inputs/custom_input_spec.rb +0 -1
- data/spec/inputs/datalist_input_spec.rb +0 -1
- data/spec/inputs/date_picker_input_spec.rb +0 -1
- data/spec/inputs/date_select_input_spec.rb +1 -2
- data/spec/inputs/datetime_picker_input_spec.rb +0 -1
- data/spec/inputs/datetime_select_input_spec.rb +1 -2
- data/spec/inputs/email_input_spec.rb +0 -1
- data/spec/inputs/file_input_spec.rb +0 -1
- data/spec/inputs/hidden_input_spec.rb +0 -1
- data/spec/inputs/include_blank_spec.rb +0 -1
- data/spec/inputs/label_spec.rb +32 -1
- data/spec/inputs/number_input_spec.rb +0 -1
- data/spec/inputs/password_input_spec.rb +1 -2
- data/spec/inputs/phone_input_spec.rb +0 -1
- data/spec/inputs/placeholder_spec.rb +0 -1
- data/spec/inputs/radio_input_spec.rb +0 -1
- data/spec/inputs/range_input_spec.rb +0 -1
- data/spec/inputs/readonly_spec.rb +0 -1
- data/spec/inputs/search_input_spec.rb +0 -1
- data/spec/inputs/select_input_spec.rb +0 -1
- data/spec/inputs/string_input_spec.rb +67 -2
- data/spec/inputs/text_input_spec.rb +0 -1
- data/spec/inputs/time_picker_input_spec.rb +0 -1
- data/spec/inputs/time_select_input_spec.rb +1 -2
- data/spec/inputs/time_zone_input_spec.rb +0 -1
- data/spec/inputs/url_input_spec.rb +0 -1
- data/spec/inputs/with_options_spec.rb +0 -1
- data/spec/localizer_spec.rb +0 -1
- data/spec/namespaced_class_finder_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/support/custom_macros.rb +11 -3
- data/spec/support/specialized_class_finder_shared_example.rb +0 -1
- data/spec/support/test_environment.rb +2 -2
- metadata +53 -41
- data/CHANGELOG.md +0 -61
- data/Gemfile.lock +0 -140
- data/app/assets/stylesheets/formtastic_ie6.css +0 -33
- data/app/assets/stylesheets/formtastic_ie7.css +0 -23
- data/gemfiles/rails_6.0/Gemfile +0 -5
- data/gemfiles/rails_6.1/Gemfile +0 -5
- data/gemfiles/rails_7.0/Gemfile +0 -5
- data/gemfiles/rails_7.1/Gemfile +0 -5
- data/gemfiles/rails_edge/Gemfile +0 -13
- data/lib/formtastic/engine.rb +0 -14
|
@@ -16,6 +16,10 @@ module Formtastic
|
|
|
16
16
|
# A hash can be used as the last set of arguments to pass HTML attributes to the `<ul>`
|
|
17
17
|
# wrapper.
|
|
18
18
|
#
|
|
19
|
+
# # in config/initializers/formtastic.rb
|
|
20
|
+
# Setting `Formtastic::FormBuilder.semantic_errors_link_to_inputs = true`
|
|
21
|
+
# will render attribute errors as links to the corresponding errored inputs.
|
|
22
|
+
#
|
|
19
23
|
# @example A list of errors on the base model
|
|
20
24
|
# <%= semantic_form_for ... %>
|
|
21
25
|
# <%= f.semantic_errors %>
|
|
@@ -41,24 +45,37 @@ module Formtastic
|
|
|
41
45
|
# <% end %>
|
|
42
46
|
def semantic_errors(*args)
|
|
43
47
|
html_options = args.extract_options!
|
|
44
|
-
args = args - [:base]
|
|
45
|
-
full_errors = args.inject([]) do |array, method|
|
|
46
|
-
attribute = localized_string(method, method.to_sym, :label) || humanized_attribute_name(method)
|
|
47
|
-
errors = Array(@object.errors[method.to_sym]).to_sentence
|
|
48
|
-
errors.present? ? array << [attribute, errors].join(" ") : array ||= []
|
|
49
|
-
end
|
|
50
|
-
full_errors << @object.errors[:base]
|
|
51
|
-
full_errors.flatten!
|
|
52
|
-
full_errors.compact!
|
|
53
|
-
return nil if full_errors.blank?
|
|
54
48
|
html_options[:class] ||= "errors"
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
|
|
50
|
+
if Formtastic::FormBuilder.semantic_errors_link_to_inputs
|
|
51
|
+
attribute_error_hash = semantic_error_hash_from_attributes(args)
|
|
52
|
+
return nil if @object.errors[:base].blank? && attribute_error_hash.blank?
|
|
53
|
+
|
|
54
|
+
template.content_tag(:ul, html_options) do
|
|
55
|
+
(
|
|
56
|
+
@object.errors[:base].map { |base_error| template.content_tag(:li, base_error) } <<
|
|
57
|
+
attribute_error_hash.map { |attribute, error_message|
|
|
58
|
+
template.content_tag(:li) do
|
|
59
|
+
template.content_tag(:a, href: "##{object_name}_#{attribute}") do
|
|
60
|
+
error_message
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
}
|
|
64
|
+
).join.html_safe
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
full_errors = @object.errors[:base]
|
|
68
|
+
full_errors += semantic_error_list_from_attributes(args)
|
|
69
|
+
return nil if full_errors.blank?
|
|
70
|
+
|
|
71
|
+
template.content_tag(:ul, html_options) do
|
|
72
|
+
full_errors.map { |error| template.content_tag(:li, error) }.join.html_safe
|
|
73
|
+
end
|
|
57
74
|
end
|
|
58
75
|
end
|
|
59
|
-
|
|
76
|
+
|
|
60
77
|
protected
|
|
61
|
-
|
|
78
|
+
|
|
62
79
|
def error_keys(method, options)
|
|
63
80
|
@methods_for_error ||= {}
|
|
64
81
|
@methods_for_error[method] ||= begin
|
|
@@ -77,6 +94,46 @@ module Formtastic
|
|
|
77
94
|
def render_inline_errors?
|
|
78
95
|
@object && @object.respond_to?(:errors) && Formtastic::FormBuilder::INLINE_ERROR_TYPES.include?(inline_errors)
|
|
79
96
|
end
|
|
97
|
+
|
|
98
|
+
def semantic_error_list_from_attributes(*args)
|
|
99
|
+
attribute_errors = []
|
|
100
|
+
args = args.flatten
|
|
101
|
+
args.each do |attribute|
|
|
102
|
+
next if attribute == :base
|
|
103
|
+
|
|
104
|
+
full_message = error_message_for_attribute(attribute)
|
|
105
|
+
|
|
106
|
+
attribute_errors << full_message unless full_message.blank?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
attribute_errors
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# returns { 'attribute': 'error_message_for_attribute' }
|
|
113
|
+
def semantic_error_hash_from_attributes(*args)
|
|
114
|
+
attribute_error_hash = {}
|
|
115
|
+
args = args.flatten
|
|
116
|
+
args.each do |attribute|
|
|
117
|
+
next if attribute == :base
|
|
118
|
+
|
|
119
|
+
full_message = error_message_for_attribute(attribute)
|
|
120
|
+
|
|
121
|
+
attribute_error_hash[attribute] = full_message unless full_message.blank?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
attribute_error_hash
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Returns "Attribute error_message_sentence" localized, humanized
|
|
128
|
+
def error_message_for_attribute(attribute)
|
|
129
|
+
attribute_string = localized_string(attribute, attribute.to_sym, :label) || humanized_attribute_name(attribute)
|
|
130
|
+
error_message = @object.errors[attribute.to_sym]&.to_sentence
|
|
131
|
+
|
|
132
|
+
return nil if error_message.blank?
|
|
133
|
+
|
|
134
|
+
full_message = [attribute_string, error_message].join(" ")
|
|
135
|
+
full_message
|
|
136
|
+
end
|
|
80
137
|
end
|
|
81
138
|
end
|
|
82
139
|
end
|
|
@@ -65,7 +65,7 @@ module Formtastic
|
|
|
65
65
|
|
|
66
66
|
# Could be symbol for the association, or a model (or an array of either, I think? TODO)
|
|
67
67
|
child = parent[:for]
|
|
68
|
-
# Pull a
|
|
68
|
+
# Pull a symbol or model out of Array (TODO: check if there's an Array)
|
|
69
69
|
child = child.first if child.respond_to?(:first)
|
|
70
70
|
# If it's an object, get a symbol from the class name
|
|
71
71
|
child = child.class.name.underscore.to_sym unless child.is_a?(Symbol)
|
|
@@ -59,7 +59,7 @@ module Formtastic
|
|
|
59
59
|
@@default_form_class = 'formtastic'
|
|
60
60
|
mattr_accessor :default_form_class
|
|
61
61
|
|
|
62
|
-
# Allows to set a custom proc to handle the class
|
|
62
|
+
# Allows to set a custom proc to handle the class inferred from the model's name. By default it
|
|
63
63
|
# will infer the name from the class name (eg. Post will be "post").
|
|
64
64
|
@@default_form_model_class_proc = proc { |model_class_name| model_class_name }
|
|
65
65
|
mattr_accessor :default_form_model_class_proc
|
|
@@ -84,8 +84,8 @@ module Formtastic
|
|
|
84
84
|
# Most of the examples below have been adapted from the examples found in the Rails `form_for`
|
|
85
85
|
# documentation.
|
|
86
86
|
#
|
|
87
|
-
# @see
|
|
88
|
-
# @see
|
|
87
|
+
# @see https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html Rails' FormHelper documentation (`form_for`, etc)
|
|
88
|
+
# @see https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html Rails' FormBuilder documentation (`text_field`, etc)
|
|
89
89
|
# @see FormHelper The overview of the FormBuilder module
|
|
90
90
|
#
|
|
91
91
|
# @example Resource-oriented form generation
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
module Formtastic
|
|
4
3
|
module Helpers
|
|
@@ -177,6 +176,9 @@ module Formtastic
|
|
|
177
176
|
# @example Changing or adding to HTML attributes in the main `<input>` or `<select>` tag
|
|
178
177
|
# <%= f.input :title, :input_html => { :onchange => "somethingAwesome();", :class => 'awesome' } %>
|
|
179
178
|
#
|
|
179
|
+
# @example Changing or adding to HTML attributes in the main `<label>` tag
|
|
180
|
+
# <%= f.input :title, :label_html => { :data => { :tooltip => 'Great Tooltip' } } %>
|
|
181
|
+
#
|
|
180
182
|
# @example Changing or adding to HTML attributes in the wrapper `<li>` tag
|
|
181
183
|
# <%= f.input :title, :wrapper_html => { :class => "important-input" } %>
|
|
182
184
|
#
|
|
@@ -278,7 +280,7 @@ module Formtastic
|
|
|
278
280
|
return :number
|
|
279
281
|
when :float, :decimal
|
|
280
282
|
return :number
|
|
281
|
-
when :datetime, :timestamp
|
|
283
|
+
when :datetime, :timestamp, :timestamptz
|
|
282
284
|
return :datetime_select
|
|
283
285
|
when :time
|
|
284
286
|
return :time_select
|
|
@@ -290,7 +292,7 @@ module Formtastic
|
|
|
290
292
|
return :string
|
|
291
293
|
end
|
|
292
294
|
|
|
293
|
-
# Try look for hints in options hash. Quite common
|
|
295
|
+
# Try look for hints in options hash. Quite common scenario: Enum keys stored as string in the database.
|
|
294
296
|
return :select if column.type == :string && options.key?(:collection)
|
|
295
297
|
# Try 3: Assume the input name will be the same as the column type (e.g. string_input).
|
|
296
298
|
return column.type
|
data/lib/formtastic/i18n.rb
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Formtastic
|
|
3
|
+
module Inputs
|
|
4
|
+
module Base
|
|
5
|
+
module Aria
|
|
6
|
+
|
|
7
|
+
def error_aria_attributes
|
|
8
|
+
return {} unless builder.semantic_errors_link_to_inputs
|
|
9
|
+
return {} unless errors?
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
'aria-describedby': describedby,
|
|
13
|
+
'aria-invalid': options.dig(:input_html, :'aria-invalid') || 'true'
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def describedby
|
|
18
|
+
describedby = options.dig(:input_html, :'aria-describedby') || ''
|
|
19
|
+
describedby += ' ' unless describedby.empty?
|
|
20
|
+
describedby += "#{method}_error"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -53,7 +53,7 @@ module Formtastic
|
|
|
53
53
|
|
|
54
54
|
# Return if we have an Array of strings, integers or arrays
|
|
55
55
|
return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
|
|
56
|
-
([Array, String].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
|
|
56
|
+
([Array, String, Symbol].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
|
|
57
57
|
!(options.include?(:member_label) || options.include?(:member_value))
|
|
58
58
|
|
|
59
59
|
raw_collection.map { |o| [send_or_call(label_method, o), send_or_call(value_method, o)] }
|
|
@@ -10,7 +10,7 @@ module Formtastic
|
|
|
10
10
|
|
|
11
11
|
def error_sentence_html
|
|
12
12
|
error_class = builder.default_inline_error_class
|
|
13
|
-
template.content_tag(:p, errors.to_sentence, :class => error_class)
|
|
13
|
+
template.content_tag(:p, errors.to_sentence, id: "#{method}_error", :class => error_class)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def error_list_html
|
|
@@ -57,7 +57,8 @@ module Formtastic
|
|
|
57
57
|
validation.kind == :length
|
|
58
58
|
end
|
|
59
59
|
if validation
|
|
60
|
-
validation.options[:maximum] ||
|
|
60
|
+
option_value(validation.options[:maximum], object) ||
|
|
61
|
+
(validation.options[:within].present? ? option_value(validation.options[:within], object).max : nil)
|
|
61
62
|
else
|
|
62
63
|
nil
|
|
63
64
|
end
|
|
@@ -145,8 +146,8 @@ module Formtastic
|
|
|
145
146
|
validator.options[:allow_blank] != true
|
|
146
147
|
when :length
|
|
147
148
|
validator.options[:allow_blank] != true &&
|
|
148
|
-
validator.options[:minimum].to_i > 0 ||
|
|
149
|
-
validator.options[:within].try(:first).to_i > 0
|
|
149
|
+
option_value(validator.options[:minimum], object).to_i > 0 ||
|
|
150
|
+
option_value(validator.options[:within], object).try(:first).to_i > 0
|
|
150
151
|
else
|
|
151
152
|
false
|
|
152
153
|
end
|
|
@@ -183,7 +184,22 @@ module Formtastic
|
|
|
183
184
|
end
|
|
184
185
|
|
|
185
186
|
def column_limit
|
|
186
|
-
|
|
187
|
+
return unless column?
|
|
188
|
+
return unless column.respond_to?(:limit)
|
|
189
|
+
|
|
190
|
+
limit = column.limit # already in characters for string, text, etc
|
|
191
|
+
|
|
192
|
+
if column.type == :integer && column.limit.is_a?(Integer)
|
|
193
|
+
return {
|
|
194
|
+
1 => 3, # 8 bit
|
|
195
|
+
2 => 5, # 16 bit
|
|
196
|
+
3 => 7, # 24 bit
|
|
197
|
+
4 => 10, # 32 bit
|
|
198
|
+
8 => 19, # 64 bit
|
|
199
|
+
}[limit] || nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
return limit
|
|
187
203
|
end
|
|
188
204
|
|
|
189
205
|
def limit
|
|
@@ -208,16 +224,24 @@ module Formtastic
|
|
|
208
224
|
|
|
209
225
|
private
|
|
210
226
|
|
|
211
|
-
#
|
|
212
|
-
# https://github.com/rails/rails/blob/
|
|
227
|
+
# Implements `ActiveModel::Validations::ResolveValue`, introduced by Rails 7.1.
|
|
228
|
+
# https://github.com/rails/rails/blob/v7.1.0/activemodel/lib/active_model/validations/resolve_value.rb
|
|
213
229
|
def option_value(option, object)
|
|
214
230
|
case option
|
|
215
231
|
when Symbol
|
|
216
232
|
object.send(option)
|
|
217
233
|
when Proc
|
|
218
|
-
option.
|
|
234
|
+
if option.arity == 0
|
|
235
|
+
option.call
|
|
236
|
+
else
|
|
237
|
+
option.call(object)
|
|
238
|
+
end
|
|
219
239
|
else
|
|
220
|
-
option
|
|
240
|
+
if option.respond_to?(:call)
|
|
241
|
+
option.call(object)
|
|
242
|
+
else
|
|
243
|
+
option
|
|
244
|
+
end
|
|
221
245
|
end
|
|
222
246
|
end
|
|
223
247
|
end
|
|
@@ -19,22 +19,22 @@ module Formtastic
|
|
|
19
19
|
warn_deprecated_option!(:member_value, member_deprecation_message)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
#
|
|
22
|
+
# Useful for deprecating options.
|
|
23
23
|
def warn_and_correct_option!(old_option_name, new_option_name)
|
|
24
24
|
if options.key?(old_option_name)
|
|
25
|
-
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",
|
|
25
|
+
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_locations(6))
|
|
26
26
|
options[new_option_name] = options.delete(old_option_name)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# Useful for deprecating options.
|
|
31
31
|
def warn_deprecated_option!(old_option_name, instructions)
|
|
32
32
|
if options.key?(old_option_name)
|
|
33
|
-
Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}`. :#{old_option_name} will be removed in the next version",
|
|
33
|
+
Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}`. :#{old_option_name} will be removed in the next version", caller_locations(6))
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Useful for raising an error on previously supported option.
|
|
38
38
|
def removed_option!(old_option_name)
|
|
39
39
|
raise ArgumentError, ":#{old_option_name} is no longer available" if options.key?(old_option_name)
|
|
40
40
|
end
|
|
@@ -59,6 +59,7 @@ module Formtastic
|
|
|
59
59
|
autoload :Timeish
|
|
60
60
|
autoload :Validations
|
|
61
61
|
autoload :Wrapping
|
|
62
|
+
autoload :Aria
|
|
62
63
|
|
|
63
64
|
include Html
|
|
64
65
|
include Options
|
|
@@ -71,6 +72,7 @@ module Formtastic
|
|
|
71
72
|
include Associations
|
|
72
73
|
include Labelling
|
|
73
74
|
include Wrapping
|
|
75
|
+
include Aria
|
|
74
76
|
|
|
75
77
|
end
|
|
76
78
|
end
|
|
@@ -5,13 +5,10 @@ module Formtastic
|
|
|
5
5
|
# Rails doesn't come with a `country_select` helper by default any more, so you'll need to do
|
|
6
6
|
# one of the following:
|
|
7
7
|
#
|
|
8
|
-
# * install the [country_select](https://github.com/
|
|
8
|
+
# * install the [country_select](https://github.com/countries/country_select) gem
|
|
9
9
|
# * install any other country_select plugin that behaves in a similar way
|
|
10
10
|
# * roll your own `country_select` helper with the same args and options as the Rails one
|
|
11
11
|
#
|
|
12
|
-
# Formtastic supports both 1.x and 2.x of stefanpenner/country_select, but if you're upgrading
|
|
13
|
-
# from 1.x, they behave quite differently, so please see their [upgrade instructions](https://github.com/stefanpenner/country_select/blob/master/UPGRADING.md).
|
|
14
|
-
#
|
|
15
12
|
# By default, Formtastic includes a handful of English-speaking countries as "priority
|
|
16
13
|
# countries", which can be set in the `priority_countries` configuration array in the
|
|
17
14
|
# formtastic.rb initializer to suit your market and user base (see README for more info on
|
|
@@ -71,12 +68,18 @@ module Formtastic
|
|
|
71
68
|
CountrySelectPluginMissing = Class.new(StandardError)
|
|
72
69
|
|
|
73
70
|
def to_html
|
|
74
|
-
raise CountrySelectPluginMissing, "To use the :country input, please install a country_select plugin, like this one: https://github.com/
|
|
71
|
+
raise CountrySelectPluginMissing, "To use the :country input, please install a country_select plugin, like this one: https://github.com/countries/country_select" unless builder.respond_to?(:country_select)
|
|
75
72
|
input_wrapping do
|
|
76
73
|
label_html <<
|
|
77
|
-
builder.country_select(method,
|
|
74
|
+
builder.country_select(method, input_options_including_priorities, input_html_options)
|
|
78
75
|
end
|
|
79
76
|
end
|
|
77
|
+
|
|
78
|
+
def input_options_including_priorities
|
|
79
|
+
return input_options unless priority_countries
|
|
80
|
+
|
|
81
|
+
input_options.merge(:priority_countries => priority_countries)
|
|
82
|
+
end
|
|
80
83
|
|
|
81
84
|
def priority_countries
|
|
82
85
|
options[:priority_countries] || builder.priority_countries
|
|
@@ -4,9 +4,9 @@ module Formtastic
|
|
|
4
4
|
# Outputs a label and a text field, along with a datalist tag
|
|
5
5
|
# datalist tag provides a list of options which drives a simple autocomplete
|
|
6
6
|
# on the text field. This is a HTML5 feature, more info can be found at
|
|
7
|
-
# {https://developer.mozilla.org/en/docs/Web/HTML/
|
|
7
|
+
# {https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/datalist <datalist> at MDN}
|
|
8
8
|
# This input accepts a :collection option which takes data in all the usual formats accepted by
|
|
9
|
-
# {
|
|
9
|
+
# {https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select options_for_select}
|
|
10
10
|
#
|
|
11
11
|
# @example Input is used as follows
|
|
12
12
|
# f.input :fav_book, :as => :datalist, :collection => Book.pluck(:name)
|
|
@@ -67,7 +67,7 @@ module Formtastic
|
|
|
67
67
|
# <%= f.input :shoe_size, :as => :number, :input_html => { :in => 3..15, :step => 1 } %>
|
|
68
68
|
#
|
|
69
69
|
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
70
|
-
# @see
|
|
70
|
+
# @see https://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
|
|
71
71
|
class NumberInput
|
|
72
72
|
include Base
|
|
73
73
|
include Base::Numeric
|
|
@@ -66,7 +66,7 @@ module Formtastic
|
|
|
66
66
|
# <%= f.input :shoe_size, :as => :range, :input_html => { :in => 3..15, :step => 1 } %>
|
|
67
67
|
#
|
|
68
68
|
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
69
|
-
# @see
|
|
69
|
+
# @see https://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
|
|
70
70
|
#
|
|
71
71
|
class RangeInput
|
|
72
72
|
include Base
|
|
@@ -39,7 +39,7 @@ module Formtastic
|
|
|
39
39
|
# Formtastic::FormBuilder.priority_time_zones = [timezone1, timezone2]
|
|
40
40
|
# ```
|
|
41
41
|
#
|
|
42
|
-
# See
|
|
42
|
+
# See https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/time_zone_select for more information.
|
|
43
43
|
#
|
|
44
44
|
class TimeZoneInput
|
|
45
45
|
include Base
|
data/lib/formtastic/version.rb
CHANGED
data/lib/formtastic.rb
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
|
-
require 'formtastic/engine' if defined?(::Rails)
|
|
4
2
|
|
|
5
3
|
module Formtastic
|
|
6
4
|
extend ActiveSupport::Autoload
|
|
@@ -42,3 +40,9 @@ module Formtastic
|
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
end
|
|
43
|
+
|
|
44
|
+
if defined?(::Rails)
|
|
45
|
+
ActiveSupport.on_load(:action_view) do
|
|
46
|
+
include Formtastic::Helpers::FormHelper
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
module Formtastic
|
|
4
3
|
# Generates a Formtastic form partial based on an existing model. It will not overwrite existing
|
|
@@ -33,7 +32,7 @@ module Formtastic
|
|
|
33
32
|
class_option :copy, :type => :boolean, :default => false, :group => :formtastic,
|
|
34
33
|
:desc => 'Copy the generated code the clipboard instead of generating a partial file."'
|
|
35
34
|
|
|
36
|
-
class_option :controller, :type => :string, :default =>
|
|
35
|
+
class_option :controller, :type => :string, :default => nil, :group => :formtastic,
|
|
37
36
|
:desc => 'Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts"'
|
|
38
37
|
|
|
39
38
|
def create_or_show
|
|
@@ -43,7 +42,7 @@ module Formtastic
|
|
|
43
42
|
|
|
44
43
|
if options[:copy]
|
|
45
44
|
template = File.read("#{self.class.source_root}/_form.html.#{engine}")
|
|
46
|
-
erb = ERB.new(template,
|
|
45
|
+
erb = ERB.new(template, trim_mode: '-')
|
|
47
46
|
generated_code = erb.result(binding).strip rescue nil
|
|
48
47
|
puts "The following code has been copied to the clipboard, just paste it in your views:" if save_to_clipboard(generated_code)
|
|
49
48
|
puts generated_code || "Error: Nothing generated. Does the model exist?"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
# Copies a stylesheet into to app/assets/stylesheets/formtastic.css
|
|
3
|
+
#
|
|
4
|
+
# @example
|
|
5
|
+
# !!!shell
|
|
6
|
+
# $ rails generate formtastic:stylesheets
|
|
7
|
+
class StylesheetsGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("../../../templates", __FILE__)
|
|
9
|
+
|
|
10
|
+
desc "Copies Formtastic example stylesheet into your app"
|
|
11
|
+
def copy_files
|
|
12
|
+
copy_file "formtastic.css", "app/assets/stylesheets/formtastic.css"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -4,8 +4,8 @@ It's *strongly* suggested that you don't modify this file. Instead, load a new
|
|
|
4
4
|
this one in your layouts (eg formtastic_changes.css) and override the styles to suit your needs.
|
|
5
5
|
This will allow you to update formtastic.css with new releases without clobbering your own changes.
|
|
6
6
|
|
|
7
|
-
This stylesheet forms part of the Formtastic Rails
|
|
8
|
-
(c)
|
|
7
|
+
This stylesheet forms part of the Formtastic Rails gem
|
|
8
|
+
(c) Justin French
|
|
9
9
|
|
|
10
10
|
--------------------------------------------------------------------------------------------------*/
|
|
11
11
|
|
|
@@ -84,13 +84,13 @@ This stylesheet forms part of the Formtastic Rails Plugin
|
|
|
84
84
|
|
|
85
85
|
/* BUTTONS & ACTIONS
|
|
86
86
|
--------------------------------------------------------------------------------------------------*/
|
|
87
|
-
.formtastic .buttons,
|
|
87
|
+
.formtastic .buttons,
|
|
88
88
|
.formtastic .actions {
|
|
89
89
|
overflow:hidden; /* clear containing floats */
|
|
90
90
|
padding-left:25%;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
.formtastic .button,
|
|
93
|
+
.formtastic .button,
|
|
94
94
|
.formtastic .action {
|
|
95
95
|
float:left;
|
|
96
96
|
padding-right:0.5em;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
# Set the default text field size when input is a string. Default is nil.
|
|
@@ -110,3 +109,10 @@
|
|
|
110
109
|
|
|
111
110
|
# Which columns to skip when automatically rendering a form without any fields specified.
|
|
112
111
|
# Formtastic::FormBuilder.skipped_columns = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
|
|
112
|
+
|
|
113
|
+
# You can opt-in to accessibility features for the `semantic_errors` helper by setting
|
|
114
|
+
# this to true. Doing so will render the attributes in the error summary list
|
|
115
|
+
# as `<li> <a>` links to the inputs that have errors. the inline error sentence's id is added to
|
|
116
|
+
# the errored input's aria-describedby. This ensures that the errored input is read out with
|
|
117
|
+
# the inline error sentence's error explanation, aria-invalid is set to true for errored inputs
|
|
118
|
+
# Formtastic::FormBuilder.semantic_errors_link_to_inputs = true
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
gem 'formtastic', path: '..'
|
|
3
|
-
gem 'bcrypt'
|
|
3
|
+
gem 'bcrypt'
|
|
4
4
|
gem 'rails-dom-testing', group: :test
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
gem 'minitest', '~> 5.27' # TODO: Remove this line when the Rails version used for integration tests will include rails/rails#56202
|
|
6
7
|
|
|
7
8
|
# to speed up bundle install, reuse the bundle path
|
|
8
9
|
def bundle_path
|
|
9
10
|
File.expand_path ENV.fetch('BUNDLE_PATH', 'vendor/bundle')
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
if Rails.version >= '
|
|
13
|
-
|
|
14
|
-
elsif Rails.version >= '
|
|
15
|
-
|
|
16
|
-
elsif Rails.version >= '
|
|
17
|
-
|
|
13
|
+
if Rails.version >= '8.1'
|
|
14
|
+
gsub_file 'Gemfile', /gem "rails".*/, %(gem "rails", "~> #{Rails.version}", github: "rails/rails", branch: "8-1-stable")
|
|
15
|
+
elsif Rails.version >= '8.0'
|
|
16
|
+
gsub_file 'Gemfile', /gem "rails".*/, %(gem "rails", "~> #{Rails.version}", github: "rails/rails", branch: "8-0-stable")
|
|
17
|
+
elsif Rails.version >= '7.2'
|
|
18
|
+
gsub_file 'Gemfile', /gem "rails".*/, %(gem "rails", "~> #{Rails.version}", github: "rails/rails", branch: "7-2-stable")
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
### Ensure Dummy App's Ruby version matches the current environments Ruby Version
|
|
@@ -26,8 +27,9 @@ if bundle_install?
|
|
|
26
27
|
previous_bundle_path = bundle_path
|
|
27
28
|
|
|
28
29
|
require "bundler"
|
|
29
|
-
Bundler.
|
|
30
|
-
system("bundle
|
|
30
|
+
Bundler.with_original_env do
|
|
31
|
+
system("bundle config set path '#{previous_bundle_path}'")
|
|
32
|
+
system("bundle install --jobs=3 --retry=3")
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -40,12 +42,13 @@ formtastic = -> do
|
|
|
40
42
|
generate(:scaffold, 'user name:string password:digest')
|
|
41
43
|
generate('formtastic:install')
|
|
42
44
|
generate('formtastic:form', 'user name password --force')
|
|
45
|
+
generate('formtastic:stylesheets')
|
|
43
46
|
|
|
44
47
|
rails_command('db:migrate')
|
|
48
|
+
rails_command('assets:precompile')
|
|
45
49
|
|
|
46
50
|
in_root do
|
|
47
51
|
inject_into_class 'app/models/user.rb', 'User', " has_secure_password\n"
|
|
48
|
-
inject_into_file 'app/assets/stylesheets/application.css', " *= require formtastic\n", before: ' *= require_self'
|
|
49
52
|
inject_into_class 'test/controllers/users_controller_test.rb', 'UsersControllerTest', <<-RUBY
|
|
50
53
|
|
|
51
54
|
test "should show form" do
|
data/script/integration.sh
CHANGED
|
@@ -4,16 +4,29 @@ set -e
|
|
|
4
4
|
set -o verbose
|
|
5
5
|
|
|
6
6
|
test_app=dummy
|
|
7
|
-
|
|
8
7
|
rm -rf ${test_app}
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
export RAILS_INTEGRATION_VERSION="8.1.2"
|
|
10
|
+
gem install rails -v ${RAILS_INTEGRATION_VERSION}
|
|
11
|
+
rails -v
|
|
12
|
+
|
|
13
|
+
rails new ${test_app} \
|
|
11
14
|
--template=$(dirname "$0")/integration-template.rb \
|
|
12
15
|
--skip-bootsnap \
|
|
16
|
+
--skip-brakeman \
|
|
17
|
+
--skip-bundler-audit \
|
|
18
|
+
--skip-ci \
|
|
19
|
+
--skip-git \
|
|
13
20
|
--skip-javascript \
|
|
21
|
+
--skip-jbuilder \
|
|
22
|
+
--skip-kamal \
|
|
23
|
+
--skip-rubocop \
|
|
24
|
+
--skip-solid \
|
|
14
25
|
--skip-spring \
|
|
15
|
-
--skip-
|
|
26
|
+
--skip-thruster
|
|
16
27
|
|
|
17
|
-
cd ${test_app}
|
|
28
|
+
cd ${test_app}
|
|
18
29
|
|
|
30
|
+
bundle add formtastic --path=../formtastic
|
|
31
|
+
bundle install
|
|
19
32
|
bundle exec rake test
|