formtastic 3.0.0 → 5.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 +5 -13
- data/.gitattributes +1 -0
- data/.github/workflows/test.yml +61 -0
- data/.gitignore +3 -2
- data/CHANGELOG.md +61 -0
- data/Gemfile.lock +140 -0
- data/MIT-LICENSE +1 -1
- data/{README.textile → README.md} +191 -168
- data/RELEASE_PROCESS +3 -1
- data/Rakefile +24 -8
- data/app/assets/stylesheets/formtastic.css +1 -1
- data/bin/appraisal +8 -0
- data/formtastic.gemspec +13 -17
- data/gemfiles/rails_6.0/Gemfile +5 -0
- data/gemfiles/rails_6.1/Gemfile +5 -0
- data/gemfiles/rails_7.0/Gemfile +5 -0
- data/gemfiles/rails_7.1/Gemfile +5 -0
- data/gemfiles/rails_edge/Gemfile +13 -0
- data/lib/formtastic/action_class_finder.rb +19 -0
- data/lib/formtastic/actions/base.rb +1 -0
- data/lib/formtastic/actions/button_action.rb +56 -53
- data/lib/formtastic/actions/buttonish.rb +1 -0
- data/lib/formtastic/actions/input_action.rb +60 -57
- data/lib/formtastic/actions/link_action.rb +69 -67
- data/lib/formtastic/actions.rb +7 -3
- data/lib/formtastic/deprecation.rb +6 -0
- data/lib/formtastic/engine.rb +4 -1
- data/lib/formtastic/form_builder.rb +32 -25
- data/lib/formtastic/helpers/action_helper.rb +22 -31
- data/lib/formtastic/helpers/actions_helper.rb +1 -0
- data/lib/formtastic/helpers/enum.rb +14 -0
- data/lib/formtastic/helpers/errors_helper.rb +3 -2
- data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -11
- data/lib/formtastic/helpers/file_column_detection.rb +1 -0
- data/lib/formtastic/helpers/form_helper.rb +4 -3
- data/lib/formtastic/helpers/input_helper.rb +59 -80
- data/lib/formtastic/helpers/inputs_helper.rb +33 -27
- data/lib/formtastic/helpers/reflection.rb +5 -4
- data/lib/formtastic/helpers.rb +2 -2
- data/lib/formtastic/html_attributes.rb +13 -1
- data/lib/formtastic/i18n.rb +2 -1
- data/lib/formtastic/input_class_finder.rb +19 -0
- data/lib/formtastic/inputs/base/associations.rb +1 -0
- data/lib/formtastic/inputs/base/choices.rb +4 -3
- data/lib/formtastic/inputs/base/collections.rb +47 -11
- data/lib/formtastic/inputs/base/database.rb +8 -5
- data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
- data/lib/formtastic/inputs/base/errors.rb +7 -6
- data/lib/formtastic/inputs/base/fileish.rb +1 -0
- data/lib/formtastic/inputs/base/hints.rb +2 -1
- data/lib/formtastic/inputs/base/html.rb +12 -10
- data/lib/formtastic/inputs/base/labelling.rb +3 -2
- data/lib/formtastic/inputs/base/naming.rb +5 -4
- data/lib/formtastic/inputs/base/numeric.rb +1 -0
- data/lib/formtastic/inputs/base/options.rb +3 -3
- data/lib/formtastic/inputs/base/placeholder.rb +1 -0
- data/lib/formtastic/inputs/base/stringish.rb +1 -0
- data/lib/formtastic/inputs/base/timeish.rb +9 -4
- data/lib/formtastic/inputs/base/validations.rb +39 -12
- data/lib/formtastic/inputs/base/wrapping.rb +2 -3
- data/lib/formtastic/inputs/base.rb +17 -12
- data/lib/formtastic/inputs/boolean_input.rb +2 -1
- data/lib/formtastic/inputs/check_boxes_input.rb +16 -24
- data/lib/formtastic/inputs/color_input.rb +1 -1
- data/lib/formtastic/inputs/country_input.rb +4 -1
- data/lib/formtastic/inputs/datalist_input.rb +42 -0
- data/lib/formtastic/inputs/date_picker_input.rb +1 -0
- data/lib/formtastic/inputs/date_select_input.rb +1 -0
- data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
- data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
- data/lib/formtastic/inputs/email_input.rb +1 -0
- data/lib/formtastic/inputs/file_input.rb +3 -2
- data/lib/formtastic/inputs/hidden_input.rb +3 -2
- data/lib/formtastic/inputs/number_input.rb +1 -0
- data/lib/formtastic/inputs/password_input.rb +1 -0
- data/lib/formtastic/inputs/phone_input.rb +1 -0
- data/lib/formtastic/inputs/radio_input.rb +26 -21
- data/lib/formtastic/inputs/range_input.rb +1 -0
- data/lib/formtastic/inputs/search_input.rb +1 -0
- data/lib/formtastic/inputs/select_input.rb +32 -10
- data/lib/formtastic/inputs/string_input.rb +1 -0
- data/lib/formtastic/inputs/text_input.rb +1 -0
- data/lib/formtastic/inputs/time_picker_input.rb +1 -0
- data/lib/formtastic/inputs/time_select_input.rb +1 -0
- data/lib/formtastic/inputs/time_zone_input.rb +17 -6
- data/lib/formtastic/inputs/url_input.rb +1 -0
- data/lib/formtastic/inputs.rb +33 -28
- data/lib/formtastic/localized_string.rb +2 -1
- data/lib/formtastic/localizer.rb +23 -24
- data/lib/formtastic/namespaced_class_finder.rb +98 -0
- data/lib/formtastic/version.rb +2 -1
- data/lib/formtastic.rb +19 -14
- data/lib/generators/formtastic/form/form_generator.rb +8 -2
- data/lib/generators/formtastic/input/input_generator.rb +47 -0
- data/lib/generators/formtastic/install/install_generator.rb +2 -0
- data/lib/generators/templates/formtastic.rb +29 -7
- data/lib/generators/templates/input.rb +19 -0
- data/sample/basic_inputs.html +1 -1
- data/script/integration-template.rb +73 -0
- data/script/integration.sh +19 -0
- data/spec/action_class_finder_spec.rb +13 -0
- data/spec/actions/button_action_spec.rb +21 -20
- data/spec/actions/generic_action_spec.rb +134 -133
- data/spec/actions/input_action_spec.rb +20 -19
- data/spec/actions/link_action_spec.rb +30 -29
- data/spec/builder/custom_builder_spec.rb +39 -22
- data/spec/builder/error_proc_spec.rb +6 -5
- data/spec/builder/semantic_fields_for_spec.rb +46 -45
- data/spec/fast_spec_helper.rb +13 -0
- data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
- data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
- data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
- data/spec/helpers/action_helper_spec.rb +70 -97
- data/spec/helpers/actions_helper_spec.rb +43 -42
- data/spec/helpers/form_helper_spec.rb +56 -39
- data/spec/helpers/input_helper_spec.rb +314 -255
- data/spec/helpers/inputs_helper_spec.rb +217 -202
- data/spec/helpers/reflection_helper_spec.rb +7 -6
- data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
- data/spec/i18n_spec.rb +30 -29
- data/spec/input_class_finder_spec.rb +11 -0
- data/spec/inputs/base/collections_spec.rb +78 -0
- data/spec/inputs/base/validations_spec.rb +481 -0
- data/spec/inputs/boolean_input_spec.rb +73 -72
- data/spec/inputs/check_boxes_input_spec.rb +174 -123
- data/spec/inputs/color_input_spec.rb +53 -64
- data/spec/inputs/country_input_spec.rb +23 -22
- data/spec/inputs/custom_input_spec.rb +3 -6
- data/spec/inputs/datalist_input_spec.rb +62 -0
- data/spec/inputs/date_picker_input_spec.rb +114 -113
- data/spec/inputs/date_select_input_spec.rb +76 -61
- data/spec/inputs/datetime_picker_input_spec.rb +123 -122
- data/spec/inputs/datetime_select_input_spec.rb +85 -68
- data/spec/inputs/email_input_spec.rb +17 -16
- data/spec/inputs/file_input_spec.rb +18 -17
- data/spec/inputs/hidden_input_spec.rb +32 -31
- data/spec/inputs/include_blank_spec.rb +10 -9
- data/spec/inputs/label_spec.rb +36 -31
- data/spec/inputs/number_input_spec.rb +212 -211
- data/spec/inputs/password_input_spec.rb +17 -16
- data/spec/inputs/phone_input_spec.rb +17 -16
- data/spec/inputs/placeholder_spec.rb +18 -17
- data/spec/inputs/radio_input_spec.rb +92 -65
- data/spec/inputs/range_input_spec.rb +136 -135
- data/spec/inputs/readonly_spec.rb +51 -0
- data/spec/inputs/search_input_spec.rb +16 -15
- data/spec/inputs/select_input_spec.rb +209 -102
- data/spec/inputs/string_input_spec.rb +51 -50
- data/spec/inputs/text_input_spec.rb +34 -33
- data/spec/inputs/time_picker_input_spec.rb +115 -114
- data/spec/inputs/time_select_input_spec.rb +84 -70
- data/spec/inputs/time_zone_input_spec.rb +58 -31
- data/spec/inputs/url_input_spec.rb +17 -16
- data/spec/inputs/with_options_spec.rb +9 -8
- data/spec/localizer_spec.rb +18 -17
- data/spec/namespaced_class_finder_spec.rb +91 -0
- data/spec/schema.rb +22 -0
- data/spec/spec_helper.rb +180 -249
- data/spec/support/custom_macros.rb +128 -98
- data/spec/support/deprecation.rb +2 -1
- data/spec/support/shared_examples.rb +13 -0
- data/spec/support/specialized_class_finder_shared_example.rb +28 -0
- data/spec/support/test_environment.rb +25 -10
- metadata +95 -136
- data/.travis.yml +0 -28
- data/Appraisals +0 -25
- data/CHANGELOG +0 -27
- data/gemfiles/rails_3.2.gemfile +0 -7
- data/gemfiles/rails_4.0.4.gemfile +0 -7
- data/gemfiles/rails_4.1.gemfile +0 -7
- data/gemfiles/rails_4.gemfile +0 -7
- data/gemfiles/rails_edge.gemfile +0 -10
- data/lib/formtastic/util.rb +0 -53
- data/spec/support/deferred_garbage_collection.rb +0 -21
- data/spec/util_spec.rb +0 -52
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Formtastic
|
|
2
3
|
module Inputs
|
|
3
4
|
# A select input is used to render a `<select>` tag with a series of options to choose from.
|
|
@@ -8,6 +9,7 @@ module Formtastic
|
|
|
8
9
|
# This is the default input choice when:
|
|
9
10
|
#
|
|
10
11
|
# * the database column type is an `:integer` and there is an association (`belongs_to`)
|
|
12
|
+
# * the database column type is an `:integer` and there is an enum defined (`enum`)
|
|
11
13
|
# * the database column type is a `:string` and the `:collection` option is used
|
|
12
14
|
# * there an object with an association, but no database column on the object (`has_many`, etc)
|
|
13
15
|
# * there is no object and the `:collection` option is used
|
|
@@ -38,6 +40,13 @@ module Formtastic
|
|
|
38
40
|
# `:to_s`, which are defined in the configurations `collection_label_methods` and
|
|
39
41
|
# `collection_value_methods` (see examples below).
|
|
40
42
|
#
|
|
43
|
+
# For select inputs that map to ActiveRecord `enum` attributes, Formtastic will automatically
|
|
44
|
+
# load in your enum options to be used as the select's options. This can be overridden with
|
|
45
|
+
# the `:collection` option, or augmented with I18n translations. See examples below.
|
|
46
|
+
# An error is raised if you try to render a multi-select with an enum, as ActiveRecord can
|
|
47
|
+
# only store one choice in the database.
|
|
48
|
+
#
|
|
49
|
+
#
|
|
41
50
|
# @example Basic `belongs_to` example with full form context
|
|
42
51
|
#
|
|
43
52
|
# <%= semantic_form_for @post do |f| %>
|
|
@@ -94,6 +103,8 @@ module Formtastic
|
|
|
94
103
|
# <%= f.input :author, :as => :select, :collection => @authors %>
|
|
95
104
|
# <%= f.input :author, :as => :select, :collection => Author.all %>
|
|
96
105
|
# <%= f.input :author, :as => :select, :collection => Author.some_named_scope %>
|
|
106
|
+
# <%= f.input :author, :as => :select, :collection => Author.pluck(:full_name, :id) %>
|
|
107
|
+
# <%= f.input :author, :as => :select, :collection => Author.pluck(Arel.sql("CONCAT(`first_name`, ' ', `last_name`)"), :id)) %>
|
|
97
108
|
# <%= f.input :author, :as => :select, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
|
|
98
109
|
# <%= f.input :author, :as => :select, :collection => ["Justin", "Kate"] %>
|
|
99
110
|
# <%= f.input :author, :as => :select, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
|
|
@@ -106,15 +117,6 @@ module Formtastic
|
|
|
106
117
|
# <%= f.input :author, :as => :select, :collection => grouped_options_for_select(...) %>
|
|
107
118
|
# <%= f.input :author, :as => :select, :collection => time_zone_options_for_select(...) %>
|
|
108
119
|
#
|
|
109
|
-
# @example The `:member_label` can be used to call a different method (or a Proc) on each object in the collection for rendering the label text (it'll try the methods like `to_s` in `collection_label_methods` config by default)
|
|
110
|
-
# <%= f.input :author, :as => :select, :member_label => :name %>
|
|
111
|
-
# <%= f.input :author, :as => :select, :member_label => :name_with_post_count %>
|
|
112
|
-
# <%= f.input :author, :as => :select, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" } %>
|
|
113
|
-
#
|
|
114
|
-
# @example The `:member_value` can be used to call a different method (or a Proc) on each object in the collection for rendering the value for each checkbox (it'll try the methods like `id` in `collection_value_methods` config by default)
|
|
115
|
-
# <%= f.input :author, :as => :select, :member_value => :login %>
|
|
116
|
-
# <%= f.input :author, :as => :select, :member_value => Proc.new { |c| c.full_name.downcase.underscore } %>
|
|
117
|
-
#
|
|
118
120
|
# @example Set HTML attributes on the `<select>` tag with `:input_html`
|
|
119
121
|
# <%= f.input :authors, :as => :select, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
|
|
120
122
|
#
|
|
@@ -131,6 +133,21 @@ module Formtastic
|
|
|
131
133
|
# <%= f.input :author, :as => :select, :prompt => true %> => <option value="">Please select</option>
|
|
132
134
|
# <%= f.input :author, :as => :select, :prompt => "Please select an author" %>
|
|
133
135
|
#
|
|
136
|
+
# @example Using ActiveRecord enum attribute with i18n translation:
|
|
137
|
+
# # post.rb
|
|
138
|
+
# class Post < ActiveRecord::Base
|
|
139
|
+
# enum :status => [ :active, :archived ]
|
|
140
|
+
# end
|
|
141
|
+
# # en.yml
|
|
142
|
+
# en:
|
|
143
|
+
# activerecord:
|
|
144
|
+
# attributes:
|
|
145
|
+
# post:
|
|
146
|
+
# statuses:
|
|
147
|
+
# active: I am active!
|
|
148
|
+
# archived: I am archived!
|
|
149
|
+
# # form
|
|
150
|
+
# <%= f.input :status, :as => :select %>
|
|
134
151
|
#
|
|
135
152
|
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
136
153
|
# @see Formtastic::Inputs::CheckBoxesInput CheckBoxesInput as an alternative for `has_many` and `has_and_belongs_to_many` associations
|
|
@@ -141,6 +158,11 @@ module Formtastic
|
|
|
141
158
|
include Base
|
|
142
159
|
include Base::Collections
|
|
143
160
|
|
|
161
|
+
def initialize(*args)
|
|
162
|
+
super
|
|
163
|
+
raise Formtastic::UnsupportedEnumCollection if collection_from_enum? && multiple?
|
|
164
|
+
end
|
|
165
|
+
|
|
144
166
|
def to_html
|
|
145
167
|
input_wrapping do
|
|
146
168
|
label_html <<
|
|
@@ -175,7 +197,7 @@ module Formtastic
|
|
|
175
197
|
def extra_input_html_options
|
|
176
198
|
{
|
|
177
199
|
:multiple => multiple?,
|
|
178
|
-
:name =>
|
|
200
|
+
:name => multiple? ? input_html_options_name_multiple : input_html_options_name
|
|
179
201
|
}
|
|
180
202
|
|
|
181
203
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Formtastic
|
|
2
3
|
module Inputs
|
|
3
4
|
|
|
@@ -28,9 +29,19 @@ module Formtastic
|
|
|
28
29
|
#
|
|
29
30
|
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
30
31
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
32
|
+
# The priority_zones option:
|
|
33
|
+
# Since this input actually uses Rails' `time_zone_select` helper, the :priority_zones
|
|
34
|
+
# option needs to be an array of ActiveSupport::TimeZone objects.
|
|
35
|
+
#
|
|
36
|
+
# And you can configure default value using
|
|
37
|
+
#
|
|
38
|
+
# ```
|
|
39
|
+
# Formtastic::FormBuilder.priority_time_zones = [timezone1, timezone2]
|
|
40
|
+
# ```
|
|
41
|
+
#
|
|
42
|
+
# See http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/time_zone_select for more information.
|
|
43
|
+
#
|
|
44
|
+
class TimeZoneInput
|
|
34
45
|
include Base
|
|
35
46
|
|
|
36
47
|
def to_html
|
|
@@ -39,10 +50,10 @@ module Formtastic
|
|
|
39
50
|
builder.time_zone_select(method, priority_zones, input_options, input_html_options)
|
|
40
51
|
end
|
|
41
52
|
end
|
|
42
|
-
|
|
53
|
+
|
|
43
54
|
def priority_zones
|
|
44
|
-
options[:priority_zones] ||
|
|
55
|
+
options[:priority_zones] || Formtastic::FormBuilder.priority_time_zones
|
|
45
56
|
end
|
|
46
57
|
end
|
|
47
58
|
end
|
|
48
|
-
end
|
|
59
|
+
end
|
data/lib/formtastic/inputs.rb
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Formtastic
|
|
2
3
|
module Inputs
|
|
3
4
|
extend ActiveSupport::Autoload
|
|
4
5
|
|
|
5
6
|
autoload :Base
|
|
6
7
|
autoload :Basic
|
|
7
|
-
autoload :BooleanInput
|
|
8
|
-
autoload :CheckBoxesInput
|
|
9
|
-
autoload :ColorInput
|
|
10
|
-
autoload :CountryInput
|
|
11
|
-
autoload :DateInput
|
|
12
|
-
autoload :DatePickerInput
|
|
13
|
-
autoload :DatetimePickerInput
|
|
14
|
-
autoload :DateSelectInput
|
|
15
|
-
autoload :DatetimeInput
|
|
16
|
-
autoload :DatetimeSelectInput
|
|
17
|
-
autoload :EmailInput
|
|
18
|
-
autoload :FileInput
|
|
19
|
-
autoload :HiddenInput
|
|
20
|
-
autoload :NumberInput
|
|
21
|
-
autoload :NumericInput
|
|
22
|
-
autoload :PasswordInput
|
|
23
|
-
autoload :PhoneInput
|
|
24
|
-
autoload :RadioInput
|
|
25
|
-
autoload :RangeInput
|
|
26
|
-
autoload :SearchInput
|
|
27
|
-
autoload :SelectInput
|
|
28
|
-
autoload :StringInput
|
|
29
|
-
autoload :TextInput
|
|
30
|
-
autoload :TimeInput
|
|
31
|
-
autoload :TimePickerInput
|
|
32
|
-
autoload :TimeSelectInput
|
|
33
|
-
autoload :TimeZoneInput
|
|
34
8
|
autoload :Timeish
|
|
35
|
-
|
|
9
|
+
|
|
10
|
+
eager_autoload do
|
|
11
|
+
autoload :BooleanInput
|
|
12
|
+
autoload :CheckBoxesInput
|
|
13
|
+
autoload :ColorInput
|
|
14
|
+
autoload :CountryInput
|
|
15
|
+
autoload :DatalistInput
|
|
16
|
+
autoload :DateInput
|
|
17
|
+
autoload :DatePickerInput
|
|
18
|
+
autoload :DatetimePickerInput
|
|
19
|
+
autoload :DateSelectInput
|
|
20
|
+
autoload :DatetimeInput
|
|
21
|
+
autoload :DatetimeSelectInput
|
|
22
|
+
autoload :EmailInput
|
|
23
|
+
autoload :FileInput
|
|
24
|
+
autoload :HiddenInput
|
|
25
|
+
autoload :NumberInput
|
|
26
|
+
autoload :NumericInput
|
|
27
|
+
autoload :PasswordInput
|
|
28
|
+
autoload :PhoneInput
|
|
29
|
+
autoload :RadioInput
|
|
30
|
+
autoload :RangeInput
|
|
31
|
+
autoload :SearchInput
|
|
32
|
+
autoload :SelectInput
|
|
33
|
+
autoload :StringInput
|
|
34
|
+
autoload :TextInput
|
|
35
|
+
autoload :TimeInput
|
|
36
|
+
autoload :TimePickerInput
|
|
37
|
+
autoload :TimeSelectInput
|
|
38
|
+
autoload :TimeZoneInput
|
|
39
|
+
autoload :UrlInput
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Formtastic
|
|
2
3
|
module LocalizedString
|
|
3
4
|
|
|
@@ -7,7 +8,7 @@ module Formtastic
|
|
|
7
8
|
|
|
8
9
|
protected
|
|
9
10
|
|
|
10
|
-
def localized_string(key, value, type, options = {})
|
|
11
|
+
def localized_string(key, value, type, options = {}) # @private
|
|
11
12
|
current_builder = respond_to?(:builder) ? builder : self
|
|
12
13
|
localizer = Formtastic::FormBuilder.i18n_localizer.new(current_builder)
|
|
13
14
|
localizer.localize(key, value, type, options)
|
data/lib/formtastic/localizer.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Formtastic
|
|
2
|
-
# Implementation for looking up localized values within Formtastic using I18n, if no
|
|
3
|
+
# Implementation for looking up localized values within Formtastic using I18n, if no
|
|
3
4
|
# explicit value (like the `:label` option) is set and I18n-lookups are enabled in the
|
|
4
5
|
# configuration.
|
|
5
6
|
#
|
|
@@ -28,50 +29,50 @@ module Formtastic
|
|
|
28
29
|
def get(key)
|
|
29
30
|
cache[key]
|
|
30
31
|
end
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
def has_key?(key)
|
|
33
34
|
cache.has_key?(key)
|
|
34
35
|
end
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
def set(key, result)
|
|
37
38
|
cache[key] = result
|
|
38
39
|
end
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
def cache
|
|
41
42
|
@cache ||= {}
|
|
42
43
|
end
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
def clear!
|
|
45
46
|
cache.clear
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
|
-
|
|
49
|
+
|
|
49
50
|
attr_accessor :builder
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
def self.cache
|
|
52
53
|
@cache ||= Cache.new
|
|
53
54
|
end
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
def initialize(current_builder)
|
|
56
|
-
self.builder = current_builder
|
|
57
|
+
self.builder = current_builder
|
|
57
58
|
end
|
|
58
59
|
|
|
59
|
-
def localize(key, value, type, options = {})
|
|
60
|
+
def localize(key, value, type, options = {}) # @private
|
|
60
61
|
key = value if value.is_a?(::Symbol)
|
|
61
|
-
|
|
62
|
+
|
|
62
63
|
if value.is_a?(::String)
|
|
63
64
|
escape_html_entities(value)
|
|
64
65
|
else
|
|
65
66
|
use_i18n = value.nil? ? i18n_lookups_by_default : (value != false)
|
|
66
67
|
use_cache = i18n_cache_lookups
|
|
67
68
|
cache = self.class.cache
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
if use_i18n
|
|
70
71
|
model_name, nested_model_name = normalize_model_name(builder.model_name.underscore)
|
|
71
72
|
|
|
72
73
|
action_name = builder.template.params[:action].to_s rescue ''
|
|
73
74
|
attribute_name = key.to_s
|
|
74
|
-
|
|
75
|
+
|
|
75
76
|
# look in the cache first
|
|
76
77
|
if use_cache
|
|
77
78
|
cache_key = [::I18n.locale, action_name, model_name, nested_model_name, attribute_name, key, value, type, options]
|
|
@@ -86,7 +87,7 @@ module Formtastic
|
|
|
86
87
|
i18n_path.gsub!('%{model}', model_name)
|
|
87
88
|
i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
|
|
88
89
|
i18n_path.gsub!('%{attribute}', attribute_name)
|
|
89
|
-
i18n_path.
|
|
90
|
+
i18n_path.tr!('..', '.')
|
|
90
91
|
i18n_path.to_sym
|
|
91
92
|
end
|
|
92
93
|
defaults << ''
|
|
@@ -101,9 +102,9 @@ module Formtastic
|
|
|
101
102
|
# This is effectively what Rails label helper does for i18n lookup
|
|
102
103
|
options[:scope] = [:helpers, type]
|
|
103
104
|
options[:default] = defaults
|
|
104
|
-
i18n_value = ::I18n.t(default_key, options)
|
|
105
|
+
i18n_value = ::I18n.t(default_key, **options)
|
|
105
106
|
end
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
# save the result to the cache
|
|
108
109
|
result = (i18n_value.is_a?(::String) && i18n_value.present?) ? escape_html_entities(i18n_value) : nil
|
|
109
110
|
cache.set(cache_key, result) if use_cache
|
|
@@ -115,16 +116,14 @@ module Formtastic
|
|
|
115
116
|
protected
|
|
116
117
|
|
|
117
118
|
def normalize_model_name(name)
|
|
118
|
-
if
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
elsif name =~ /(.+)\[(.+)\]/
|
|
122
|
-
# Rails 3 (and 3.1?) nested builder case with :post rather than @post
|
|
119
|
+
if name =~ /(.+)\[(.+)\]/
|
|
120
|
+
# Nested builder case with :post rather than @post
|
|
121
|
+
# TODO: check if this is no longer required with a minimum of Rails 4.1
|
|
123
122
|
[$1, $2]
|
|
124
123
|
elsif builder.respond_to?(:options) && builder.options.key?(:as)
|
|
125
124
|
[builder.options[:as].to_s]
|
|
126
125
|
elsif builder.respond_to?(:options) && builder.options.key?(:parent_builder)
|
|
127
|
-
# Rails 3.0 nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
|
|
126
|
+
# Rails 3.0+ nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
|
|
128
127
|
[builder.options[:parent_builder].object_name.to_s, name]
|
|
129
128
|
else
|
|
130
129
|
# Non-nested case
|
|
@@ -132,7 +131,7 @@ module Formtastic
|
|
|
132
131
|
end
|
|
133
132
|
end
|
|
134
133
|
|
|
135
|
-
def escape_html_entities(string)
|
|
134
|
+
def escape_html_entities(string) # @private
|
|
136
135
|
if (builder.escape_html_entities_in_hints_and_labels) ||
|
|
137
136
|
(self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
|
|
138
137
|
string = builder.template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true # Accept html_safe flag as indicator to skip escaping
|
|
@@ -143,7 +142,7 @@ module Formtastic
|
|
|
143
142
|
def i18n_lookups_by_default
|
|
144
143
|
builder.i18n_lookups_by_default
|
|
145
144
|
end
|
|
146
|
-
|
|
145
|
+
|
|
147
146
|
def i18n_cache_lookups
|
|
148
147
|
builder.i18n_cache_lookups
|
|
149
148
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Formtastic
|
|
3
|
+
# This class implements class resolution in a namespace chain. It
|
|
4
|
+
# is used both by Formtastic::Helpers::InputHelper and
|
|
5
|
+
# Formtastic::Helpers::ActionHelper to look up action and input classes.
|
|
6
|
+
#
|
|
7
|
+
# @example Implementing own class finder
|
|
8
|
+
# # You can implement own class finder that for example prefixes the class name or uses custom module.
|
|
9
|
+
# class MyInputClassFinder < Formtastic::NamespacedClassFinder
|
|
10
|
+
# def initialize(namespaces)
|
|
11
|
+
# super [MyNamespace] + namespaces # first lookup in MyNamespace then the defaults
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# private
|
|
15
|
+
#
|
|
16
|
+
# def class_name(as)
|
|
17
|
+
# "My#{super}Input" # for example MyStringInput
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # in config/initializers/formtastic.rb
|
|
22
|
+
# Formtastic::FormBuilder.input_class_finder = MyInputClassFinder
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
class NamespacedClassFinder
|
|
26
|
+
attr_reader :namespaces # @private
|
|
27
|
+
|
|
28
|
+
# @private
|
|
29
|
+
class NotFoundError < NameError
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.use_const_defined?
|
|
33
|
+
defined?(Rails) && ::Rails.application && ::Rails.application.config.respond_to?(:eager_load) && ::Rails.application.config.eager_load
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.finder_method
|
|
37
|
+
@finder_method ||= use_const_defined? ? :find_with_const_defined : :find_by_trying
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param namespaces [Array<Module>]
|
|
41
|
+
def initialize(namespaces)
|
|
42
|
+
@namespaces = namespaces.flatten
|
|
43
|
+
@cache = {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Looks up the given reference in the configured namespaces.
|
|
47
|
+
#
|
|
48
|
+
# Two finder methods are provided, one for development tries to
|
|
49
|
+
# reference the constant directly, triggering Rails' autoloading
|
|
50
|
+
# const_missing machinery; the second one instead for production
|
|
51
|
+
# checks with .const_defined before referencing the constant.
|
|
52
|
+
#
|
|
53
|
+
def find(as)
|
|
54
|
+
@cache[as] ||= resolve(as)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve(as)
|
|
58
|
+
class_name = class_name(as)
|
|
59
|
+
|
|
60
|
+
finder(class_name) or raise NotFoundError, "class #{class_name}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Converts symbol to class name
|
|
64
|
+
# Overridden in subclasses to create `StringInput` and `ButtonAction`
|
|
65
|
+
# @example
|
|
66
|
+
# class_name(:string) == "String"
|
|
67
|
+
|
|
68
|
+
def class_name(as)
|
|
69
|
+
as.to_s.camelize
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def finder(class_name) # @private
|
|
75
|
+
send(self.class.finder_method, class_name)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Looks up the given class name in the configured namespaces in order,
|
|
79
|
+
# returning the first one that has the class name constant defined.
|
|
80
|
+
def find_with_const_defined(class_name)
|
|
81
|
+
@namespaces.find do |namespace|
|
|
82
|
+
if namespace.const_defined?(class_name)
|
|
83
|
+
break namespace.const_get(class_name)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Use auto-loading in development environment
|
|
89
|
+
def find_by_trying(class_name)
|
|
90
|
+
@namespaces.find do |namespace|
|
|
91
|
+
begin
|
|
92
|
+
break namespace.const_get(class_name)
|
|
93
|
+
rescue NameError
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/formtastic/version.rb
CHANGED
data/lib/formtastic.rb
CHANGED
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
require 'formtastic/engine' if defined?(::Rails)
|
|
3
4
|
|
|
4
5
|
module Formtastic
|
|
5
6
|
extend ActiveSupport::Autoload
|
|
6
7
|
|
|
7
|
-
autoload :FormBuilder
|
|
8
8
|
autoload :Helpers
|
|
9
9
|
autoload :HtmlAttributes
|
|
10
|
-
autoload :I18n
|
|
11
|
-
autoload :Inputs
|
|
12
|
-
autoload :Actions
|
|
13
10
|
autoload :LocalizedString
|
|
14
11
|
autoload :Localizer
|
|
15
|
-
autoload :
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
autoload :NamespacedClassFinder
|
|
13
|
+
autoload :InputClassFinder
|
|
14
|
+
autoload :ActionClassFinder
|
|
15
|
+
autoload :Deprecation
|
|
16
|
+
|
|
17
|
+
eager_autoload do
|
|
18
|
+
autoload :I18n
|
|
19
|
+
autoload :FormBuilder
|
|
20
|
+
autoload :Inputs
|
|
21
|
+
autoload :Actions
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
# @
|
|
24
|
+
# @public
|
|
24
25
|
class UnknownInputError < NameError
|
|
25
26
|
end
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
# @private
|
|
28
29
|
class UnknownActionError < NameError
|
|
29
30
|
end
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
# @private
|
|
32
33
|
class PolymorphicInputWithoutCollectionError < ArgumentError
|
|
33
34
|
end
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
# @private
|
|
36
37
|
class UnsupportedMethodForAction < ArgumentError
|
|
37
38
|
end
|
|
38
39
|
|
|
40
|
+
# @private
|
|
41
|
+
class UnsupportedEnumCollection < NameError
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
end
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
module Formtastic
|
|
3
4
|
# Generates a Formtastic form partial based on an existing model. It will not overwrite existing
|
|
4
5
|
# files without confirmation.
|
|
5
6
|
#
|
|
6
7
|
# @example
|
|
8
|
+
# !!!shell
|
|
7
9
|
# $ rails generate formtastic:form Post
|
|
8
10
|
# @example Copy the partial code to the pasteboard rather than generating a partial
|
|
11
|
+
# !!!shell
|
|
9
12
|
# $ rails generate formtastic:form Post --copy
|
|
10
13
|
# @example Return HAML or Slim output instead of default ERB
|
|
14
|
+
# !!!shell
|
|
11
15
|
# $ rails generate formtastic:form Post --template-engine haml
|
|
12
16
|
# $ rails generate formtastic:form Post --template-engine slim
|
|
13
17
|
# @example Generate a form for specific model attributes
|
|
18
|
+
# !!!shell
|
|
14
19
|
# $ rails generate formtastic:form Post title:string body:text
|
|
15
20
|
# @example Generate a form for a specific controller
|
|
21
|
+
# !!!shell
|
|
16
22
|
# $ rails generate formtastic:form Post --controller admin/posts
|
|
17
23
|
class FormGenerator < Rails::Generators::NamedBase
|
|
18
24
|
desc "Generates a Formtastic form partial based on an existing model."
|
|
@@ -70,7 +76,7 @@ module Formtastic
|
|
|
70
76
|
# Skips Active Record Timestamps.
|
|
71
77
|
def content_columns
|
|
72
78
|
model.content_columns.select do |column|
|
|
73
|
-
!Formtastic::
|
|
79
|
+
!Formtastic::FormBuilder.skipped_columns.include? column.name.to_sym
|
|
74
80
|
end
|
|
75
81
|
end
|
|
76
82
|
|
|
@@ -103,4 +109,4 @@ module Formtastic
|
|
|
103
109
|
end
|
|
104
110
|
|
|
105
111
|
end
|
|
106
|
-
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Formtastic
|
|
3
|
+
|
|
4
|
+
# Modify existing inputs, subclass them, or create your own from scratch.
|
|
5
|
+
# @example
|
|
6
|
+
# !!!shell
|
|
7
|
+
# $ rails generate formtastic:input HatSize
|
|
8
|
+
|
|
9
|
+
# @example Define input name using underscore convention
|
|
10
|
+
# !!!shell
|
|
11
|
+
# $ rails generate formtastic:input hat_size
|
|
12
|
+
|
|
13
|
+
# @example Override an existing input behavior
|
|
14
|
+
# !!!shell
|
|
15
|
+
# $ rails generate formtastic:input string --extend
|
|
16
|
+
|
|
17
|
+
# @example Extend an existing input behavior
|
|
18
|
+
# !!!shell
|
|
19
|
+
# $ rails generate formtastic:input FlexibleText --extend string
|
|
20
|
+
class InputGenerator < Rails::Generators::NamedBase
|
|
21
|
+
|
|
22
|
+
argument :name, :type => :string, :required => true, :banner => 'FILE_NAME'
|
|
23
|
+
|
|
24
|
+
source_root File.expand_path('../../../templates', __FILE__)
|
|
25
|
+
|
|
26
|
+
class_option :extend
|
|
27
|
+
|
|
28
|
+
def create
|
|
29
|
+
normalize_file_name
|
|
30
|
+
define_extension_sentence
|
|
31
|
+
template "input.rb", "app/inputs/#{name.underscore}_input.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def normalize_file_name
|
|
37
|
+
name.chomp!("Input") if name.ends_with?("Input")
|
|
38
|
+
name.chomp!("_input") if name.ends_with?("_input")
|
|
39
|
+
name.chomp!("input") if name.ends_with?("input")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def define_extension_sentence
|
|
43
|
+
@extension_sentence = "< Formtastic::Inputs::#{name.camelize}Input" if options[:extend] == "extend"
|
|
44
|
+
@extension_sentence ||= "< Formtastic::Inputs::#{options[:extend].camelize}Input" if options[:extend]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Formtastic
|
|
4
5
|
# Copies a config initializer to config/initializers/formtastic.rb
|
|
5
6
|
#
|
|
6
7
|
# @example
|
|
8
|
+
# !!!shell
|
|
7
9
|
# $ rails generate formtastic:install
|
|
8
10
|
class InstallGenerator < Rails::Generators::Base
|
|
9
11
|
source_root File.expand_path('../../../templates', __FILE__)
|