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.
Files changed (175) hide show
  1. checksums.yaml +5 -13
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +3 -2
  5. data/CHANGELOG.md +61 -0
  6. data/Gemfile.lock +140 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +191 -168
  9. data/RELEASE_PROCESS +3 -1
  10. data/Rakefile +24 -8
  11. data/app/assets/stylesheets/formtastic.css +1 -1
  12. data/bin/appraisal +8 -0
  13. data/formtastic.gemspec +13 -17
  14. data/gemfiles/rails_6.0/Gemfile +5 -0
  15. data/gemfiles/rails_6.1/Gemfile +5 -0
  16. data/gemfiles/rails_7.0/Gemfile +5 -0
  17. data/gemfiles/rails_7.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +19 -0
  20. data/lib/formtastic/actions/base.rb +1 -0
  21. data/lib/formtastic/actions/button_action.rb +56 -53
  22. data/lib/formtastic/actions/buttonish.rb +1 -0
  23. data/lib/formtastic/actions/input_action.rb +60 -57
  24. data/lib/formtastic/actions/link_action.rb +69 -67
  25. data/lib/formtastic/actions.rb +7 -3
  26. data/lib/formtastic/deprecation.rb +6 -0
  27. data/lib/formtastic/engine.rb +4 -1
  28. data/lib/formtastic/form_builder.rb +32 -25
  29. data/lib/formtastic/helpers/action_helper.rb +22 -31
  30. data/lib/formtastic/helpers/actions_helper.rb +1 -0
  31. data/lib/formtastic/helpers/enum.rb +14 -0
  32. data/lib/formtastic/helpers/errors_helper.rb +3 -2
  33. data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -11
  34. data/lib/formtastic/helpers/file_column_detection.rb +1 -0
  35. data/lib/formtastic/helpers/form_helper.rb +4 -3
  36. data/lib/formtastic/helpers/input_helper.rb +59 -80
  37. data/lib/formtastic/helpers/inputs_helper.rb +33 -27
  38. data/lib/formtastic/helpers/reflection.rb +5 -4
  39. data/lib/formtastic/helpers.rb +2 -2
  40. data/lib/formtastic/html_attributes.rb +13 -1
  41. data/lib/formtastic/i18n.rb +2 -1
  42. data/lib/formtastic/input_class_finder.rb +19 -0
  43. data/lib/formtastic/inputs/base/associations.rb +1 -0
  44. data/lib/formtastic/inputs/base/choices.rb +4 -3
  45. data/lib/formtastic/inputs/base/collections.rb +47 -11
  46. data/lib/formtastic/inputs/base/database.rb +8 -5
  47. data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
  48. data/lib/formtastic/inputs/base/errors.rb +7 -6
  49. data/lib/formtastic/inputs/base/fileish.rb +1 -0
  50. data/lib/formtastic/inputs/base/hints.rb +2 -1
  51. data/lib/formtastic/inputs/base/html.rb +12 -10
  52. data/lib/formtastic/inputs/base/labelling.rb +3 -2
  53. data/lib/formtastic/inputs/base/naming.rb +5 -4
  54. data/lib/formtastic/inputs/base/numeric.rb +1 -0
  55. data/lib/formtastic/inputs/base/options.rb +3 -3
  56. data/lib/formtastic/inputs/base/placeholder.rb +1 -0
  57. data/lib/formtastic/inputs/base/stringish.rb +1 -0
  58. data/lib/formtastic/inputs/base/timeish.rb +9 -4
  59. data/lib/formtastic/inputs/base/validations.rb +39 -12
  60. data/lib/formtastic/inputs/base/wrapping.rb +2 -3
  61. data/lib/formtastic/inputs/base.rb +17 -12
  62. data/lib/formtastic/inputs/boolean_input.rb +2 -1
  63. data/lib/formtastic/inputs/check_boxes_input.rb +16 -24
  64. data/lib/formtastic/inputs/color_input.rb +1 -1
  65. data/lib/formtastic/inputs/country_input.rb +4 -1
  66. data/lib/formtastic/inputs/datalist_input.rb +42 -0
  67. data/lib/formtastic/inputs/date_picker_input.rb +1 -0
  68. data/lib/formtastic/inputs/date_select_input.rb +1 -0
  69. data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
  70. data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
  71. data/lib/formtastic/inputs/email_input.rb +1 -0
  72. data/lib/formtastic/inputs/file_input.rb +3 -2
  73. data/lib/formtastic/inputs/hidden_input.rb +3 -2
  74. data/lib/formtastic/inputs/number_input.rb +1 -0
  75. data/lib/formtastic/inputs/password_input.rb +1 -0
  76. data/lib/formtastic/inputs/phone_input.rb +1 -0
  77. data/lib/formtastic/inputs/radio_input.rb +26 -21
  78. data/lib/formtastic/inputs/range_input.rb +1 -0
  79. data/lib/formtastic/inputs/search_input.rb +1 -0
  80. data/lib/formtastic/inputs/select_input.rb +32 -10
  81. data/lib/formtastic/inputs/string_input.rb +1 -0
  82. data/lib/formtastic/inputs/text_input.rb +1 -0
  83. data/lib/formtastic/inputs/time_picker_input.rb +1 -0
  84. data/lib/formtastic/inputs/time_select_input.rb +1 -0
  85. data/lib/formtastic/inputs/time_zone_input.rb +17 -6
  86. data/lib/formtastic/inputs/url_input.rb +1 -0
  87. data/lib/formtastic/inputs.rb +33 -28
  88. data/lib/formtastic/localized_string.rb +2 -1
  89. data/lib/formtastic/localizer.rb +23 -24
  90. data/lib/formtastic/namespaced_class_finder.rb +98 -0
  91. data/lib/formtastic/version.rb +2 -1
  92. data/lib/formtastic.rb +19 -14
  93. data/lib/generators/formtastic/form/form_generator.rb +8 -2
  94. data/lib/generators/formtastic/input/input_generator.rb +47 -0
  95. data/lib/generators/formtastic/install/install_generator.rb +2 -0
  96. data/lib/generators/templates/formtastic.rb +29 -7
  97. data/lib/generators/templates/input.rb +19 -0
  98. data/sample/basic_inputs.html +1 -1
  99. data/script/integration-template.rb +73 -0
  100. data/script/integration.sh +19 -0
  101. data/spec/action_class_finder_spec.rb +13 -0
  102. data/spec/actions/button_action_spec.rb +21 -20
  103. data/spec/actions/generic_action_spec.rb +134 -133
  104. data/spec/actions/input_action_spec.rb +20 -19
  105. data/spec/actions/link_action_spec.rb +30 -29
  106. data/spec/builder/custom_builder_spec.rb +39 -22
  107. data/spec/builder/error_proc_spec.rb +6 -5
  108. data/spec/builder/semantic_fields_for_spec.rb +46 -45
  109. data/spec/fast_spec_helper.rb +13 -0
  110. data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
  111. data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
  112. data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
  113. data/spec/helpers/action_helper_spec.rb +70 -97
  114. data/spec/helpers/actions_helper_spec.rb +43 -42
  115. data/spec/helpers/form_helper_spec.rb +56 -39
  116. data/spec/helpers/input_helper_spec.rb +314 -255
  117. data/spec/helpers/inputs_helper_spec.rb +217 -202
  118. data/spec/helpers/reflection_helper_spec.rb +7 -6
  119. data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
  120. data/spec/i18n_spec.rb +30 -29
  121. data/spec/input_class_finder_spec.rb +11 -0
  122. data/spec/inputs/base/collections_spec.rb +78 -0
  123. data/spec/inputs/base/validations_spec.rb +481 -0
  124. data/spec/inputs/boolean_input_spec.rb +73 -72
  125. data/spec/inputs/check_boxes_input_spec.rb +174 -123
  126. data/spec/inputs/color_input_spec.rb +53 -64
  127. data/spec/inputs/country_input_spec.rb +23 -22
  128. data/spec/inputs/custom_input_spec.rb +3 -6
  129. data/spec/inputs/datalist_input_spec.rb +62 -0
  130. data/spec/inputs/date_picker_input_spec.rb +114 -113
  131. data/spec/inputs/date_select_input_spec.rb +76 -61
  132. data/spec/inputs/datetime_picker_input_spec.rb +123 -122
  133. data/spec/inputs/datetime_select_input_spec.rb +85 -68
  134. data/spec/inputs/email_input_spec.rb +17 -16
  135. data/spec/inputs/file_input_spec.rb +18 -17
  136. data/spec/inputs/hidden_input_spec.rb +32 -31
  137. data/spec/inputs/include_blank_spec.rb +10 -9
  138. data/spec/inputs/label_spec.rb +36 -31
  139. data/spec/inputs/number_input_spec.rb +212 -211
  140. data/spec/inputs/password_input_spec.rb +17 -16
  141. data/spec/inputs/phone_input_spec.rb +17 -16
  142. data/spec/inputs/placeholder_spec.rb +18 -17
  143. data/spec/inputs/radio_input_spec.rb +92 -65
  144. data/spec/inputs/range_input_spec.rb +136 -135
  145. data/spec/inputs/readonly_spec.rb +51 -0
  146. data/spec/inputs/search_input_spec.rb +16 -15
  147. data/spec/inputs/select_input_spec.rb +209 -102
  148. data/spec/inputs/string_input_spec.rb +51 -50
  149. data/spec/inputs/text_input_spec.rb +34 -33
  150. data/spec/inputs/time_picker_input_spec.rb +115 -114
  151. data/spec/inputs/time_select_input_spec.rb +84 -70
  152. data/spec/inputs/time_zone_input_spec.rb +58 -31
  153. data/spec/inputs/url_input_spec.rb +17 -16
  154. data/spec/inputs/with_options_spec.rb +9 -8
  155. data/spec/localizer_spec.rb +18 -17
  156. data/spec/namespaced_class_finder_spec.rb +91 -0
  157. data/spec/schema.rb +22 -0
  158. data/spec/spec_helper.rb +180 -249
  159. data/spec/support/custom_macros.rb +128 -98
  160. data/spec/support/deprecation.rb +2 -1
  161. data/spec/support/shared_examples.rb +13 -0
  162. data/spec/support/specialized_class_finder_shared_example.rb +28 -0
  163. data/spec/support/test_environment.rb +25 -10
  164. metadata +95 -136
  165. data/.travis.yml +0 -28
  166. data/Appraisals +0 -25
  167. data/CHANGELOG +0 -27
  168. data/gemfiles/rails_3.2.gemfile +0 -7
  169. data/gemfiles/rails_4.0.4.gemfile +0 -7
  170. data/gemfiles/rails_4.1.gemfile +0 -7
  171. data/gemfiles/rails_4.gemfile +0 -7
  172. data/gemfiles/rails_edge.gemfile +0 -10
  173. data/lib/formtastic/util.rb +0 -53
  174. data/spec/support/deferred_garbage_collection.rb +0 -21
  175. 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 => (multiple? && Rails::VERSION::MAJOR >= 3) ? input_html_options_name_multiple : input_html_options_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
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  # Outputs a series of select boxes for the fragments that make up a time (hour, minute, second).
@@ -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
- # @todo document :priority_zones option
32
- # @todo configurable default :priority_zones?
33
- class TimeZoneInput
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] || [] # TODO config?
55
+ options[:priority_zones] || Formtastic::FormBuilder.priority_time_zones
45
56
  end
46
57
  end
47
58
  end
48
- end
59
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -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
- autoload :UrlInput
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 = {}) #:nodoc:
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)
@@ -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 = {}) #:nodoc:
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.gsub!('..', '.')
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 !name =~ /\[/ && builder.respond_to?(:parent_builder) && builder.parent_builder.object_name
119
- # Rails 3.1 nested builder case
120
- [builder.parent_builder.object_name.to_s, name]
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) #:nodoc:
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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
- VERSION = "3.0.0"
3
+ VERSION = "5.0.0"
3
4
  end
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 :Util
16
-
17
- if defined?(::Rails) && Util.deprecated_version_of_rails?
18
- ::ActiveSupport::Deprecation.warn(
19
- "Support for Rails < 4.0.4 will be dropped from Formtastic 4.0",
20
- caller)
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
- # @private
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::Helpers::InputsHelper::SKIPPED_COLUMNS.include? column.name.to_sym
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__)