mega-safe-tool 0.0.1

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 (171) hide show
  1. checksums.yaml +7 -0
  2. data/formtastic-6.0.0/Appraisals +23 -0
  3. data/formtastic-6.0.0/CONTRIBUTING.md +27 -0
  4. data/formtastic-6.0.0/Gemfile +8 -0
  5. data/formtastic-6.0.0/MIT-LICENSE +20 -0
  6. data/formtastic-6.0.0/README.md +600 -0
  7. data/formtastic-6.0.0/RELEASE_PROCESS +7 -0
  8. data/formtastic-6.0.0/Rakefile +54 -0
  9. data/formtastic-6.0.0/bin/appraisal +8 -0
  10. data/formtastic-6.0.0/formtastic.gemspec +45 -0
  11. data/formtastic-6.0.0/gemfiles/rails_72.gemfile +9 -0
  12. data/formtastic-6.0.0/gemfiles/rails_80.gemfile +9 -0
  13. data/formtastic-6.0.0/gemfiles/rails_81.gemfile +9 -0
  14. data/formtastic-6.0.0/gemfiles/rails_edge.gemfile +9 -0
  15. data/formtastic-6.0.0/lib/formtastic/action_class_finder.rb +19 -0
  16. data/formtastic-6.0.0/lib/formtastic/actions/base.rb +157 -0
  17. data/formtastic-6.0.0/lib/formtastic/actions/button_action.rb +68 -0
  18. data/formtastic-6.0.0/lib/formtastic/actions/buttonish.rb +18 -0
  19. data/formtastic-6.0.0/lib/formtastic/actions/input_action.rb +71 -0
  20. data/formtastic-6.0.0/lib/formtastic/actions/link_action.rb +89 -0
  21. data/formtastic-6.0.0/lib/formtastic/actions.rb +15 -0
  22. data/formtastic-6.0.0/lib/formtastic/deprecation.rb +6 -0
  23. data/formtastic-6.0.0/lib/formtastic/form_builder.rb +109 -0
  24. data/formtastic-6.0.0/lib/formtastic/helpers/action_helper.rb +115 -0
  25. data/formtastic-6.0.0/lib/formtastic/helpers/actions_helper.rb +169 -0
  26. data/formtastic-6.0.0/lib/formtastic/helpers/enum.rb +14 -0
  27. data/formtastic-6.0.0/lib/formtastic/helpers/errors_helper.rb +139 -0
  28. data/formtastic-6.0.0/lib/formtastic/helpers/fieldset_wrapper.rb +85 -0
  29. data/formtastic-6.0.0/lib/formtastic/helpers/file_column_detection.rb +17 -0
  30. data/formtastic-6.0.0/lib/formtastic/helpers/form_helper.rb +204 -0
  31. data/formtastic-6.0.0/lib/formtastic/helpers/input_helper.rb +349 -0
  32. data/formtastic-6.0.0/lib/formtastic/helpers/inputs_helper.rb +421 -0
  33. data/formtastic-6.0.0/lib/formtastic/helpers/reflection.rb +38 -0
  34. data/formtastic-6.0.0/lib/formtastic/helpers.rb +16 -0
  35. data/formtastic-6.0.0/lib/formtastic/html_attributes.rb +33 -0
  36. data/formtastic-6.0.0/lib/formtastic/i18n.rb +33 -0
  37. data/formtastic-6.0.0/lib/formtastic/input_class_finder.rb +19 -0
  38. data/formtastic-6.0.0/lib/formtastic/inputs/base/aria.rb +26 -0
  39. data/formtastic-6.0.0/lib/formtastic/inputs/base/associations.rb +32 -0
  40. data/formtastic-6.0.0/lib/formtastic/inputs/base/choices.rb +109 -0
  41. data/formtastic-6.0.0/lib/formtastic/inputs/base/collections.rb +158 -0
  42. data/formtastic-6.0.0/lib/formtastic/inputs/base/database.rb +20 -0
  43. data/formtastic-6.0.0/lib/formtastic/inputs/base/datetime_pickerish.rb +86 -0
  44. data/formtastic-6.0.0/lib/formtastic/inputs/base/errors.rb +59 -0
  45. data/formtastic-6.0.0/lib/formtastic/inputs/base/fileish.rb +24 -0
  46. data/formtastic-6.0.0/lib/formtastic/inputs/base/hints.rb +32 -0
  47. data/formtastic-6.0.0/lib/formtastic/inputs/base/html.rb +54 -0
  48. data/formtastic-6.0.0/lib/formtastic/inputs/base/labelling.rb +53 -0
  49. data/formtastic-6.0.0/lib/formtastic/inputs/base/naming.rb +43 -0
  50. data/formtastic-6.0.0/lib/formtastic/inputs/base/numeric.rb +51 -0
  51. data/formtastic-6.0.0/lib/formtastic/inputs/base/options.rb +18 -0
  52. data/formtastic-6.0.0/lib/formtastic/inputs/base/placeholder.rb +18 -0
  53. data/formtastic-6.0.0/lib/formtastic/inputs/base/stringish.rb +39 -0
  54. data/formtastic-6.0.0/lib/formtastic/inputs/base/timeish.rb +246 -0
  55. data/formtastic-6.0.0/lib/formtastic/inputs/base/validations.rb +250 -0
  56. data/formtastic-6.0.0/lib/formtastic/inputs/base/wrapping.rb +51 -0
  57. data/formtastic-6.0.0/lib/formtastic/inputs/base.rb +79 -0
  58. data/formtastic-6.0.0/lib/formtastic/inputs/boolean_input.rb +119 -0
  59. data/formtastic-6.0.0/lib/formtastic/inputs/check_boxes_input.rb +198 -0
  60. data/formtastic-6.0.0/lib/formtastic/inputs/color_input.rb +42 -0
  61. data/formtastic-6.0.0/lib/formtastic/inputs/country_input.rb +90 -0
  62. data/formtastic-6.0.0/lib/formtastic/inputs/datalist_input.rb +42 -0
  63. data/formtastic-6.0.0/lib/formtastic/inputs/date_picker_input.rb +94 -0
  64. data/formtastic-6.0.0/lib/formtastic/inputs/date_select_input.rb +35 -0
  65. data/formtastic-6.0.0/lib/formtastic/inputs/datetime_picker_input.rb +104 -0
  66. data/formtastic-6.0.0/lib/formtastic/inputs/datetime_select_input.rb +13 -0
  67. data/formtastic-6.0.0/lib/formtastic/inputs/email_input.rb +42 -0
  68. data/formtastic-6.0.0/lib/formtastic/inputs/file_input.rb +43 -0
  69. data/formtastic-6.0.0/lib/formtastic/inputs/hidden_input.rb +63 -0
  70. data/formtastic-6.0.0/lib/formtastic/inputs/number_input.rb +89 -0
  71. data/formtastic-6.0.0/lib/formtastic/inputs/password_input.rb +42 -0
  72. data/formtastic-6.0.0/lib/formtastic/inputs/phone_input.rb +43 -0
  73. data/formtastic-6.0.0/lib/formtastic/inputs/radio_input.rb +164 -0
  74. data/formtastic-6.0.0/lib/formtastic/inputs/range_input.rb +96 -0
  75. data/formtastic-6.0.0/lib/formtastic/inputs/search_input.rb +42 -0
  76. data/formtastic-6.0.0/lib/formtastic/inputs/select_input.rb +236 -0
  77. data/formtastic-6.0.0/lib/formtastic/inputs/string_input.rb +37 -0
  78. data/formtastic-6.0.0/lib/formtastic/inputs/text_input.rb +49 -0
  79. data/formtastic-6.0.0/lib/formtastic/inputs/time_picker_input.rb +100 -0
  80. data/formtastic-6.0.0/lib/formtastic/inputs/time_select_input.rb +39 -0
  81. data/formtastic-6.0.0/lib/formtastic/inputs/time_zone_input.rb +59 -0
  82. data/formtastic-6.0.0/lib/formtastic/inputs/url_input.rb +42 -0
  83. data/formtastic-6.0.0/lib/formtastic/inputs.rb +43 -0
  84. data/formtastic-6.0.0/lib/formtastic/localized_string.rb +18 -0
  85. data/formtastic-6.0.0/lib/formtastic/localizer.rb +151 -0
  86. data/formtastic-6.0.0/lib/formtastic/namespaced_class_finder.rb +98 -0
  87. data/formtastic-6.0.0/lib/formtastic/version.rb +4 -0
  88. data/formtastic-6.0.0/lib/formtastic.rb +48 -0
  89. data/formtastic-6.0.0/lib/generators/formtastic/form/form_generator.rb +111 -0
  90. data/formtastic-6.0.0/lib/generators/formtastic/input/input_generator.rb +47 -0
  91. data/formtastic-6.0.0/lib/generators/formtastic/install/install_generator.rb +23 -0
  92. data/formtastic-6.0.0/lib/generators/formtastic/stylesheets/stylesheets_generator.rb +15 -0
  93. data/formtastic-6.0.0/lib/generators/templates/_form.html.erb +11 -0
  94. data/formtastic-6.0.0/lib/generators/templates/_form.html.haml +8 -0
  95. data/formtastic-6.0.0/lib/generators/templates/_form.html.slim +8 -0
  96. data/formtastic-6.0.0/lib/generators/templates/formtastic.css +289 -0
  97. data/formtastic-6.0.0/lib/generators/templates/formtastic.rb +118 -0
  98. data/formtastic-6.0.0/lib/generators/templates/input.rb +19 -0
  99. data/formtastic-6.0.0/lib/locale/en.yml +10 -0
  100. data/formtastic-6.0.0/sample/basic_inputs.html +224 -0
  101. data/formtastic-6.0.0/sample/config.ru +69 -0
  102. data/formtastic-6.0.0/sample/index.html +14 -0
  103. data/formtastic-6.0.0/script/integration-template.rb +76 -0
  104. data/formtastic-6.0.0/script/integration.sh +32 -0
  105. data/formtastic-6.0.0/spec/action_class_finder_spec.rb +12 -0
  106. data/formtastic-6.0.0/spec/actions/button_action_spec.rb +63 -0
  107. data/formtastic-6.0.0/spec/actions/generic_action_spec.rb +521 -0
  108. data/formtastic-6.0.0/spec/actions/input_action_spec.rb +59 -0
  109. data/formtastic-6.0.0/spec/actions/link_action_spec.rb +92 -0
  110. data/formtastic-6.0.0/spec/builder/custom_builder_spec.rb +132 -0
  111. data/formtastic-6.0.0/spec/builder/error_proc_spec.rb +27 -0
  112. data/formtastic-6.0.0/spec/builder/semantic_fields_for_spec.rb +213 -0
  113. data/formtastic-6.0.0/spec/fast_spec_helper.rb +12 -0
  114. data/formtastic-6.0.0/spec/generators/formtastic/form/form_generator_spec.rb +132 -0
  115. data/formtastic-6.0.0/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
  116. data/formtastic-6.0.0/spec/generators/formtastic/install/install_generator_spec.rb +48 -0
  117. data/formtastic-6.0.0/spec/generators/formtastic/stylesheets/stylesheets_generator_spec.rb +22 -0
  118. data/formtastic-6.0.0/spec/helpers/action_helper_spec.rb +338 -0
  119. data/formtastic-6.0.0/spec/helpers/actions_helper_spec.rb +143 -0
  120. data/formtastic-6.0.0/spec/helpers/form_helper_spec.rb +224 -0
  121. data/formtastic-6.0.0/spec/helpers/input_helper_spec.rb +991 -0
  122. data/formtastic-6.0.0/spec/helpers/inputs_helper_spec.rb +669 -0
  123. data/formtastic-6.0.0/spec/helpers/reflection_helper_spec.rb +32 -0
  124. data/formtastic-6.0.0/spec/helpers/semantic_errors_helper_spec.rb +220 -0
  125. data/formtastic-6.0.0/spec/i18n_spec.rb +210 -0
  126. data/formtastic-6.0.0/spec/input_class_finder_spec.rb +10 -0
  127. data/formtastic-6.0.0/spec/inputs/base/collections_spec.rb +119 -0
  128. data/formtastic-6.0.0/spec/inputs/base/validations_spec.rb +481 -0
  129. data/formtastic-6.0.0/spec/inputs/boolean_input_spec.rb +254 -0
  130. data/formtastic-6.0.0/spec/inputs/check_boxes_input_spec.rb +547 -0
  131. data/formtastic-6.0.0/spec/inputs/color_input_spec.rb +85 -0
  132. data/formtastic-6.0.0/spec/inputs/country_input_spec.rb +133 -0
  133. data/formtastic-6.0.0/spec/inputs/custom_input_spec.rb +51 -0
  134. data/formtastic-6.0.0/spec/inputs/datalist_input_spec.rb +61 -0
  135. data/formtastic-6.0.0/spec/inputs/date_picker_input_spec.rb +449 -0
  136. data/formtastic-6.0.0/spec/inputs/date_select_input_spec.rb +249 -0
  137. data/formtastic-6.0.0/spec/inputs/datetime_picker_input_spec.rb +490 -0
  138. data/formtastic-6.0.0/spec/inputs/datetime_select_input_spec.rb +209 -0
  139. data/formtastic-6.0.0/spec/inputs/email_input_spec.rb +85 -0
  140. data/formtastic-6.0.0/spec/inputs/file_input_spec.rb +89 -0
  141. data/formtastic-6.0.0/spec/inputs/hidden_input_spec.rb +135 -0
  142. data/formtastic-6.0.0/spec/inputs/include_blank_spec.rb +78 -0
  143. data/formtastic-6.0.0/spec/inputs/label_spec.rb +181 -0
  144. data/formtastic-6.0.0/spec/inputs/number_input_spec.rb +815 -0
  145. data/formtastic-6.0.0/spec/inputs/password_input_spec.rb +99 -0
  146. data/formtastic-6.0.0/spec/inputs/phone_input_spec.rb +85 -0
  147. data/formtastic-6.0.0/spec/inputs/placeholder_spec.rb +71 -0
  148. data/formtastic-6.0.0/spec/inputs/radio_input_spec.rb +326 -0
  149. data/formtastic-6.0.0/spec/inputs/range_input_spec.rb +505 -0
  150. data/formtastic-6.0.0/spec/inputs/readonly_spec.rb +50 -0
  151. data/formtastic-6.0.0/spec/inputs/search_input_spec.rb +84 -0
  152. data/formtastic-6.0.0/spec/inputs/select_input_spec.rb +661 -0
  153. data/formtastic-6.0.0/spec/inputs/string_input_spec.rb +326 -0
  154. data/formtastic-6.0.0/spec/inputs/text_input_spec.rb +187 -0
  155. data/formtastic-6.0.0/spec/inputs/time_picker_input_spec.rb +455 -0
  156. data/formtastic-6.0.0/spec/inputs/time_select_input_spec.rb +261 -0
  157. data/formtastic-6.0.0/spec/inputs/time_zone_input_spec.rb +143 -0
  158. data/formtastic-6.0.0/spec/inputs/url_input_spec.rb +85 -0
  159. data/formtastic-6.0.0/spec/inputs/with_options_spec.rb +43 -0
  160. data/formtastic-6.0.0/spec/localizer_spec.rb +130 -0
  161. data/formtastic-6.0.0/spec/namespaced_class_finder_spec.rb +90 -0
  162. data/formtastic-6.0.0/spec/schema.rb +22 -0
  163. data/formtastic-6.0.0/spec/spec.opts +2 -0
  164. data/formtastic-6.0.0/spec/spec_helper.rb +466 -0
  165. data/formtastic-6.0.0/spec/support/custom_macros.rb +570 -0
  166. data/formtastic-6.0.0/spec/support/deprecation.rb +7 -0
  167. data/formtastic-6.0.0/spec/support/shared_examples.rb +13 -0
  168. data/formtastic-6.0.0/spec/support/specialized_class_finder_shared_example.rb +27 -0
  169. data/formtastic-6.0.0/spec/support/test_environment.rb +46 -0
  170. data/mega-safe-tool.gemspec +11 -0
  171. metadata +209 -0
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ module Options
6
+
7
+ def input_options
8
+ options.except(*formtastic_options)
9
+ end
10
+
11
+ def formtastic_options
12
+ [:priority_countries, :priority_zones, :member_label, :member_value, :collection, :required, :label, :as, :hint, :input_html, :value_as_class, :class]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ module Placeholder
6
+
7
+ def input_html_options
8
+ {:placeholder => placeholder_text}.merge(super)
9
+ end
10
+
11
+ def placeholder_text
12
+ localized_string(method, options[:placeholder], :placeholder)
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ module Stringish
6
+
7
+ # @abstract Override this method in your input class to describe how the input should render itself.
8
+ def to_html
9
+ input_wrapping do
10
+ label_html <<
11
+ builder.text_field(method, input_html_options)
12
+ end
13
+ end
14
+
15
+ # Overrides standard `input_html_options` to provide a `maxlength` and `size` attribute.
16
+ def input_html_options
17
+ {
18
+ :maxlength => maxlength,
19
+ :size => size
20
+ }.merge(super)
21
+ end
22
+
23
+ def size
24
+ builder.default_text_field_size
25
+ end
26
+
27
+ def maxlength
28
+ options[:input_html].try(:[], :maxlength) || limit
29
+ end
30
+
31
+ def wrapper_html_options
32
+ new_class = [super[:class], "stringish"].compact.join(" ")
33
+ super.merge(:class => new_class)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ # Timeish inputs (`:date_select`, `:datetime_select`, `:time_select`) are similar to the Rails date and time
6
+ # helpers (`date_select`, `datetime_select`, `time_select`), rendering a series of `<select>`
7
+ # tags for each fragment (year, month, day, hour, minute, seconds). The fragments are then
8
+ # re-combined to a date by ActiveRecord through multi-parameter assignment.
9
+ #
10
+ # The mark-up produced by Rails is simple but far from ideal, with no way to label the
11
+ # individual fragments for accessibility, no fieldset to group the related fields, and no
12
+ # legend describing the group. Formtastic addresses this within the standard `<li>` wrapper
13
+ # with a `<fieldset>` with a `<legend>` as a label, followed by an ordered list (`<ol>`) of
14
+ # list items (`<li>`), one for each fragment (year, month, ...). Each `<li>` fragment contains
15
+ # a `<label>` (eg "Year") for the fragment, and a `<select>` containing `<option>`s (eg a
16
+ # range of years).
17
+ #
18
+ # In the supplied formtastic.css file, the resulting mark-up is styled to appear a lot like a
19
+ # standard Rails date time select by:
20
+ #
21
+ # * styling the legend to look like the other labels (to the left hand side of the selects)
22
+ # * floating the `<li>` fragments against each other as a single line
23
+ # * hiding the `<label>` of each fragment with `display:none`
24
+ #
25
+ # @example `:date_select` input with full form context and sample HTMl output
26
+ #
27
+ # <%= semantic_form_for(@post) do |f| %>
28
+ # <%= f.inputs do %>
29
+ # ...
30
+ # <%= f.input :publish_at, :as => :date_select %>
31
+ # <% end %>
32
+ # <% end %>
33
+ #
34
+ # <form...>
35
+ # <fieldset class="inputs">
36
+ # <ol>
37
+ # <li class="date">
38
+ # <fieldset class="fragments">
39
+ # <ol class="fragments-group">
40
+ # <li class="fragment">
41
+ # <label for="post_publish_at_1i">Year</label>
42
+ # <select id="post_publish_at_1i" name="post[publish_at_1i]">...</select>
43
+ # </li>
44
+ # <li class="fragment">
45
+ # <label for="post_publish_at_2i">Month</label>
46
+ # <select id="post_publish_at_2i" name="post[publish_at_2i]">...</select>
47
+ # </li>
48
+ # <li class="fragment">
49
+ # <label for="post_publish_at_3i">Day</label>
50
+ # <select id="post_publish_at_3i" name="post[publish_at_3i]">...</select>
51
+ # </li>
52
+ # </ol>
53
+ # </fieldset>
54
+ # </li>
55
+ # </ol>
56
+ # </fieldset>
57
+ # </form>
58
+ #
59
+ #
60
+ # @example `:time_select` input
61
+ # <%= f.input :publish_at, :as => :time_select %>
62
+ #
63
+ # @example `:datetime_select` input
64
+ # <%= f.input :publish_at, :as => :datetime_select %>
65
+ #
66
+ # @example Change the labels for each fragment
67
+ # <%= f.input :publish_at, :as => :date_select, :labels => { :year => "Y", :month => "M", :day => "D" } %>
68
+ #
69
+ # @example Suppress the labels for all fragments
70
+ # <%= f.input :publish_at, :as => :date_select, :labels => false %>
71
+ #
72
+ # @example Skip a fragment (defaults to 1, skips all following fragments)
73
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_minute => true %>
74
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_hour => true %>
75
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_day => true %>
76
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_month => true %>
77
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_year => true %>
78
+ #
79
+ # @example Change the order
80
+ # <%= f.input :publish_at, :as => :date_select, :order => [:month, :day, :year] %>
81
+ #
82
+ # @example Include seconds with times (excluded by default)
83
+ # <%= f.input :publish_at, :as => :time_select, :include_seconds => true %>
84
+ #
85
+ # @example Specify if there should be a blank option at the start of each select or not. Note that, unlike select inputs, :include_blank does not accept a string value.
86
+ # <%= f.input :publish_at, :as => :time_select, :include_blank => true %>
87
+ # <%= f.input :publish_at, :as => :time_select, :include_blank => false %>
88
+ #
89
+ # @example Provide a value for the field via selected
90
+ # <%= f.input :publish_at, :as => :datetime_select, :selected => DateTime.new(2018, 10, 4, 12, 00)
91
+ #
92
+ # @todo Document i18n
93
+ # @todo Check what other Rails options are supported (`start_year`, `end_year`, `use_month_numbers`, `use_short_month`, `add_month_numbers`, `prompt`), write tests for them, and otherwise support them
94
+ # @todo Could we take the rendering from Rails' helpers and inject better HTML in and around it rather than re-inventing the whee?
95
+ module Timeish
96
+
97
+ def to_html
98
+ input_wrapping do
99
+ fragments_wrapping do
100
+ hidden_fragments <<
101
+ fragments_label <<
102
+ template.content_tag(:ol,
103
+ fragments.map do |fragment|
104
+ fragment_wrapping do
105
+ fragment_label_html(fragment) <<
106
+ fragment_input_html(fragment)
107
+ end
108
+ end.join.html_safe, # TODO is this safe?
109
+ { :class => 'fragments-group' } # TODO refactor to fragments_group_wrapping
110
+ )
111
+ end
112
+ end
113
+ end
114
+
115
+ def fragments
116
+ date_fragments + time_fragments
117
+ end
118
+
119
+ def time_fragments
120
+ options[:include_seconds] ? [:hour, :minute, :second] : [:hour, :minute]
121
+ end
122
+
123
+ def date_fragments
124
+ options[:order] || i18n_date_fragments || default_date_fragments
125
+ end
126
+
127
+ def default_date_fragments
128
+ [:year, :month, :day]
129
+ end
130
+
131
+ def fragment_wrapping(&block)
132
+ template.content_tag(:li, template.capture(&block), fragment_wrapping_html_options)
133
+ end
134
+
135
+ def fragment_wrapping_html_options
136
+ { :class => 'fragment' }
137
+ end
138
+
139
+ def fragment_label(fragment)
140
+ labels_from_options = options.key?(:labels) ? options[:labels] : {}
141
+ if !labels_from_options
142
+ ''
143
+ elsif labels_from_options.key?(fragment)
144
+ labels_from_options[fragment]
145
+ else
146
+ ::I18n.t(fragment.to_s, :default => fragment.to_s.humanize, :scope => [:datetime, :prompts])
147
+ end
148
+ end
149
+
150
+ def fragment_id(fragment)
151
+ "#{input_html_options[:id]}_#{position(fragment)}i"
152
+ end
153
+
154
+ def fragment_name(fragment)
155
+ "#{method}(#{position(fragment)}i)"
156
+ end
157
+
158
+ def fragment_label_html(fragment)
159
+ text = fragment_label(fragment)
160
+ text.blank? ? +"".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
161
+ end
162
+
163
+ def value
164
+ return input_options[:selected] if options.key?(:selected)
165
+ object.send(method) if object && object.respond_to?(method)
166
+ end
167
+
168
+ def fragment_input_html(fragment)
169
+ opts = input_options.merge(:prefix => fragment_prefix, :field_name => fragment_name(fragment), :default => value, :include_blank => include_blank?)
170
+ template.send(:"select_#{fragment}", value, opts, input_html_options.merge(:id => fragment_id(fragment)))
171
+ end
172
+
173
+ def fragment_prefix
174
+ if builder.options.key?(:index)
175
+ object_name + "[#{builder.options[:index]}]"
176
+ else
177
+ object_name
178
+ end
179
+ end
180
+
181
+ # TODO extract to BlankOptions or similar -- Select uses similar code
182
+ def include_blank?
183
+ options.key?(:include_blank) ? options[:include_blank] : builder.include_blank_for_select_by_default
184
+ end
185
+
186
+ def positions
187
+ { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
188
+ end
189
+
190
+ def position(fragment)
191
+ positions[fragment]
192
+ end
193
+
194
+ def i18n_date_fragments
195
+ order = ::I18n.t(:order, :scope => [:date])
196
+ if order.is_a?(Array)
197
+ order.map &:to_sym
198
+ else
199
+ nil
200
+ end
201
+ end
202
+
203
+ def fragments_wrapping(&block)
204
+ template.content_tag(:fieldset,
205
+ template.capture(&block).html_safe,
206
+ fragments_wrapping_html_options
207
+ )
208
+ end
209
+
210
+ def fragments_wrapping_html_options
211
+ { :class => "fragments" }
212
+ end
213
+
214
+ def fragments_label
215
+ if render_label?
216
+ template.content_tag(:legend,
217
+ builder.label(method, label_text, :for => fragment_id(fragments.first)),
218
+ :class => "label"
219
+ )
220
+ else
221
+ +"".html_safe
222
+ end
223
+ end
224
+
225
+ def fragments_inner_wrapping(&block)
226
+ template.content_tag(:ol,
227
+ template.capture(&block)
228
+ )
229
+ end
230
+
231
+ def hidden_fragments
232
+ +"".html_safe
233
+ end
234
+
235
+ def hidden_field_name(fragment)
236
+ if builder.options.key?(:index)
237
+ "#{object_name}[#{builder.options[:index]}][#{fragment_name(fragment)}]"
238
+ else
239
+ "#{object_name}[#{fragment_name(fragment)}]"
240
+ end
241
+ end
242
+
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ module Validations
6
+
7
+ class IndeterminableMinimumAttributeError < ArgumentError
8
+ def message
9
+ [
10
+ "A minimum value can not be determined when the validation uses :greater_than on a :decimal or :float column type.",
11
+ "Please alter the validation to use :greater_than_or_equal_to, or provide a value for this attribute explicitly with the :min option on input()."
12
+ ].join("\n")
13
+ end
14
+ end
15
+
16
+ class IndeterminableMaximumAttributeError < ArgumentError
17
+ def message
18
+ [
19
+ "A maximum value can not be determined when the validation uses :less_than on a :decimal or :float column type.",
20
+ "Please alter the validation to use :less_than_or_equal_to, or provide a value for this attribute explicitly with the :max option on input()."
21
+ ].join("\n")
22
+ end
23
+ end
24
+
25
+ def validations
26
+ @validations ||= if object && object.class.respond_to?(:validators_on)
27
+ object.class.validators_on(attributized_method_name).select do |validator|
28
+ validator_relevant?(validator)
29
+ end
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ def validator_relevant?(validator)
36
+ return true unless validator.options.key?(:if) || validator.options.key?(:unless)
37
+ conditional = validator.options.key?(:if) ? validator.options[:if] : validator.options[:unless]
38
+
39
+ result = if conditional.respond_to?(:call) && conditional.arity > 0
40
+ conditional.call(object)
41
+ elsif conditional.respond_to?(:call) && conditional.arity == 0
42
+ object.instance_exec(&conditional)
43
+ elsif conditional.is_a?(::Symbol) && object.respond_to?(conditional)
44
+ object.send(conditional)
45
+ else
46
+ conditional
47
+ end
48
+
49
+ result = validator.options.key?(:unless) ? !result : !!result
50
+ not_required_through_negated_validation! if !result && [:presence, :inclusion, :length].include?(validator.kind)
51
+
52
+ result
53
+ end
54
+
55
+ def validation_limit
56
+ validation = validations? && validations.find do |validation|
57
+ validation.kind == :length
58
+ end
59
+ if validation
60
+ option_value(validation.options[:maximum], object) ||
61
+ (validation.options[:within].present? ? option_value(validation.options[:within], object).max : nil)
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+ # Prefer :greater_than_or_equal_to over :greater_than, for no particular reason.
68
+ def validation_min
69
+ validation = validations? && validations.find do |validation|
70
+ validation.kind == :numericality
71
+ end
72
+
73
+ if validation
74
+ # We can't determine an appropriate value for :greater_than with a float/decimal column
75
+ raise IndeterminableMinimumAttributeError if validation.options[:greater_than] && column? && [:float, :decimal].include?(column.type)
76
+
77
+ if validation.options[:greater_than_or_equal_to]
78
+ return option_value(validation.options[:greater_than_or_equal_to], object)
79
+ end
80
+
81
+ if validation.options[:greater_than]
82
+ return option_value(validation.options[:greater_than], object) + 1
83
+ end
84
+ end
85
+ end
86
+
87
+ # Prefer :less_than_or_equal_to over :less_than, for no particular reason.
88
+ def validation_max
89
+ validation = validations? && validations.find do |validation|
90
+ validation.kind == :numericality
91
+ end
92
+ if validation
93
+ # We can't determine an appropriate value for :greater_than with a float/decimal column
94
+ raise IndeterminableMaximumAttributeError if validation.options[:less_than] && column? && [:float, :decimal].include?(column.type)
95
+
96
+ if validation.options[:less_than_or_equal_to]
97
+ return option_value(validation.options[:less_than_or_equal_to], object)
98
+ end
99
+
100
+ if validation.options[:less_than]
101
+ return option_value(validation.options[:less_than], object) - 1
102
+ end
103
+ end
104
+ end
105
+
106
+ def validation_step
107
+ validation = validations? && validations.find do |validation|
108
+ validation.kind == :numericality
109
+ end
110
+ if validation
111
+ validation.options[:step] || (1 if validation_integer_only?)
112
+ else
113
+ nil
114
+ end
115
+ end
116
+
117
+ def validation_integer_only?
118
+ validation = validations? && validations.find do |validation|
119
+ validation.kind == :numericality
120
+ end
121
+ if validation
122
+ validation.options[:only_integer]
123
+ else
124
+ false
125
+ end
126
+ end
127
+
128
+ def validations?
129
+ validations != nil
130
+ end
131
+
132
+ def required?
133
+ return false if options[:required] == false
134
+ return true if options[:required] == true
135
+ return false if not_required_through_negated_validation?
136
+ if validations?
137
+ validations.any? { |validator|
138
+ if validator.options.key?(:on)
139
+ validator_on = Array(validator.options[:on])
140
+ next false if (validator_on.exclude?(:save)) && ((object.new_record? && validator_on.exclude?(:create)) || (!object.new_record? && validator_on.exclude?(:update)))
141
+ end
142
+ case validator.kind
143
+ when :presence
144
+ true
145
+ when :inclusion
146
+ validator.options[:allow_blank] != true
147
+ when :length
148
+ validator.options[:allow_blank] != true &&
149
+ option_value(validator.options[:minimum], object).to_i > 0 ||
150
+ option_value(validator.options[:within], object).try(:first).to_i > 0
151
+ else
152
+ false
153
+ end
154
+ }
155
+ else
156
+ return responds_to_global_required? && !!builder.all_fields_required_by_default
157
+ end
158
+ end
159
+
160
+ def required_attribute?
161
+ required? && builder.use_required_attribute
162
+ end
163
+
164
+ def not_required_through_negated_validation?
165
+ @not_required_through_negated_validation
166
+ end
167
+
168
+ def not_required_through_negated_validation!
169
+ @not_required_through_negated_validation = true
170
+ end
171
+
172
+ def responds_to_global_required?
173
+ true
174
+ end
175
+
176
+ def optional?
177
+ !required?
178
+ end
179
+
180
+ def autofocus?
181
+ opt_autofocus = options[:input_html] && options[:input_html][:autofocus]
182
+
183
+ !!opt_autofocus
184
+ end
185
+
186
+ def column_limit
187
+ return unless column?
188
+ return unless column.respond_to?(:limit)
189
+
190
+ limit = column.limit # already in characters for string, text, etc
191
+
192
+ if column.type == :integer && column.limit.is_a?(Integer)
193
+ return {
194
+ 1 => 3, # 8 bit
195
+ 2 => 5, # 16 bit
196
+ 3 => 7, # 24 bit
197
+ 4 => 10, # 32 bit
198
+ 8 => 19, # 64 bit
199
+ }[limit] || nil
200
+ end
201
+
202
+ return limit
203
+ end
204
+
205
+ def limit
206
+ validation_limit || column_limit
207
+ end
208
+
209
+ def readonly?
210
+ readonly_from_options? || readonly_attribute?
211
+ end
212
+
213
+ def readonly_attribute?
214
+ object_class = self.object.class
215
+ object_class.respond_to?(:readonly_attributes) &&
216
+ self.object.persisted? &&
217
+ column.respond_to?(:name) &&
218
+ object_class.readonly_attributes.include?(column.name.to_s)
219
+ end
220
+
221
+ def readonly_from_options?
222
+ options[:input_html] && options[:input_html][:readonly]
223
+ end
224
+
225
+ private
226
+
227
+ # Implements `ActiveModel::Validations::ResolveValue`, introduced by Rails 7.1.
228
+ # https://github.com/rails/rails/blob/v7.1.0/activemodel/lib/active_model/validations/resolve_value.rb
229
+ def option_value(option, object)
230
+ case option
231
+ when Symbol
232
+ object.send(option)
233
+ when Proc
234
+ if option.arity == 0
235
+ option.call
236
+ else
237
+ option.call(object)
238
+ end
239
+ else
240
+ if option.respond_to?(:call)
241
+ option.call(object)
242
+ else
243
+ option
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ module Base
5
+ # @todo relies on `dom_id`, `required?`, `optional`, `errors?`, `association_primary_key` & `sanitized_method_name` methods from another module
6
+ module Wrapping
7
+
8
+ # Override this method if you want to change the display order (for example, rendering the
9
+ # errors before the body of the input).
10
+ def input_wrapping(&block)
11
+ template.content_tag(:li,
12
+ [template.capture(&block), error_html, hint_html].join("\n").html_safe,
13
+ wrapper_html_options
14
+ )
15
+ end
16
+
17
+ def wrapper_html_options
18
+ opts = wrapper_html_options_raw
19
+ opts[:class] = wrapper_classes
20
+ opts[:id] = wrapper_dom_id unless opts.has_key? :id
21
+ opts
22
+ end
23
+
24
+ def wrapper_html_options_raw
25
+ (options[:wrapper_html] || {}).dup
26
+ end
27
+
28
+ def wrapper_classes_raw
29
+ [*wrapper_html_options_raw[:class]]
30
+ end
31
+
32
+ def wrapper_classes
33
+ classes = wrapper_classes_raw
34
+ classes << as
35
+ classes << "input"
36
+ classes << "error" if errors?
37
+ classes << "optional" if optional?
38
+ classes << "required" if required?
39
+ classes << "autofocus" if autofocus?
40
+
41
+ classes.join(' ')
42
+ end
43
+
44
+ def wrapper_dom_id
45
+ @wrapper_dom_id ||= "#{dom_id.to_s.gsub((association_primary_key || method).to_s, sanitized_method_name.to_s)}_input"
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end