formtastic 2.1.0 → 4.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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +4 -2
  5. data/CHANGELOG.md +52 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +105 -0
  8. data/MIT-LICENSE +1 -1
  9. data/{README.textile → README.md} +204 -219
  10. data/RELEASE_PROCESS +3 -1
  11. data/Rakefile +27 -29
  12. data/app/assets/stylesheets/formtastic.css +3 -2
  13. data/bin/appraisal +8 -0
  14. data/formtastic.gemspec +11 -14
  15. data/gemfiles/rails_5.2/Gemfile +5 -0
  16. data/gemfiles/rails_6.0/Gemfile +5 -0
  17. data/gemfiles/rails_6.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +18 -0
  20. data/lib/formtastic/actions/button_action.rb +55 -60
  21. data/lib/formtastic/actions/input_action.rb +59 -57
  22. data/lib/formtastic/actions/link_action.rb +68 -67
  23. data/lib/formtastic/actions.rb +6 -3
  24. data/lib/formtastic/deprecation.rb +5 -0
  25. data/lib/formtastic/engine.rb +3 -1
  26. data/lib/formtastic/form_builder.rb +35 -16
  27. data/lib/formtastic/helpers/action_helper.rb +34 -28
  28. data/lib/formtastic/helpers/enum.rb +13 -0
  29. data/lib/formtastic/helpers/errors_helper.rb +2 -2
  30. data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -12
  31. data/lib/formtastic/helpers/form_helper.rb +19 -16
  32. data/lib/formtastic/helpers/input_helper.rb +69 -97
  33. data/lib/formtastic/helpers/inputs_helper.rb +35 -25
  34. data/lib/formtastic/helpers/reflection.rb +4 -4
  35. data/lib/formtastic/helpers.rb +1 -2
  36. data/lib/formtastic/html_attributes.rb +12 -1
  37. data/lib/formtastic/i18n.rb +1 -1
  38. data/lib/formtastic/input_class_finder.rb +18 -0
  39. data/lib/formtastic/inputs/base/choices.rb +2 -2
  40. data/lib/formtastic/inputs/base/collections.rb +46 -14
  41. data/lib/formtastic/inputs/base/database.rb +7 -2
  42. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  43. data/lib/formtastic/inputs/base/errors.rb +7 -7
  44. data/lib/formtastic/inputs/base/hints.rb +2 -2
  45. data/lib/formtastic/inputs/base/html.rb +10 -9
  46. data/lib/formtastic/inputs/base/labelling.rb +5 -8
  47. data/lib/formtastic/inputs/base/naming.rb +4 -4
  48. data/lib/formtastic/inputs/base/numeric.rb +1 -1
  49. data/lib/formtastic/inputs/base/options.rb +3 -4
  50. data/lib/formtastic/inputs/base/stringish.rb +10 -2
  51. data/lib/formtastic/inputs/base/timeish.rb +34 -22
  52. data/lib/formtastic/inputs/base/validations.rb +41 -13
  53. data/lib/formtastic/inputs/base/wrapping.rb +29 -26
  54. data/lib/formtastic/inputs/base.rb +22 -15
  55. data/lib/formtastic/inputs/boolean_input.rb +26 -12
  56. data/lib/formtastic/inputs/check_boxes_input.rb +39 -31
  57. data/lib/formtastic/inputs/color_input.rb +41 -0
  58. data/lib/formtastic/inputs/country_input.rb +24 -5
  59. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  60. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  61. data/lib/formtastic/inputs/{date_input.rb → date_select_input.rb} +1 -1
  62. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  63. data/lib/formtastic/inputs/{datetime_input.rb → datetime_select_input.rb} +1 -1
  64. data/lib/formtastic/inputs/file_input.rb +2 -2
  65. data/lib/formtastic/inputs/hidden_input.rb +2 -6
  66. data/lib/formtastic/inputs/radio_input.rb +28 -22
  67. data/lib/formtastic/inputs/select_input.rb +36 -39
  68. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  69. data/lib/formtastic/inputs/{time_input.rb → time_select_input.rb} +6 -2
  70. data/lib/formtastic/inputs/time_zone_input.rb +16 -6
  71. data/lib/formtastic/inputs.rb +32 -21
  72. data/lib/formtastic/localized_string.rb +1 -1
  73. data/lib/formtastic/localizer.rb +24 -24
  74. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  75. data/lib/formtastic/version.rb +1 -1
  76. data/lib/formtastic.rb +20 -10
  77. data/lib/generators/formtastic/form/form_generator.rb +10 -4
  78. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  79. data/lib/generators/formtastic/install/install_generator.rb +5 -19
  80. data/lib/generators/templates/_form.html.slim +2 -2
  81. data/lib/generators/templates/formtastic.rb +46 -25
  82. data/lib/generators/templates/input.rb +19 -0
  83. data/sample/basic_inputs.html +23 -3
  84. data/script/integration-template.rb +74 -0
  85. data/script/integration.sh +19 -0
  86. data/spec/action_class_finder_spec.rb +12 -0
  87. data/spec/actions/button_action_spec.rb +8 -8
  88. data/spec/actions/generic_action_spec.rb +92 -56
  89. data/spec/actions/input_action_spec.rb +7 -7
  90. data/spec/actions/link_action_spec.rb +10 -10
  91. data/spec/builder/custom_builder_spec.rb +36 -20
  92. data/spec/builder/error_proc_spec.rb +4 -4
  93. data/spec/builder/semantic_fields_for_spec.rb +28 -29
  94. data/spec/fast_spec_helper.rb +12 -0
  95. data/spec/generators/formtastic/form/form_generator_spec.rb +45 -32
  96. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  97. data/spec/generators/formtastic/install/install_generator_spec.rb +9 -9
  98. data/spec/helpers/action_helper_spec.rb +75 -103
  99. data/spec/helpers/actions_helper_spec.rb +17 -17
  100. data/spec/helpers/form_helper_spec.rb +84 -33
  101. data/spec/helpers/input_helper_spec.rb +333 -285
  102. data/spec/helpers/inputs_helper_spec.rb +167 -121
  103. data/spec/helpers/reflection_helper_spec.rb +3 -3
  104. data/spec/helpers/semantic_errors_helper_spec.rb +23 -23
  105. data/spec/i18n_spec.rb +26 -26
  106. data/spec/input_class_finder_spec.rb +10 -0
  107. data/spec/inputs/base/collections_spec.rb +76 -0
  108. data/spec/inputs/base/validations_spec.rb +480 -0
  109. data/spec/inputs/boolean_input_spec.rb +100 -65
  110. data/spec/inputs/check_boxes_input_spec.rb +200 -101
  111. data/spec/inputs/color_input_spec.rb +85 -0
  112. data/spec/inputs/country_input_spec.rb +20 -20
  113. data/spec/inputs/custom_input_spec.rb +3 -4
  114. data/spec/inputs/datalist_input_spec.rb +61 -0
  115. data/spec/inputs/date_picker_input_spec.rb +449 -0
  116. data/spec/inputs/date_select_input_spec.rb +249 -0
  117. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  118. data/spec/inputs/datetime_select_input_spec.rb +209 -0
  119. data/spec/inputs/email_input_spec.rb +5 -5
  120. data/spec/inputs/file_input_spec.rb +6 -6
  121. data/spec/inputs/hidden_input_spec.rb +22 -35
  122. data/spec/inputs/include_blank_spec.rb +11 -11
  123. data/spec/inputs/label_spec.rb +62 -25
  124. data/spec/inputs/number_input_spec.rb +112 -112
  125. data/spec/inputs/password_input_spec.rb +5 -5
  126. data/spec/inputs/phone_input_spec.rb +5 -5
  127. data/spec/inputs/placeholder_spec.rb +6 -6
  128. data/spec/inputs/radio_input_spec.rb +99 -55
  129. data/spec/inputs/range_input_spec.rb +66 -66
  130. data/spec/inputs/readonly_spec.rb +50 -0
  131. data/spec/inputs/search_input_spec.rb +5 -5
  132. data/spec/inputs/select_input_spec.rb +170 -170
  133. data/spec/inputs/string_input_spec.rb +68 -16
  134. data/spec/inputs/text_input_spec.rb +16 -16
  135. data/spec/inputs/time_picker_input_spec.rb +455 -0
  136. data/spec/inputs/time_select_input_spec.rb +261 -0
  137. data/spec/inputs/time_zone_input_spec.rb +54 -28
  138. data/spec/inputs/url_input_spec.rb +5 -5
  139. data/spec/inputs/with_options_spec.rb +7 -7
  140. data/spec/localizer_spec.rb +39 -17
  141. data/spec/namespaced_class_finder_spec.rb +79 -0
  142. data/spec/schema.rb +21 -0
  143. data/spec/spec_helper.rb +254 -221
  144. data/spec/support/custom_macros.rb +128 -95
  145. data/spec/support/shared_examples.rb +12 -0
  146. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  147. data/spec/support/test_environment.rb +26 -10
  148. metadata +177 -238
  149. data/.travis.yml +0 -8
  150. data/Appraisals +0 -11
  151. data/CHANGELOG +0 -371
  152. data/gemfiles/rails-3.0.gemfile +0 -7
  153. data/gemfiles/rails-3.1.gemfile +0 -7
  154. data/gemfiles/rails-3.2.gemfile +0 -7
  155. data/lib/formtastic/helpers/buttons_helper.rb +0 -310
  156. data/lib/formtastic/inputs/base/grouped_collections.rb +0 -77
  157. data/lib/formtastic/util.rb +0 -25
  158. data/lib/tasks/verify_rcov.rb +0 -44
  159. data/spec/helpers/buttons_helper_spec.rb +0 -166
  160. data/spec/helpers/commit_button_helper_spec.rb +0 -530
  161. data/spec/inputs/date_input_spec.rb +0 -227
  162. data/spec/inputs/datetime_input_spec.rb +0 -185
  163. data/spec/inputs/time_input_spec.rb +0 -267
  164. data/spec/support/deferred_garbage_collection.rb +0 -21
@@ -0,0 +1,99 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="time">` wrapped in the standard
5
+ # `<li>` wrapper. This is an alternative to `:time_select` for `:date`, `:time`, `:datetime`
6
+ # database columns. You can use this input with `:as => :time_picker`.
7
+ #
8
+ # *Please note:* Formtastic only provides suitable markup for a date picker, but does not supply
9
+ # any additional CSS or Javascript to render calendar-style date pickers. Browsers that support
10
+ # this input type (such as Mobile Webkit and Opera on the desktop) will render a native widget.
11
+ # Browsers that don't will default to a plain text field`<input type="text">` and can be
12
+ # poly-filled with some Javascript and a UI library of your choice.
13
+ #
14
+ # @example Full form context and output
15
+ #
16
+ # <%= semantic_form_for(@post) do |f| %>
17
+ # <%= f.inputs do %>
18
+ # <%= f.input :publish_at, :as => :time_picker %>
19
+ # <% end %>
20
+ # <% end %>
21
+ #
22
+ # <form...>
23
+ # <fieldset>
24
+ # <ol>
25
+ # <li class="string">
26
+ # <label for="post_publish_at">First name</label>
27
+ # <input type="date" id="post_publish_at" name="post[publish_at]">
28
+ # </li>
29
+ # </ol>
30
+ # </fieldset>
31
+ # </form>
32
+ #
33
+ # @example Setting the size (defaults to 5 for HH:MM)
34
+ # <%= f.input :publish_at, :as => :time_picker, :size => 20 %>
35
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :size => 20 } %>
36
+ #
37
+ # @example Setting the maxlength (defaults to 5 for HH:MM)
38
+ # <%= f.input :publish_at, :as => :time_picker, :maxlength => 20 %>
39
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :maxlength => 20 } %>
40
+ #
41
+ # @example Setting the value (defaults to HH:MM for Date and Time objects, otherwise renders string)
42
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :value => "14:14" } %>
43
+ #
44
+ # @example Setting the step attribute (defaults to 60)
45
+ # <%= f.input :publish_at, :as => :time_picker, :step => 120 %>
46
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :step => 120 } %>
47
+ #
48
+ # @example Setting the step attribute with a macro
49
+ # <%= f.input :publish_at, :as => :time_picker, :step => :second %>
50
+ # <%= f.input :publish_at, :as => :time_picker, :step => :minute %>
51
+ # <%= f.input :publish_at, :as => :time_picker, :step => :quarter_hour %>
52
+ # <%= f.input :publish_at, :as => :time_picker, :step => :fifteen_minutes %>
53
+ # <%= f.input :publish_at, :as => :time_picker, :step => :half_hour %>
54
+ # <%= f.input :publish_at, :as => :time_picker, :step => :thirty_minutes %>
55
+ # <%= f.input :publish_at, :as => :time_picker, :step => :hour %>
56
+ # <%= f.input :publish_at, :as => :time_picker, :step => :sixty_minutes %>
57
+ #
58
+ # @example Setting the min attribute
59
+ # <%= f.input :publish_at, :as => :time_picker, :min => "09:00" %>
60
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :min => "01:00" } %>
61
+ #
62
+ # @example Setting the max attribute
63
+ # <%= f.input :publish_at, :as => :time_picker, :max => "18:00" %>
64
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :max => "18:00" } %>
65
+ #
66
+ # @example Setting the placeholder attribute
67
+ # <%= f.input :publish_at, :as => :time_picker, :placeholder => "HH:MM" %>
68
+ # <%= f.input :publish_at, :as => :time_picker, :input_html => { :placeholder => "HH:MM" } %>
69
+ #
70
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
71
+ class TimePickerInput
72
+ include Base
73
+ include Base::Stringish
74
+ include Base::DatetimePickerish
75
+
76
+ def html_input_type
77
+ "time"
78
+ end
79
+
80
+ def default_size
81
+ 5
82
+ end
83
+
84
+ def value
85
+ return options[:input_html][:value] if options[:input_html] && options[:input_html].key?(:value)
86
+ val = object.send(method)
87
+ return "00:00" if val.is_a?(Date)
88
+ return val.strftime("%H:%M") if val.is_a?(Time)
89
+ return val if val.nil?
90
+ val.to_s
91
+ end
92
+
93
+ def default_step
94
+ 60
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -6,7 +6,7 @@ module Formtastic
6
6
  # own `time_select`.
7
7
  #
8
8
  # @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
9
- class TimeInput
9
+ class TimeSelectInput
10
10
  include Base
11
11
  include Base::Timeish
12
12
 
@@ -15,8 +15,12 @@ module Formtastic
15
15
  time_fragments
16
16
  end
17
17
 
18
+ def value_or_default_value
19
+ value ? value : Time.current
20
+ end
21
+
18
22
  def fragment_value(fragment)
19
- value ? value.send(fragment) : ""
23
+ value_or_default_value.send(fragment)
20
24
  end
21
25
 
22
26
  def hidden_fragments
@@ -28,9 +28,19 @@ module Formtastic
28
28
  #
29
29
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
30
30
  #
31
- # @todo document :priority_zones option
32
- # @todo configurable default :priority_zones?
33
- class TimeZoneInput
31
+ # The priority_zones option:
32
+ # Since this input actually uses Rails' `time_zone_select` helper, the :priority_zones
33
+ # option needs to be an array of ActiveSupport::TimeZone objects.
34
+ #
35
+ # And you can configure default value using
36
+ #
37
+ # ```
38
+ # Formtastic::FormBuilder.priority_time_zones = [timezone1, timezone2]
39
+ # ```
40
+ #
41
+ # See http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/time_zone_select for more information.
42
+ #
43
+ class TimeZoneInput
34
44
  include Base
35
45
 
36
46
  def to_html
@@ -39,10 +49,10 @@ module Formtastic
39
49
  builder.time_zone_select(method, priority_zones, input_options, input_html_options)
40
50
  end
41
51
  end
42
-
52
+
43
53
  def priority_zones
44
- options[:priority_zones] || [] # TODO config?
54
+ options[:priority_zones] || Formtastic::FormBuilder.priority_time_zones
45
55
  end
46
56
  end
47
57
  end
48
- end
58
+ end
@@ -4,28 +4,39 @@ module Formtastic
4
4
 
5
5
  autoload :Base
6
6
  autoload :Basic
7
- autoload :BooleanInput
8
- autoload :CheckBoxesInput
9
- autoload :CountryInput
10
- autoload :DateInput
11
- autoload :DatetimeInput
12
- autoload :EmailInput
13
- autoload :FileInput
14
- autoload :HiddenInput
15
- autoload :NumberInput
16
- autoload :NumericInput
17
- autoload :PasswordInput
18
- autoload :PhoneInput
19
- autoload :RadioInput
20
- autoload :RangeInput
21
- autoload :SearchInput
22
- autoload :SelectInput
23
- autoload :StringInput
24
- autoload :TextInput
25
- autoload :TimeInput
26
- autoload :TimeZoneInput
27
7
  autoload :Timeish
28
- autoload :UrlInput
8
+
9
+ eager_autoload do
10
+ autoload :BooleanInput
11
+ autoload :CheckBoxesInput
12
+ autoload :ColorInput
13
+ autoload :CountryInput
14
+ autoload :DatalistInput
15
+ autoload :DateInput
16
+ autoload :DatePickerInput
17
+ autoload :DatetimePickerInput
18
+ autoload :DateSelectInput
19
+ autoload :DatetimeInput
20
+ autoload :DatetimeSelectInput
21
+ autoload :EmailInput
22
+ autoload :FileInput
23
+ autoload :HiddenInput
24
+ autoload :NumberInput
25
+ autoload :NumericInput
26
+ autoload :PasswordInput
27
+ autoload :PhoneInput
28
+ autoload :RadioInput
29
+ autoload :RangeInput
30
+ autoload :SearchInput
31
+ autoload :SelectInput
32
+ autoload :StringInput
33
+ autoload :TextInput
34
+ autoload :TimeInput
35
+ autoload :TimePickerInput
36
+ autoload :TimeSelectInput
37
+ autoload :TimeZoneInput
38
+ autoload :UrlInput
39
+ end
29
40
  end
30
41
  end
31
42
 
@@ -7,7 +7,7 @@ module Formtastic
7
7
 
8
8
  protected
9
9
 
10
- def localized_string(key, value, type, options = {}) #:nodoc:
10
+ def localized_string(key, value, type, options = {}) # @private
11
11
  current_builder = respond_to?(:builder) ? builder : self
12
12
  localizer = Formtastic::FormBuilder.i18n_localizer.new(current_builder)
13
13
  localizer.localize(key, value, type, options)
@@ -1,5 +1,5 @@
1
1
  module Formtastic
2
- # Implementation for looking up localized values within Formtastic using I18n, if no
2
+ # Implementation for looking up localized values within Formtastic using I18n, if no
3
3
  # explicit value (like the `:label` option) is set and I18n-lookups are enabled in the
4
4
  # configuration.
5
5
  #
@@ -28,50 +28,50 @@ module Formtastic
28
28
  def get(key)
29
29
  cache[key]
30
30
  end
31
-
31
+
32
32
  def has_key?(key)
33
33
  cache.has_key?(key)
34
34
  end
35
-
35
+
36
36
  def set(key, result)
37
37
  cache[key] = result
38
38
  end
39
-
39
+
40
40
  def cache
41
41
  @cache ||= {}
42
42
  end
43
-
43
+
44
44
  def clear!
45
45
  cache.clear
46
46
  end
47
47
  end
48
-
48
+
49
49
  attr_accessor :builder
50
-
50
+
51
51
  def self.cache
52
52
  @cache ||= Cache.new
53
53
  end
54
-
54
+
55
55
  def initialize(current_builder)
56
- self.builder = current_builder
56
+ self.builder = current_builder
57
57
  end
58
58
 
59
- def localize(key, value, type, options = {}) #:nodoc:
59
+ def localize(key, value, type, options = {}) # @private
60
60
  key = value if value.is_a?(::Symbol)
61
-
61
+
62
62
  if value.is_a?(::String)
63
63
  escape_html_entities(value)
64
64
  else
65
65
  use_i18n = value.nil? ? i18n_lookups_by_default : (value != false)
66
66
  use_cache = i18n_cache_lookups
67
67
  cache = self.class.cache
68
-
68
+
69
69
  if use_i18n
70
70
  model_name, nested_model_name = normalize_model_name(builder.model_name.underscore)
71
71
 
72
72
  action_name = builder.template.params[:action].to_s rescue ''
73
73
  attribute_name = key.to_s
74
-
74
+
75
75
  # look in the cache first
76
76
  if use_cache
77
77
  cache_key = [::I18n.locale, action_name, model_name, nested_model_name, attribute_name, key, value, type, options]
@@ -86,7 +86,7 @@ module Formtastic
86
86
  i18n_path.gsub!('%{model}', model_name)
87
87
  i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
88
88
  i18n_path.gsub!('%{attribute}', attribute_name)
89
- i18n_path.gsub!('..', '.')
89
+ i18n_path.tr!('..', '.')
90
90
  i18n_path.to_sym
91
91
  end
92
92
  defaults << ''
@@ -101,9 +101,9 @@ module Formtastic
101
101
  # This is effectively what Rails label helper does for i18n lookup
102
102
  options[:scope] = [:helpers, type]
103
103
  options[:default] = defaults
104
- i18n_value = ::I18n.t(default_key, options)
104
+ i18n_value = ::I18n.t(default_key, **options)
105
105
  end
106
-
106
+
107
107
  # save the result to the cache
108
108
  result = (i18n_value.is_a?(::String) && i18n_value.present?) ? escape_html_entities(i18n_value) : nil
109
109
  cache.set(cache_key, result) if use_cache
@@ -115,14 +115,14 @@ module Formtastic
115
115
  protected
116
116
 
117
117
  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
118
+ if name =~ /(.+)\[(.+)\]/
119
+ # Nested builder case with :post rather than @post
120
+ # TODO: check if this is no longer required with a minimum of Rails 4.1
123
121
  [$1, $2]
122
+ elsif builder.respond_to?(:options) && builder.options.key?(:as)
123
+ [builder.options[:as].to_s]
124
124
  elsif builder.respond_to?(:options) && builder.options.key?(:parent_builder)
125
- # Rails 3.0 nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
125
+ # Rails 3.0+ nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
126
126
  [builder.options[:parent_builder].object_name.to_s, name]
127
127
  else
128
128
  # Non-nested case
@@ -130,7 +130,7 @@ module Formtastic
130
130
  end
131
131
  end
132
132
 
133
- def escape_html_entities(string) #:nodoc:
133
+ def escape_html_entities(string) # @private
134
134
  if (builder.escape_html_entities_in_hints_and_labels) ||
135
135
  (self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
136
136
  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
@@ -141,7 +141,7 @@ module Formtastic
141
141
  def i18n_lookups_by_default
142
142
  builder.i18n_lookups_by_default
143
143
  end
144
-
144
+
145
145
  def i18n_cache_lookups
146
146
  builder.i18n_cache_lookups
147
147
  end
@@ -0,0 +1,99 @@
1
+ module Formtastic
2
+ # This class implements class resolution in a namespace chain. It
3
+ # is used both by Formtastic::Helpers::InputHelper and
4
+ # Formtastic::Helpers::ActionHelper to look up action and input classes.
5
+ #
6
+ # @example Implementing own class finder
7
+ # # You can implement own class finder that for example prefixes the class name or uses custom module.
8
+ # class MyInputClassFinder < Formtastic::NamespacedClassFinder
9
+ # def initialize(namespaces)
10
+ # super [MyNamespace] + namespaces # first lookup in MyNamespace then the defaults
11
+ # end
12
+ #
13
+ # private
14
+ #
15
+ # def class_name(as)
16
+ # "My#{super}Input" # for example MyStringInput
17
+ # end
18
+ # end
19
+ #
20
+ # # in config/initializers/formtastic.rb
21
+ # Formtastic::FormBuilder.input_class_finder = MyInputClassFinder
22
+ #
23
+
24
+ class NamespacedClassFinder
25
+ attr_reader :namespaces # @private
26
+
27
+ # @private
28
+ class NotFoundError < NameError
29
+ end
30
+
31
+ def self.use_const_defined?
32
+ defined?(Rails) && ::Rails.application && ::Rails.application.config.respond_to?(:eager_load) && ::Rails.application.config.eager_load
33
+ end
34
+
35
+ # @param namespaces [Array<Module>]
36
+ def initialize(namespaces)
37
+ @namespaces = namespaces.flatten
38
+ @cache = {}
39
+ end
40
+
41
+ # Looks up the given reference in the configured namespaces.
42
+ #
43
+ # Two finder methods are provided, one for development tries to
44
+ # reference the constant directly, triggering Rails' autoloading
45
+ # const_missing machinery; the second one instead for production
46
+ # checks with .const_defined before referencing the constant.
47
+ #
48
+ def find(as)
49
+ @cache[as] ||= resolve(as)
50
+ end
51
+
52
+ def resolve(as)
53
+ class_name = class_name(as)
54
+
55
+ finder(class_name) or raise NotFoundError, "class #{class_name}"
56
+ end
57
+
58
+ # Converts symbol to class name
59
+ # Overridden in subclasses to create `StringInput` and `ButtonAction`
60
+ # @example
61
+ # class_name(:string) == "String"
62
+
63
+ def class_name(as)
64
+ as.to_s.camelize
65
+ end
66
+
67
+ private
68
+
69
+ if use_const_defined?
70
+ def finder(class_name) # @private
71
+ find_with_const_defined(class_name)
72
+ end
73
+ else
74
+ def finder(class_name) # @private
75
+ find_by_trying(class_name)
76
+ end
77
+ end
78
+
79
+ # Looks up the given class name in the configured namespaces in order,
80
+ # returning the first one that has the class name constant defined.
81
+ def find_with_const_defined(class_name)
82
+ @namespaces.find do |namespace|
83
+ if namespace.const_defined?(class_name)
84
+ break namespace.const_get(class_name)
85
+ end
86
+ end
87
+ end
88
+
89
+ # Use auto-loading in development environment
90
+ def find_by_trying(class_name)
91
+ @namespaces.find do |namespace|
92
+ begin
93
+ break namespace.const_get(class_name)
94
+ rescue NameError
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  module Formtastic
2
- VERSION = "2.1.0"
2
+ VERSION = "4.0.0"
3
3
  end
data/lib/formtastic.rb CHANGED
@@ -4,30 +4,40 @@ require 'formtastic/engine' if defined?(::Rails)
4
4
  module Formtastic
5
5
  extend ActiveSupport::Autoload
6
6
 
7
- autoload :FormBuilder
8
7
  autoload :Helpers
9
8
  autoload :HtmlAttributes
10
- autoload :I18n
11
- autoload :Inputs
12
- autoload :Actions
13
9
  autoload :LocalizedString
14
10
  autoload :Localizer
15
- autoload :Util
16
-
17
- # @private
11
+ autoload :NamespacedClassFinder
12
+ autoload :InputClassFinder
13
+ autoload :ActionClassFinder
14
+ autoload :Deprecation
15
+
16
+ eager_autoload do
17
+ autoload :I18n
18
+ autoload :FormBuilder
19
+ autoload :Inputs
20
+ autoload :Actions
21
+ end
22
+
23
+ # @public
18
24
  class UnknownInputError < NameError
19
25
  end
20
-
26
+
21
27
  # @private
22
28
  class UnknownActionError < NameError
23
29
  end
24
-
30
+
25
31
  # @private
26
32
  class PolymorphicInputWithoutCollectionError < ArgumentError
27
33
  end
28
-
34
+
29
35
  # @private
30
36
  class UnsupportedMethodForAction < ArgumentError
31
37
  end
32
38
 
39
+ # @private
40
+ class UnsupportedEnumCollection < NameError
41
+ end
42
+
33
43
  end
@@ -4,14 +4,20 @@ module Formtastic
4
4
  # files without confirmation.
5
5
  #
6
6
  # @example
7
+ # !!!shell
7
8
  # $ rails generate formtastic:form Post
8
9
  # @example Copy the partial code to the pasteboard rather than generating a partial
10
+ # !!!shell
9
11
  # $ rails generate formtastic:form Post --copy
10
- # @example Return HAML output instead of default template engine
11
- # $ rails generate formtastic:form Post --haml
12
+ # @example Return HAML or Slim output instead of default ERB
13
+ # !!!shell
14
+ # $ rails generate formtastic:form Post --template-engine haml
15
+ # $ rails generate formtastic:form Post --template-engine slim
12
16
  # @example Generate a form for specific model attributes
17
+ # !!!shell
13
18
  # $ rails generate formtastic:form Post title:string body:text
14
19
  # @example Generate a form for a specific controller
20
+ # !!!shell
15
21
  # $ rails generate formtastic:form Post --controller admin/posts
16
22
  class FormGenerator < Rails::Generators::NamedBase
17
23
  desc "Generates a Formtastic form partial based on an existing model."
@@ -69,7 +75,7 @@ module Formtastic
69
75
  # Skips Active Record Timestamps.
70
76
  def content_columns
71
77
  model.content_columns.select do |column|
72
- !Formtastic::Helpers::InputsHelper::SKIPPED_COLUMNS.include? column.name.to_sym
78
+ !Formtastic::FormBuilder.skipped_columns.include? column.name.to_sym
73
79
  end
74
80
  end
75
81
 
@@ -102,4 +108,4 @@ module Formtastic
102
108
  end
103
109
 
104
110
  end
105
- end
111
+ end
@@ -0,0 +1,46 @@
1
+ module Formtastic
2
+
3
+ # Modify existing inputs, subclass them, or create your own from scratch.
4
+ # @example
5
+ # !!!shell
6
+ # $ rails generate formtastic:input HatSize
7
+
8
+ # @example Define input name using underscore convention
9
+ # !!!shell
10
+ # $ rails generate formtastic:input hat_size
11
+
12
+ # @example Override an existing input behavior
13
+ # !!!shell
14
+ # $ rails generate formtastic:input string --extend
15
+
16
+ # @example Extend an existing input behavior
17
+ # !!!shell
18
+ # $ rails generate formtastic:input FlexibleText --extend string
19
+ class InputGenerator < Rails::Generators::NamedBase
20
+
21
+ argument :name, :type => :string, :required => true, :banner => 'FILE_NAME'
22
+
23
+ source_root File.expand_path('../../../templates', __FILE__)
24
+
25
+ class_option :extend
26
+
27
+ def create
28
+ normalize_file_name
29
+ define_extension_sentence
30
+ template "input.rb", "app/inputs/#{name.underscore}_input.rb"
31
+ end
32
+
33
+ protected
34
+
35
+ def normalize_file_name
36
+ name.chomp!("Input") if name.ends_with?("Input")
37
+ name.chomp!("_input") if name.ends_with?("_input")
38
+ name.chomp!("input") if name.ends_with?("input")
39
+ end
40
+
41
+ def define_extension_sentence
42
+ @extension_sentence = "< Formtastic::Inputs::#{name.camelize}Input" if options[:extend] == "extend"
43
+ @extension_sentence ||= "< Formtastic::Inputs::#{options[:extend].camelize}Input" if options[:extend]
44
+ end
45
+ end
46
+ end
@@ -1,32 +1,18 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Formtastic
4
- # Copies formtastic.css to public/stylesheets/ (Rails 3.0.x only) and a config initializer
5
- # to config/initializers/formtastic.rb (all Rails versions).
4
+ # Copies a config initializer to config/initializers/formtastic.rb
6
5
  #
7
6
  # @example
7
+ # !!!shell
8
8
  # $ rails generate formtastic:install
9
- #
10
- # @todo Test with Rails 3.0
11
9
  class InstallGenerator < Rails::Generators::Base
12
10
  source_root File.expand_path('../../../templates', __FILE__)
13
11
  class_option :template_engine
14
12
 
15
- if ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR >= 1
16
- # Rails 3.1 has the asset pipeline, no need to copy CSS files any more
17
- desc "Copies a config initializer to config/initializers/formtastic.rb"
18
- def copy_files
19
- copy_file 'formtastic.rb', 'config/initializers/formtastic.rb'
20
- end
21
- else
22
- # Rails 3.0 doesn't have an asset pipeline, so we copy in CSS too
23
- desc "Copies some CSS files to public/stylesheets/ and a config initializer to config/initializers/formtastic.rb"
24
- def copy_files
25
- template 'formtastic.rb', 'config/initializers/formtastic.rb'
26
- template '../../../app/assets/stylesheets/formtastic.css', 'public/stylesheets/formtastic.css'
27
- template '../../../app/assets/stylesheets/formtastic_ie6.css', 'public/stylesheets/formtastic_ie6.css'
28
- template '../../../app/assets/stylesheets/formtastic_ie7.css', 'public/stylesheets/formtastic_ie7.css'
29
- end
13
+ desc "Copies a config initializer to config/initializers/formtastic.rb"
14
+ def copy_files
15
+ copy_file 'formtastic.rb', 'config/initializers/formtastic.rb'
30
16
  end
31
17
 
32
18
  def copy_scaffold_template
@@ -4,5 +4,5 @@
4
4
  = f.input :<%= attribute.name %>
5
5
  <%- end -%>
6
6
 
7
- = f.buttons do
8
- = f.commit_button
7
+ = f.actions do
8
+ = f.action :submit, as: :input