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,204 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Helpers
4
+
5
+ # FormHelper provides a handful of wrappers around Rails' built-in form helpers methods to set
6
+ # the `:builder` option to `Formtastic::FormBuilder` and apply some class names to the `<form>`
7
+ # tag.
8
+ #
9
+ # The following methods are wrapped:
10
+ #
11
+ # * `semantic_form_for` to `form_for`
12
+ # * `semantic_fields_for` to `fields_for`
13
+ # * `semantic_remote_form_for` and `semantic_form_remote_for` to `remote_form_for`
14
+ #
15
+ # The following two examples are effectively equivalent:
16
+ #
17
+ # <%= form_for(@post, :builder => Formtastic::FormBuilder, :class => 'formtastic post') do |f| %>
18
+ # #...
19
+ # <% end %>
20
+ #
21
+ # <%= semantic_form_for(@post) do |f| %>
22
+ # #...
23
+ # <% end %>
24
+ #
25
+ # This simple wrapping means that all arguments, options and variations supported by Rails' own
26
+ # helpers are also supported by Formtastic.
27
+ #
28
+ # Since `Formtastic::FormBuilder` subclasses Rails' own `FormBuilder`, you have access to all
29
+ # of Rails' built-in form helper methods such as `text_field`, `check_box`, `radio_button`,
30
+ # etc **in addition to** all of Formtastic's additional helpers like {InputsHelper#inputs inputs},
31
+ # {InputsHelper#input input}, {ButtonsHelper#buttons buttons}, etc:
32
+ #
33
+ # <%= semantic_form_for(@post) do |f| %>
34
+ #
35
+ # <!-- Formtastic -->
36
+ # <%= f.input :title %>
37
+ #
38
+ # <!-- Rails -->
39
+ # <li class='something-custom'>
40
+ # <%= f.label :title %>
41
+ # <%= f.text_field :title %>
42
+ # <p class='hints'>...</p>
43
+ # </li>
44
+ # <% end %>
45
+ #
46
+ # Formtastic is a superset of Rails' FormBuilder. It deliberately avoids overriding or modifying
47
+ # the behavior of Rails' own form helpers so that you can use Formtastic helpers when suited,
48
+ # and fall back to regular Rails helpers, ERB and HTML when needed. In other words, you're never
49
+ # fully committed to The Formtastic Way.
50
+ module FormHelper
51
+
52
+ # Allows the `:builder` option on `form_for` etc to be changed to your own which subclasses
53
+ # `Formtastic::FormBuilder`. Change this from `config/initializers/formtastic.rb`.
54
+ @@builder = Formtastic::FormBuilder
55
+ mattr_accessor :builder
56
+
57
+ # Allows the default class we add to all `<form>` tags to be changed from `formtastic` to
58
+ # `whatever`. Change this from `config/initializers/formtastic.rb`.
59
+ @@default_form_class = 'formtastic'
60
+ mattr_accessor :default_form_class
61
+
62
+ # Allows to set a custom proc to handle the class inferred from the model's name. By default it
63
+ # will infer the name from the class name (eg. Post will be "post").
64
+ @@default_form_model_class_proc = proc { |model_class_name| model_class_name }
65
+ mattr_accessor :default_form_model_class_proc
66
+
67
+ # Allows to set a custom field_error_proc wrapper. By default this wrapper
68
+ # is disabled since `formtastic` already adds an error class to the LI tag
69
+ # containing the input. Change this from `config/initializers/formtastic.rb`.
70
+ @@formtastic_field_error_proc = proc { |html_tag, instance_tag| html_tag }
71
+ mattr_accessor :formtastic_field_error_proc
72
+
73
+ # Wrapper around Rails' own `form_for` helper to set the `:builder` option to
74
+ # `Formtastic::FormBuilder` and to set some class names on the `<form>` tag such as
75
+ # `formtastic` and the downcased and underscored model name (eg `post`).
76
+ #
77
+ # See Rails' `form_for` for full documentation of all supported arguments and options.
78
+ #
79
+ # Since `Formtastic::FormBuilder` subclasses Rails' own FormBuilder, you have access to all
80
+ # of Rails' built-in form helper methods such as `text_field`, `check_box`, `radio_button`,
81
+ # etc **in addition to** all of Formtastic's additional helpers like {InputsHelper#inputs inputs},
82
+ # {InputsHelper#input input}, {ButtonsHelper#buttons buttons}, etc.
83
+ #
84
+ # Most of the examples below have been adapted from the examples found in the Rails `form_for`
85
+ # documentation.
86
+ #
87
+ # @see https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html Rails' FormHelper documentation (`form_for`, etc)
88
+ # @see https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html Rails' FormBuilder documentation (`text_field`, etc)
89
+ # @see FormHelper The overview of the FormBuilder module
90
+ #
91
+ # @example Resource-oriented form generation
92
+ # <%= semantic_form_for @user do |f| %>
93
+ # <%= f.input :name %>
94
+ # <%= f.input :email %>
95
+ # <%= f.input :password %>
96
+ # <% end %>
97
+ #
98
+ # @example Generic form generation
99
+ # <%= semantic_form_for :user do |f| %>
100
+ # <%= f.input :name %>
101
+ # <%= f.input :email %>
102
+ # <%= f.input :password %>
103
+ # <% end %>
104
+ #
105
+ # @example Resource-oriented with custom URL
106
+ # <%= semantic_form_for(@post, :url => super_post_path(@post)) do |f| %>
107
+ # ...
108
+ # <% end %>
109
+ #
110
+ # @example Resource-oriented with namespaced routes
111
+ # <%= semantic_form_for([:admin, @post]) do |f| %>
112
+ # ...
113
+ # <% end %>
114
+ #
115
+ # @example Resource-oriented with nested routes
116
+ # <%= semantic_form_for([@user, @post]) do |f| %>
117
+ # ...
118
+ # <% end %>
119
+ #
120
+ # @example Rename the resource
121
+ # <%= semantic_form_for(@post, :as => :article) do |f| %>
122
+ # ...
123
+ # <% end %>
124
+ #
125
+ # @example Remote forms (unobtrusive JavaScript)
126
+ # <%= semantic_form_for(@post, :remote => true) do |f| %>
127
+ # ...
128
+ # <% end %>
129
+ #
130
+ # @example Namespaced forms all multiple Formtastic forms to exist on the one page without DOM id clashes and invalid HTML documents.
131
+ # <%= semantic_form_for(@post, :namespace => 'first') do |f| %>
132
+ # ...
133
+ # <% end %>
134
+ #
135
+ # @example Accessing a mixture of Formtastic helpers and Rails FormBuilder helpers.
136
+ # <%= semantic_form_for(@post) do |f| %>
137
+ # <%= f.input :title %>
138
+ # <%= f.input :body %>
139
+ # <li class="something-custom">
140
+ # <label><%= f.check_box :published %></label>
141
+ # </li>
142
+ # <% end %>
143
+ #
144
+ # @param record_or_name_or_array
145
+ # Same behavior as Rails' `form_for`
146
+ #
147
+ # @option *args [Hash] :html
148
+ # Pass HTML attributes into the `<form>` tag. Same behavior as Rails' `form_for`, except we add in some of our own classes.
149
+ #
150
+ # @option *args [String, Hash] :url
151
+ # A hash of URL components just like you pass into `link_to` or `url_for`, or a named route (eg `posts_path`). Same behavior as Rails' `form_for`.
152
+ #
153
+ # @option *args [String] :namespace
154
+ def semantic_form_for(record_or_name_or_array, *args, &proc)
155
+ options = args.extract_options!
156
+ options[:builder] ||= @@builder
157
+ options[:html] ||= {}
158
+ options[:html][:novalidate] = !@@builder.perform_browser_validations unless options[:html].key?(:novalidate)
159
+ options[:custom_namespace] = options.delete(:namespace)
160
+
161
+ singularizer = defined?(ActiveModel::Naming.singular) ? ActiveModel::Naming.method(:singular) : ActionController::RecordIdentifier.method(:singular_class_name)
162
+
163
+ class_names = options[:html][:class] ? options[:html][:class].split(" ") : []
164
+ class_names << @@default_form_class
165
+ model_class_name = case record_or_name_or_array
166
+ when String, Symbol then record_or_name_or_array.to_s # :post => "post"
167
+ when Array then options[:as] || singularizer.call(record_or_name_or_array[-1].class) # [@post, @comment] # => "comment"
168
+ else options[:as] || singularizer.call(record_or_name_or_array.class) # @post => "post"
169
+ end
170
+ class_names << @@default_form_model_class_proc.call(model_class_name)
171
+
172
+ options[:html][:class] = class_names.compact.join(" ")
173
+
174
+ with_custom_field_error_proc do
175
+ self.form_for(record_or_name_or_array, *(args << options), &proc)
176
+ end
177
+ end
178
+
179
+ # Wrapper around Rails' own `fields_for` helper to set the `:builder` option to
180
+ # `Formtastic::FormBuilder`.
181
+ #
182
+ # @see #semantic_form_for
183
+ def semantic_fields_for(record_name, record_object = nil, options = {}, &block)
184
+ options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
185
+ options[:builder] ||= @@builder
186
+ options[:custom_namespace] = options.delete(:namespace)
187
+
188
+ with_custom_field_error_proc do
189
+ self.fields_for(record_name, record_object, options, &block)
190
+ end
191
+ end
192
+
193
+ protected
194
+
195
+ def with_custom_field_error_proc(&block)
196
+ default_field_error_proc = ::ActionView::Base.field_error_proc
197
+ ::ActionView::Base.field_error_proc = @@formtastic_field_error_proc
198
+ yield
199
+ ensure
200
+ ::ActionView::Base.field_error_proc = default_field_error_proc
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Helpers
4
+
5
+ # {#input} is used to render all content (labels, form widgets, error messages, hints, etc) for
6
+ # a single form input (or field), usually representing a single method or attribute on the
7
+ # form's object or model.
8
+ #
9
+ # The content is wrapped in an `<li>` tag, so it's usually called inside an {Formtastic::Helpers::InputsHelper#inputs inputs} block
10
+ # (which renders an `<ol>` inside a `<fieldset>`), which should be inside a {Formtastic::Helpers::FormHelper#semantic_form_for `semantic_form_for`}
11
+ # block:
12
+ #
13
+ # <%= semantic_form_for @post do |f| %>
14
+ # <%= f.inputs do %>
15
+ # <%= f.input :title %>
16
+ # <%= f.input :body %>
17
+ # <% end %>
18
+ # <% end %>
19
+ #
20
+ # The HTML output will be something like:
21
+ #
22
+ # <form class="formtastic" method="post" action="...">
23
+ # <fieldset>
24
+ # <ol>
25
+ # <li class="string required" id="post_title_input">
26
+ # ...
27
+ # </li>
28
+ # <li class="text required" id="post_body_input">
29
+ # ...
30
+ # </li>
31
+ # </ol>
32
+ # </fieldset>
33
+ # </form>
34
+ #
35
+ # @see #input
36
+ # @see Formtastic::Helpers::InputsHelper#inputs
37
+ # @see Formtastic::Helpers::FormHelper#semantic_form_for
38
+ module InputHelper
39
+ include Formtastic::Helpers::Reflection
40
+ include Formtastic::Helpers::Enum
41
+ include Formtastic::Helpers::FileColumnDetection
42
+
43
+ # Returns a chunk of HTML markup for a given `method` on the form object, wrapped in
44
+ # an `<li>` wrapper tag with appropriate `class` and `id` attribute hooks for CSS and JS.
45
+ # In many cases, the contents of the wrapper will be as simple as a `<label>` and an `<input>`:
46
+ #
47
+ # <%= f.input :title, :as => :string, :required => true %>
48
+ #
49
+ # <li class="string required" id="post_title_input">
50
+ # <label for="post_title">Title<abbr title="Required">*</abbr></label>
51
+ # <input type="text" name="post[title]" value="" id="post_title" required="required">
52
+ # </li>
53
+ #
54
+ # In other cases (like a series of checkboxes for a `has_many` relationship), the wrapper may
55
+ # include more complex markup, like a nested `<fieldset>` with a `<legend>` and an `<ol>` of
56
+ # checkbox/label pairs for each choice:
57
+ #
58
+ # <%= f.input :categories, :as => :check_boxes, :collection => Category.active.ordered %>
59
+ #
60
+ # <li class="check_boxes" id="post_categories_input">
61
+ # <fieldset>
62
+ # <legend>Categories</legend>
63
+ # <ol>
64
+ # <li>
65
+ # <label><input type="checkbox" name="post[categories][1]" value="1"> Ruby</label>
66
+ # </li>
67
+ # <li>
68
+ # <label><input type="checkbox" name="post[categories][2]" value="2"> Rails</label>
69
+ # </li>
70
+ # <li>
71
+ # <label><input type="checkbox" name="post[categories][2]" value="2"> Awesome</label>
72
+ # </li>
73
+ # </ol>
74
+ # </fieldset>
75
+ # </li>
76
+ #
77
+ # Sensible defaults for all options are guessed by looking at the method name, database column
78
+ # information, association information, validation information, etc. For example, a `:string`
79
+ # database column will map to a `:string` input, but if the method name contains 'email', will
80
+ # map to an `:email` input instead. `belongs_to` associations will have a `:select` input, etc.
81
+ #
82
+ # Formtastic supports many different styles of inputs, and you can/should override the default
83
+ # with the `:as` option. Internally, the symbol is used to map to a protected method
84
+ # responsible for the details. For example, `:as => :string` will map to `string_input`,
85
+ # defined in a module of the same name. Detailed documentation for each input style and it's
86
+ # supported options is available on the `*_input` method in each module (links provided below).
87
+ #
88
+ # Available input styles:
89
+ #
90
+ # * `:boolean` (see {Inputs::BooleanInput})
91
+ # * `:check_boxes` (see {Inputs::CheckBoxesInput})
92
+ # * `:color` (see {Inputs::ColorInput})
93
+ # * `:country` (see {Inputs::CountryInput})
94
+ # * `:datetime_select` (see {Inputs::DatetimeSelectInput})
95
+ # * `:date_select` (see {Inputs::DateSelectInput})
96
+ # * `:email` (see {Inputs::EmailInput})
97
+ # * `:file` (see {Inputs::FileInput})
98
+ # * `:hidden` (see {Inputs::HiddenInput})
99
+ # * `:number` (see {Inputs::NumberInput})
100
+ # * `:password` (see {Inputs::PasswordInput})
101
+ # * `:phone` (see {Inputs::PhoneInput})
102
+ # * `:radio` (see {Inputs::RadioInput})
103
+ # * `:search` (see {Inputs::SearchInput})
104
+ # * `:select` (see {Inputs::SelectInput})
105
+ # * `:string` (see {Inputs::StringInput})
106
+ # * `:text` (see {Inputs::TextInput})
107
+ # * `:time_zone` (see {Inputs::TimeZoneInput})
108
+ # * `:time_select` (see {Inputs::TimeSelectInput})
109
+ # * `:url` (see {Inputs::UrlInput})
110
+ #
111
+ # Calling `:as => :string` (for example) will call `#to_html` on a new instance of
112
+ # `Formtastic::Inputs::StringInput`. Before this, Formtastic will try to instantiate a top-level
113
+ # namespace StringInput, meaning you can subclass and modify `Formtastic::Inputs::StringInput`
114
+ # in `app/inputs/`. This also means you can create your own new input types in `app/inputs/`.
115
+ #
116
+ # @todo document the "guessing" of input style
117
+ #
118
+ # @param [Symbol] method
119
+ # The database column or method name on the form object that this input represents
120
+ #
121
+ # @option options :as [Symbol]
122
+ # Override the style of input should be rendered
123
+ #
124
+ # @option options :label [String, Symbol, false]
125
+ # Override the label text
126
+ #
127
+ # @option options :hint [String, Symbol, false]
128
+ # Override hint text
129
+ #
130
+ # @option options :required [Boolean]
131
+ # Override to mark the input as required (or not) — adds a required/optional class to the wrapper, and a HTML5 required attribute to the `<input>`
132
+ #
133
+ # @option options :input_html [Hash]
134
+ # Override or add to the HTML attributes to be passed down to the `<input>` tag
135
+ # (If you use attr_readonly method in your model, formtastic will automatically set those attributes's input readonly)
136
+ #
137
+ # @option options :wrapper_html [Hash]
138
+ # Override or add to the HTML attributes to be passed down to the wrapping `<li>` tag
139
+ #
140
+ # @option options :collection [Array<ActiveModel, String, Symbol>, Hash{String => String, Boolean}, OrderedHash{String => String, Boolean}]
141
+ # Override collection of objects in the association (`:select`, `:radio` & `:check_boxes` inputs only)
142
+ #
143
+ # @option options :multiple [Boolean]
144
+ # Specify if the `:select` input should allow multiple selections or not (defaults to `belongs_to` associations, and `true` for `has_many` and `has_and_belongs_to_many` associations)
145
+ #
146
+ # @option options :include_blank [Boolean]
147
+ # Specify if a `:select` input should include a blank option or not (defaults to `include_blank_for_select_by_default` configuration)
148
+ #
149
+ # @option options :prompt [String]
150
+ # Specify the text in the first ('blank') `:select` input `<option>` to prompt a user to make a selection (implicitly sets `:include_blank` to `true`)
151
+ #
152
+ # @todo Can we deprecate & kill `:label`, `:hint` & `:prompt`? All strings could be shifted to i18n!
153
+ #
154
+ # @example Accept all default options
155
+ # <%= f.input :title %>
156
+ #
157
+ # @example Change the input type
158
+ # <%= f.input :title, :as => :string %>
159
+ #
160
+ # @example Changing the label with a String
161
+ # <%= f.input :title, :label => "Post title" %>
162
+ #
163
+ # @example Disabling the label with false, even if an i18n translation exists
164
+ # <%= f.input :title, :label => false %>
165
+ #
166
+ # @example Changing the hint with a String
167
+ # <%= f.input :title, :hint => "Every post needs a title!" %>
168
+ #
169
+ # @example Disabling the hint with false, even if an i18n translation exists
170
+ # <%= f.input :title, :hint => false %>
171
+ #
172
+ # @example Marking a field as required or not (even if validations do not enforce it)
173
+ # <%= f.input :title, :required => true %>
174
+ # <%= f.input :title, :required => false %>
175
+ #
176
+ # @example Changing or adding to HTML attributes in the main `<input>` or `<select>` tag
177
+ # <%= f.input :title, :input_html => { :onchange => "somethingAwesome();", :class => 'awesome' } %>
178
+ #
179
+ # @example Changing or adding to HTML attributes in the main `<label>` tag
180
+ # <%= f.input :title, :label_html => { :data => { :tooltip => 'Great Tooltip' } } %>
181
+ #
182
+ # @example Changing or adding to HTML attributes in the wrapper `<li>` tag
183
+ # <%= f.input :title, :wrapper_html => { :class => "important-input" } %>
184
+ #
185
+ # @example Changing the association choices with `:collection`
186
+ # <%= f.input :author, :collection => User.active %>
187
+ # <%= f.input :categories, :collection => Category.where(...).order(...) %>
188
+ # <%= f.input :status, :collection => ["Draft", "Published"] %>
189
+ # <%= f.input :status, :collection => [:draft, :published] %>
190
+ # <%= f.input :status, :collection => {"Draft" => 0, "Published" => 1} %>
191
+ # <%= f.input :status, :collection => OrderedHash.new("Draft" => 0, "Published" => 1) %>
192
+ # <%= f.input :status, :collection => [["Draft", 0], ["Published", 1]] %>
193
+ # <%= f.input :status, :collection => grouped_options_for_select(...) %>
194
+ # <%= f.input :status, :collection => options_for_select(...) %>
195
+ #
196
+ # @example Specifying if a `:select` should allow multiple selections:
197
+ # <%= f.input :cateogies, :as => :select, :multiple => true %>
198
+ # <%= f.input :cateogies, :as => :select, :multiple => false %>
199
+ #
200
+ # @example Specifying if a `:select` should have a 'blank' first option to prompt selection:
201
+ # <%= f.input :author, :as => :select, :include_blank => true %>
202
+ # <%= f.input :author, :as => :select, :include_blank => false %>
203
+ #
204
+ # @example Specifying the text for a `:select` input's 'blank' first option to prompt selection:
205
+ # <%= f.input :author, :as => :select, :prompt => "Select an Author" %>
206
+ #
207
+ # @example Modifying an input to suit your needs in `app/inputs`:
208
+ # class StringInput < Formtastic::Inputs::StringInput
209
+ # def to_html
210
+ # puts "this is my custom version of StringInput"
211
+ # super
212
+ # end
213
+ # end
214
+ #
215
+ # @example Creating your own input to suit your needs in `app/inputs`:
216
+ # class DatePickerInput
217
+ # include Formtastic::Inputs::Base
218
+ # def to_html
219
+ # # ...
220
+ # end
221
+ # end
222
+ #
223
+ # @example Providing HTML5 placeholder text through i18n:
224
+ # en:
225
+ # formtastic:
226
+ # placeholders:
227
+ # user:
228
+ # email: "you@yours.com"
229
+ # first_name: "Joe"
230
+ # last_name: "Smith"
231
+ #
232
+ # @see #namespaced_input_class
233
+ # @todo Many many more examples. Some of the detail probably needs to be pushed out to the relevant methods too.
234
+ # @todo More i18n examples.
235
+ def input(method, options = {})
236
+ method = method.to_sym
237
+ options = options.dup # Allow options to be shared without being tainted by Formtastic
238
+ options[:as] ||= default_input_type(method, options)
239
+
240
+ klass = namespaced_input_class(options[:as])
241
+
242
+ klass.new(self, template, @object, @object_name, method, options).to_html
243
+ end
244
+
245
+ protected
246
+
247
+ # First try if we can detect special things like :file. With CarrierWave the method does have
248
+ # an underlying column so we don't want :string to get selected.
249
+ #
250
+ # For methods that have a database column, take a best guess as to what the input method
251
+ # should be. In most cases, it will just return the column type (eg :string), but for special
252
+ # cases it will simplify (like the case of :integer, :float & :decimal to :number), or do
253
+ # something different (like :password and :select).
254
+ #
255
+ # If there is no column for the method (eg "virtual columns" with an attr_accessor), the
256
+ # default is a :string, a similar behaviour to Rails' scaffolding.
257
+ def default_input_type(method, options = {}) # @private
258
+ if @object
259
+ return :select if reflection_for(method)
260
+
261
+ return :file if is_file?(method, options)
262
+ end
263
+
264
+ column = column_for(method)
265
+ if column && column.type
266
+ # Special cases where the column type doesn't map to an input method.
267
+ case column.type
268
+ when :string
269
+ return :password if method.to_s =~ /password/
270
+ return :country if method.to_s =~ /country$/
271
+ return :time_zone if method.to_s =~ /time_zone/
272
+ return :email if method.to_s =~ /email/
273
+ return :url if method.to_s =~ /^url$|^website$|_url$/
274
+ return :phone if method.to_s =~ /(phone|fax)/
275
+ return :search if method.to_s =~ /^search$/
276
+ return :color if method.to_s =~ /color/
277
+ when :integer
278
+ return :select if reflection_for(method)
279
+ return :select if enum_for(method)
280
+ return :number
281
+ when :float, :decimal
282
+ return :number
283
+ when :datetime, :timestamp, :timestamptz
284
+ return :datetime_select
285
+ when :time
286
+ return :time_select
287
+ when :date
288
+ return :date_select
289
+ when :hstore, :json, :jsonb
290
+ return :text
291
+ when :citext, :inet
292
+ return :string
293
+ end
294
+
295
+ # Try look for hints in options hash. Quite common scenario: Enum keys stored as string in the database.
296
+ return :select if column.type == :string && options.key?(:collection)
297
+ # Try 3: Assume the input name will be the same as the column type (e.g. string_input).
298
+ return column.type
299
+ else
300
+ return :select if options.key?(:collection)
301
+ return :password if method.to_s =~ /password/
302
+ return :string
303
+ end
304
+ end
305
+
306
+ # Get a column object for a specified attribute method - if possible.
307
+ # @return [ActiveModel::Type::Value, #type] in case of rails 5 attributes api
308
+ # @return [ActiveRecord::ConnectionAdapters::Column] in case of rails 4
309
+ def column_for(method) # @private
310
+ case
311
+ when @object.class.respond_to?(:type_for_attribute)
312
+ @object.class.type_for_attribute(method.to_s)
313
+ when @object.class.respond_to?(:column_for_attribute)
314
+ @object.class.column_for_attribute(method)
315
+ when @object.respond_to?(:column_for_attribute)
316
+ @object.column_for_attribute(method)
317
+ else nil
318
+ end
319
+ end
320
+
321
+ # Takes the `:as` option and attempts to return the corresponding input
322
+ # class. In the case of `:as => :awesome` it will first attempt to find a
323
+ # top level `AwesomeInput` class (to allow the application to subclass
324
+ # and modify to suit), falling back to `Formtastic::Inputs::AwesomeInput`.
325
+ #
326
+ # Custom input namespaces to look into can be configured via the
327
+ # {Formtastic::FormBuilder.input_namespaces} configuration setting.
328
+ #
329
+ # @param [Symbol] as A symbol representing the type of input to render
330
+ # @raise [Formtastic::UnknownInputError] An appropriate input class could not be found
331
+ # @return [Class] An input class constant
332
+ #
333
+ # @example Normal use
334
+ # input_class(:string) #=> Formtastic::Inputs::StringInput
335
+ # input_class(:date) #=> Formtastic::Inputs::DateInput
336
+ #
337
+ # @example When a top-level class is found
338
+ # input_class(:string) #=> StringInput
339
+ # input_class(:awesome) #=> AwesomeInput
340
+ # @see NamespacedClassFinder#find
341
+ def namespaced_input_class(as)
342
+ @input_class_finder ||= input_class_finder.new(self)
343
+ @input_class_finder.find(as)
344
+ rescue Formtastic::InputClassFinder::NotFoundError
345
+ raise Formtastic::UnknownInputError, "Unable to find input #{$!.message}"
346
+ end
347
+ end
348
+ end
349
+ end