padrino-helpers-cj 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +21 -0
  4. data/.yardopts +1 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +239 -0
  7. data/Rakefile +5 -0
  8. data/lib/padrino-helpers.rb +62 -0
  9. data/lib/padrino-helpers/asset_tag_helpers.rb +390 -0
  10. data/lib/padrino-helpers/form_builder/abstract_form_builder.rb +283 -0
  11. data/lib/padrino-helpers/form_builder/deprecated_builder_methods.rb +92 -0
  12. data/lib/padrino-helpers/form_builder/standard_form_builder.rb +40 -0
  13. data/lib/padrino-helpers/form_helpers.rb +633 -0
  14. data/lib/padrino-helpers/form_helpers/errors.rb +138 -0
  15. data/lib/padrino-helpers/form_helpers/options.rb +97 -0
  16. data/lib/padrino-helpers/form_helpers/security.rb +70 -0
  17. data/lib/padrino-helpers/format_helpers.rb +372 -0
  18. data/lib/padrino-helpers/locale/cs.yml +103 -0
  19. data/lib/padrino-helpers/locale/da.yml +91 -0
  20. data/lib/padrino-helpers/locale/de.yml +81 -0
  21. data/lib/padrino-helpers/locale/en.yml +103 -0
  22. data/lib/padrino-helpers/locale/es.yml +103 -0
  23. data/lib/padrino-helpers/locale/fr.yml +79 -0
  24. data/lib/padrino-helpers/locale/hu.yml +103 -0
  25. data/lib/padrino-helpers/locale/it.yml +89 -0
  26. data/lib/padrino-helpers/locale/ja.yml +103 -0
  27. data/lib/padrino-helpers/locale/lv.yml +103 -0
  28. data/lib/padrino-helpers/locale/nl.yml +82 -0
  29. data/lib/padrino-helpers/locale/no.yml +91 -0
  30. data/lib/padrino-helpers/locale/pl.yml +95 -0
  31. data/lib/padrino-helpers/locale/pt_br.yml +103 -0
  32. data/lib/padrino-helpers/locale/ro.yml +103 -0
  33. data/lib/padrino-helpers/locale/ru.yml +103 -0
  34. data/lib/padrino-helpers/locale/sv.yml +103 -0
  35. data/lib/padrino-helpers/locale/tr.yml +103 -0
  36. data/lib/padrino-helpers/locale/uk.yml +103 -0
  37. data/lib/padrino-helpers/locale/zh_cn.yml +103 -0
  38. data/lib/padrino-helpers/locale/zh_tw.yml +103 -0
  39. data/lib/padrino-helpers/number_helpers.rb +283 -0
  40. data/lib/padrino-helpers/output_helpers.rb +226 -0
  41. data/lib/padrino-helpers/output_helpers/abstract_handler.rb +61 -0
  42. data/lib/padrino-helpers/output_helpers/erb_handler.rb +27 -0
  43. data/lib/padrino-helpers/output_helpers/haml_handler.rb +25 -0
  44. data/lib/padrino-helpers/output_helpers/slim_handler.rb +18 -0
  45. data/lib/padrino-helpers/render_helpers.rb +63 -0
  46. data/lib/padrino-helpers/tag_helpers.rb +294 -0
  47. data/lib/padrino-helpers/translation_helpers.rb +36 -0
  48. data/lib/padrino/rendering.rb +397 -0
  49. data/lib/padrino/rendering/erubis_template.rb +66 -0
  50. data/lib/padrino/rendering/haml_template.rb +26 -0
  51. data/lib/padrino/rendering/slim_template.rb +20 -0
  52. data/padrino-helpers.gemspec +29 -0
  53. data/test/fixtures/apps/.components +6 -0
  54. data/test/fixtures/apps/.gitignore +7 -0
  55. data/test/fixtures/apps/render.rb +25 -0
  56. data/test/fixtures/apps/views/article/comment/show.slim +1 -0
  57. data/test/fixtures/apps/views/blog/post.erb +1 -0
  58. data/test/fixtures/apps/views/layouts/specific.erb +1 -0
  59. data/test/fixtures/apps/views/test/post.erb +1 -0
  60. data/test/fixtures/layouts/layout.erb +1 -0
  61. data/test/fixtures/markup_app/app.rb +87 -0
  62. data/test/fixtures/markup_app/views/button_to.erb +8 -0
  63. data/test/fixtures/markup_app/views/button_to.haml +5 -0
  64. data/test/fixtures/markup_app/views/button_to.slim +6 -0
  65. data/test/fixtures/markup_app/views/capture_concat.erb +14 -0
  66. data/test/fixtures/markup_app/views/capture_concat.haml +12 -0
  67. data/test/fixtures/markup_app/views/capture_concat.slim +12 -0
  68. data/test/fixtures/markup_app/views/content_for.erb +23 -0
  69. data/test/fixtures/markup_app/views/content_for.haml +19 -0
  70. data/test/fixtures/markup_app/views/content_for.slim +19 -0
  71. data/test/fixtures/markup_app/views/content_tag.erb +13 -0
  72. data/test/fixtures/markup_app/views/content_tag.haml +11 -0
  73. data/test/fixtures/markup_app/views/content_tag.slim +11 -0
  74. data/test/fixtures/markup_app/views/current_engine.erb +5 -0
  75. data/test/fixtures/markup_app/views/current_engine.haml +5 -0
  76. data/test/fixtures/markup_app/views/current_engine.slim +5 -0
  77. data/test/fixtures/markup_app/views/fields_for.erb +20 -0
  78. data/test/fixtures/markup_app/views/fields_for.haml +15 -0
  79. data/test/fixtures/markup_app/views/fields_for.slim +15 -0
  80. data/test/fixtures/markup_app/views/form_for.erb +72 -0
  81. data/test/fixtures/markup_app/views/form_for.haml +59 -0
  82. data/test/fixtures/markup_app/views/form_for.slim +59 -0
  83. data/test/fixtures/markup_app/views/form_tag.erb +95 -0
  84. data/test/fixtures/markup_app/views/form_tag.haml +78 -0
  85. data/test/fixtures/markup_app/views/form_tag.slim +79 -0
  86. data/test/fixtures/markup_app/views/link_to.erb +5 -0
  87. data/test/fixtures/markup_app/views/link_to.haml +4 -0
  88. data/test/fixtures/markup_app/views/link_to.slim +4 -0
  89. data/test/fixtures/markup_app/views/mail_to.erb +3 -0
  90. data/test/fixtures/markup_app/views/mail_to.haml +3 -0
  91. data/test/fixtures/markup_app/views/mail_to.slim +3 -0
  92. data/test/fixtures/markup_app/views/meta_tag.erb +3 -0
  93. data/test/fixtures/markup_app/views/meta_tag.haml +3 -0
  94. data/test/fixtures/markup_app/views/meta_tag.slim +3 -0
  95. data/test/fixtures/markup_app/views/partials/_erb.erb +1 -0
  96. data/test/fixtures/markup_app/views/partials/_haml.haml +1 -0
  97. data/test/fixtures/markup_app/views/partials/_slim.slim +1 -0
  98. data/test/fixtures/markup_app/views/simple_partial.erb +1 -0
  99. data/test/fixtures/markup_app/views/simple_partial.haml +1 -0
  100. data/test/fixtures/markup_app/views/simple_partial.slim +1 -0
  101. data/test/fixtures/render_app/app.rb +94 -0
  102. data/test/fixtures/render_app/views/_deep.erb +3 -0
  103. data/test/fixtures/render_app/views/_deep.haml +2 -0
  104. data/test/fixtures/render_app/views/_deep.slim +2 -0
  105. data/test/fixtures/render_app/views/_partial_block_erb.erb +10 -0
  106. data/test/fixtures/render_app/views/_partial_block_haml.haml +7 -0
  107. data/test/fixtures/render_app/views/_partial_block_slim.slim +7 -0
  108. data/test/fixtures/render_app/views/_unsafe.html.builder +2 -0
  109. data/test/fixtures/render_app/views/_unsafe_object.html.builder +2 -0
  110. data/test/fixtures/render_app/views/current_engine.haml +5 -0
  111. data/test/fixtures/render_app/views/current_engines/_erb.erb +1 -0
  112. data/test/fixtures/render_app/views/current_engines/_haml.haml +1 -0
  113. data/test/fixtures/render_app/views/current_engines/_slim.slim +1 -0
  114. data/test/fixtures/render_app/views/double_capture_erb.erb +3 -0
  115. data/test/fixtures/render_app/views/double_capture_haml.haml +2 -0
  116. data/test/fixtures/render_app/views/double_capture_slim.slim +2 -0
  117. data/test/fixtures/render_app/views/erb/test.erb +1 -0
  118. data/test/fixtures/render_app/views/explicit_engine.haml +5 -0
  119. data/test/fixtures/render_app/views/haml/test.haml +1 -0
  120. data/test/fixtures/render_app/views/render_block_erb.erb +5 -0
  121. data/test/fixtures/render_app/views/render_block_haml.haml +4 -0
  122. data/test/fixtures/render_app/views/render_block_slim.slim +4 -0
  123. data/test/fixtures/render_app/views/ruby_block_capture_erb.erb +1 -0
  124. data/test/fixtures/render_app/views/ruby_block_capture_haml.haml +1 -0
  125. data/test/fixtures/render_app/views/ruby_block_capture_slim.slim +1 -0
  126. data/test/fixtures/render_app/views/template/_user.haml +7 -0
  127. data/test/fixtures/render_app/views/template/haml_template.haml +1 -0
  128. data/test/fixtures/render_app/views/template/some_template.haml +2 -0
  129. data/test/fixtures/render_app/views/wrong_capture_erb.erb +3 -0
  130. data/test/fixtures/render_app/views/wrong_capture_haml.haml +2 -0
  131. data/test/fixtures/render_app/views/wrong_capture_slim.slim +2 -0
  132. data/test/helper.rb +131 -0
  133. data/test/test_asset_tag_helpers.rb +406 -0
  134. data/test/test_form_builder.rb +1216 -0
  135. data/test/test_form_helpers.rb +1056 -0
  136. data/test/test_format_helpers.rb +251 -0
  137. data/test/test_helpers.rb +10 -0
  138. data/test/test_locale.rb +20 -0
  139. data/test/test_number_helpers.rb +142 -0
  140. data/test/test_output_helpers.rb +157 -0
  141. data/test/test_render_helpers.rb +215 -0
  142. data/test/test_rendering.rb +694 -0
  143. data/test/test_rendering_extensions.rb +14 -0
  144. data/test/test_tag_helpers.rb +131 -0
  145. metadata +241 -0
@@ -0,0 +1,283 @@
1
+ require 'padrino-helpers/form_builder/deprecated_builder_methods'
2
+
3
+ module Padrino
4
+ module Helpers
5
+ module FormBuilder
6
+ # Base class for Padrino Form Builder
7
+ class AbstractFormBuilder
8
+ attr_accessor :template, :object, :multipart
9
+ attr_reader :namespace, :is_nested, :parent_form, :nested_index, :attributes_name, :model_name
10
+
11
+ include DeprecatedBuilderMethods
12
+
13
+ def initialize(template, object, options={})
14
+ @template = template
15
+ fail "FormBuilder template must be initialized" unless template
16
+ @object = object.kind_of?(Symbol) ? build_object(object) : object
17
+ fail "FormBuilder object must be present. If there's no object, use a symbol instead (i.e. :user)" unless object
18
+ @options = options
19
+ @namespace = options[:namespace]
20
+ @model_name = options[:as] || @object.class.to_s.underscore.gsub(/\//, '_')
21
+ nested = options[:nested]
22
+ if @is_nested = nested && (nested_parent = nested[:parent]) && nested_parent.respond_to?(:object)
23
+ @parent_form = nested_parent
24
+ @nested_index = nested[:index]
25
+ @attributes_name = "#{nested[:association]}_attributes"
26
+ end
27
+ end
28
+
29
+ def error_messages(*params)
30
+ @template.error_messages_for object, *params
31
+ end
32
+
33
+ def error_message_on(field, options={})
34
+ @template.error_message_on object, field, options
35
+ end
36
+
37
+ def label(field, options={}, &block)
38
+ options[:id] ||= nil
39
+ options[:caption] ||= I18n.t("#{model_name}.attributes.#{field}", :count => 1, :default => field.to_s.humanize, :scope => :models) + ': '
40
+ @template.label_tag(field_id(field), default_options(field, options), &block)
41
+ end
42
+
43
+ def hidden_field(field, options={})
44
+ @template.hidden_field_tag field_name(field), default_options(field, options)
45
+ end
46
+
47
+ def text_field(field, options={})
48
+ @template.text_field_tag field_name(field), default_options(field, options)
49
+ end
50
+
51
+ def number_field(field, options={})
52
+ @template.number_field_tag field_name(field), default_options(field, options)
53
+ end
54
+
55
+ def telephone_field(field, options={})
56
+ @template.telephone_field_tag field_name(field), default_options(field, options)
57
+ end
58
+ alias_method :phone_field, :telephone_field
59
+
60
+ def email_field(field, options={})
61
+ @template.email_field_tag field_name(field), default_options(field, options)
62
+ end
63
+
64
+ def search_field(field, options={})
65
+ @template.search_field_tag field_name(field), default_options(field, options)
66
+ end
67
+
68
+ def url_field(field, options={})
69
+ @template.url_field_tag field_name(field), default_options(field, options)
70
+ end
71
+
72
+ def text_area(field, options={})
73
+ @template.text_area_tag field_name(field), default_options(field, options)
74
+ end
75
+
76
+ def password_field(field, options={})
77
+ @template.password_field_tag field_name(field), default_options(field, options)
78
+ end
79
+
80
+ def select(field, options={})
81
+ @template.select_tag field_name(field), default_options(field, options)
82
+ end
83
+
84
+ def check_box_group(field, options={})
85
+ labeled_group(field, options) do |attributes|
86
+ @template.check_box_tag(field_name(field)+'[]', attributes)
87
+ end
88
+ end
89
+
90
+ def radio_button_group(field, options={})
91
+ labeled_group(field, options) do |attributes|
92
+ @template.radio_button_tag(field_name(field), attributes)
93
+ end
94
+ end
95
+
96
+ def check_box(field, options={})
97
+ options = default_options(field, options, :value => '1')
98
+ options[:checked] = true if is_checked?(field, options)
99
+ name = field_name(field)
100
+ html = @template.hidden_field_tag(name, :value => options.delete(:uncheck_value) || '0')
101
+ html << @template.check_box_tag(name, options)
102
+ end
103
+
104
+ def radio_button(field, options={})
105
+ options = default_options(field, options)
106
+ options[:checked] = true if is_checked?(field, options)
107
+ options[:id] = field_id(field, options[:value])
108
+ @template.radio_button_tag field_name(field), options
109
+ end
110
+
111
+ def file_field(field, options={})
112
+ self.multipart = true
113
+ @template.file_field_tag field_name(field), default_options(field, options).except(:value)
114
+ end
115
+
116
+ def submit(*args)
117
+ @template.submit_tag *args
118
+ end
119
+
120
+ def image_submit(source, options={})
121
+ @template.image_submit_tag source, options
122
+ end
123
+
124
+ ##
125
+ # Supports nested fields for a child model within a form.
126
+ # f.fields_for :addresses
127
+ # f.fields_for :addresses, address
128
+ # f.fields_for :addresses, @addresses
129
+ # f.fields_for :addresses, address, index: i
130
+ def fields_for(child_association, collection=nil, options={}, &block)
131
+ default_collection = self.object.send(child_association)
132
+ collection ||= default_collection
133
+ include_index = default_collection.respond_to?(:each)
134
+
135
+ nested_options = { :parent => self, :association => child_association }
136
+ Array(collection).each_with_index.inject(ActiveSupport::SafeBuffer.new) do |all,(child_instance,index)|
137
+ nested_options[:index] = options[:index] || (include_index ? index : nil)
138
+ all << @template.fields_for(child_instance, { :nested => nested_options, :builder => self.class }, &block) << "\n"
139
+ end
140
+ end
141
+
142
+ def csrf_token_field
143
+ @template.csrf_token_field
144
+ end
145
+
146
+ protected
147
+
148
+ # Returns the known field types for a Formbuilder.
149
+ def self.field_types
150
+ [:hidden_field, :text_field, :text_area, :password_field, :file_field, :radio_button, :check_box, :select]
151
+ end
152
+
153
+ ##
154
+ # Returns the name for the given field.
155
+ # field_name(:username) => "user[username]"
156
+ # field_name(:number) => "user[telephone_attributes][number]"
157
+ # field_name(:street) => "user[addresses_attributes][0][street]"
158
+ def field_name(field=nil)
159
+ result = field_name_fragment
160
+ result << "[#{field}]" unless field.blank?
161
+ result
162
+ end
163
+
164
+ ##
165
+ # Returns the id for the given field.
166
+ # field_id(:username) => "user_username"
167
+ # field_id(:gender, :male) => "user_gender_male"
168
+ # field_name(:number) => "user_telephone_attributes_number"
169
+ # field_name(:street) => "user_addresses_attributes_0_street"
170
+ def field_id(field=nil, value=nil)
171
+ result = (namespace && !is_nested) ? "#{namespace}_" : ''
172
+ result << field_id_fragment
173
+ result << "_#{field}" unless field.blank?
174
+ result << "_#{value}" unless value.blank?
175
+ result
176
+ end
177
+
178
+ ##
179
+ # Returns the child object if it exists.
180
+ #
181
+ def nested_object_id
182
+ is_nested && object.respond_to?(:new_record?) && !object.new_record? && object.id
183
+ end
184
+
185
+ ##
186
+ # Returns the value for the object's field.
187
+ #
188
+ def field_value(field)
189
+ @object.respond_to?(field) ? @object.send(field) : ''
190
+ end
191
+
192
+ ##
193
+ # Returns a record from template instance or create a record of specified class.
194
+ #
195
+ def build_object(symbol)
196
+ @template.instance_variable_get("@#{symbol}") || symbol.to_s.camelize.constantize.new
197
+ end
198
+
199
+ ##
200
+ # Builds a group of labels for radios or checkboxes.
201
+ #
202
+ def labeled_group(field, options={})
203
+ options = { :id => field_id(field), :selected => field_value(field) }.update(options)
204
+ options.update(error_class(field)){ |_,*values| values.compact.join(' ') }
205
+ selected_values = resolve_checked_values(field, options)
206
+ variants_for_group(options).inject(ActiveSupport::SafeBuffer.new) do |html, (caption,value)|
207
+ variant_id = "#{options[:id]}_#{value}"
208
+ attributes = { :value => value, :id => variant_id, :checked => selected_values.include?(value) }
209
+ caption = yield(attributes) << ' ' << caption
210
+ html << @template.label_tag("#{field_name(field)}[]", :for => variant_id, :caption => caption)
211
+ end
212
+ end
213
+
214
+ private
215
+
216
+ def is_checked?(field, options)
217
+ !options.has_key?(:checked) && [options[:value].to_s, 'true'].include?(field_value(field).to_s)
218
+ end
219
+
220
+ def variants_for_group(options)
221
+ if variants = options[:options]
222
+ variants.map{ |caption, value| [caption.to_s, (value||caption).to_s] }
223
+ elsif collection = options[:collection]
224
+ collection.map{ |variant| field_values(variant, options) }
225
+ else
226
+ []
227
+ end
228
+ end
229
+
230
+ def resolve_checked_values(field, options)
231
+ selected_values = Array(options[:selected] || field_value(field))
232
+ if options[:collection]
233
+ _, id_method = *field_methods(options)
234
+ selected_values.map do |value|
235
+ (value.respond_to?(id_method) ? value.send(id_method) : value).to_s
236
+ end
237
+ else
238
+ selected_values
239
+ end
240
+ end
241
+
242
+ def field_methods(options)
243
+ options[:fields] || [:name, :id]
244
+ end
245
+
246
+ def field_values(object, options)
247
+ field_methods(options).map{ |field| object.send(field).to_s }
248
+ end
249
+
250
+ def field_name_fragment
251
+ if is_nested
252
+ fragment = parent_form.field_name.dup << "[#{attributes_name}"
253
+ fragment << "][#{nested_index}" if nested_index
254
+ fragment << "]"
255
+ else
256
+ "#{model_name}"
257
+ end
258
+ end
259
+
260
+ def field_id_fragment
261
+ if is_nested
262
+ fragment = parent_form.field_id.dup << "_#{attributes_name}"
263
+ fragment << "_#{nested_index}" if nested_index
264
+ fragment
265
+ else
266
+ "#{model_name}"
267
+ end
268
+ end
269
+
270
+ def error_class(field)
271
+ error = @object.errors[field] if @object.respond_to?(:errors)
272
+ error.blank? ? {} : { :class => 'invalid' }
273
+ end
274
+
275
+ def default_options(field, options, defaults={})
276
+ { :value => field_value(field),
277
+ :id => field_id(field)
278
+ }.update(defaults).update(options).update(error_class(field)){ |_,*values| values.compact.join(' ') }
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,92 @@
1
+ module Padrino
2
+ module Helpers
3
+ module FormBuilder
4
+ module DeprecatedBuilderMethods
5
+ ##
6
+ # Returns true if the value matches the value in the field.
7
+ # field_has_value?(:gender, 'male')
8
+ def values_matches_field?(field, value)
9
+ logger.warn "##{__method__} is deprecated"
10
+ value.present? && (field_value(field).to_s == value.to_s || field_value(field).to_s == 'true')
11
+ end
12
+
13
+ ##
14
+ # Add a :invalid css class to the field if it contain an error.
15
+ #
16
+ def field_error(field, options)
17
+ logger.warn "##{__method__} is deprecated"
18
+ error = @object.errors[field] rescue nil
19
+ error.blank? ? options[:class] : [options[:class], :invalid].flatten.compact.join(" ")
20
+ end
21
+
22
+ def nested_form?
23
+ logger.warn "##{__method__} is deprecated"
24
+ @options[:nested] && @options[:nested][:parent] && @options[:nested][:parent].respond_to?(:object)
25
+ is_nested && object.respond_to?(:new_record?) && !object.new_record? && object.id
26
+ end
27
+
28
+ ##
29
+ # Returns a new record of the type specified in the object
30
+ #
31
+ def build_object(object_or_symbol)
32
+ logger.warn "##{__method__} is deprecated"
33
+ object_or_symbol.is_a?(Symbol) ? @template.instance_variable_get("@#{object_or_symbol}") || object_class(object_or_symbol).new : object_or
34
+ end
35
+
36
+ ##
37
+ # Returns the object's models name.
38
+ #
39
+ def object_model_name(explicit_object=object)
40
+ logger.warn "##{__method__} is deprecated"
41
+ return @options[:as] if root_form? && @options[:as].is_a?(Symbol)
42
+ explicit_object.is_a?(Symbol) ? explicit_object : explicit_object.class.to_s.underscore.gsub(/\//, '_')
43
+ end
44
+
45
+ ##
46
+ # Returns the class type for the given object.
47
+ #
48
+ def object_class(explicit_object)
49
+ logger.warn "##{__method__} is deprecated"
50
+ explicit_object.is_a?(Symbol) ? explicit_object.to_s.camelize.constantize : explicit_object.class
51
+ @object.respond_to?(field) ? @object.send(field) : ''
52
+ end
53
+
54
+ ##
55
+ # Returns true if this form is the top-level (not nested).
56
+ # Returns a record from template instance or create a record of specified class.
57
+ #
58
+ def root_form?
59
+ logger.warn "##{__method__} is deprecated"
60
+ !nested_form?
61
+ end
62
+
63
+ def build_object(symbol)
64
+ logger.warn "##{__method__} is deprecated"
65
+ @template.instance_variable_get("@#{symbol}") || symbol.to_s.camelize.constantize.new
66
+ end
67
+
68
+ def field_result
69
+ logger.warn "##{__method__} is deprecated"
70
+ result = []
71
+ result << object_model_name if root_form?
72
+ result
73
+ end
74
+
75
+ def merge_default_options!(field, options)
76
+ logger.warn "##{__method__} is deprecated"
77
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
78
+ options.merge!(:class => field_error(field, options))
79
+ end
80
+
81
+ def result_options
82
+ logger.warn "##{__method__} is deprecated"
83
+ {
84
+ :parent_form => @options[:nested][:parent],
85
+ :nested_index => @options[:nested][:index],
86
+ :attributes_name => "#{@options[:nested][:association]}_attributes"
87
+ }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/abstract_form_builder') unless defined?(AbstractFormBuilder)
2
+
3
+ module Padrino
4
+ module Helpers
5
+ module FormBuilder
6
+ class StandardFormBuilder < AbstractFormBuilder
7
+ ##
8
+ # StandardFormBuilder
9
+ #
10
+ # text_field_block(:username, { :class => 'long' }, { :class => 'wide-label' })
11
+ # text_area_block(:summary, { :class => 'long' }, { :class => 'wide-label' })
12
+ # password_field_block(:password, { :class => 'long' }, { :class => 'wide-label' })
13
+ # file_field_block(:photo, { :class => 'long' }, { :class => 'wide-label' })
14
+ # check_box_block(:remember_me, { :class => 'long' }, { :class => 'wide-label' })
15
+ # select_block(:color, :options => ['green', 'black'])
16
+ #
17
+ (self.field_types - [ :hidden_field, :radio_button ]).each do |field_type|
18
+ class_eval <<-EOF
19
+ def #{field_type}_block(field, options={}, label_options={})
20
+ label_options.reverse_merge!(:caption => options.delete(:caption)) if options[:caption]
21
+ field_html = label(field, label_options)
22
+ field_html << #{field_type}(field, options)
23
+ @template.content_tag(:p, field_html)
24
+ end
25
+ EOF
26
+ end
27
+
28
+ def submit_block(caption, options={})
29
+ submit_html = self.submit(caption, options)
30
+ @template.content_tag(:p, submit_html)
31
+ end
32
+
33
+ def image_submit_block(source, options={})
34
+ submit_html = self.image_submit(source, options)
35
+ @template.content_tag(:p, submit_html)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,633 @@
1
+ require 'padrino-helpers/form_helpers/errors'
2
+ require 'padrino-helpers/form_helpers/options'
3
+ require 'padrino-helpers/form_helpers/security'
4
+
5
+ module Padrino
6
+ module Helpers
7
+ ##
8
+ # Helpers related to producing form related tags and inputs into templates.
9
+ #
10
+ module FormHelpers
11
+ def self.included(base)
12
+ base.send(:include, FormHelpers::Errors)
13
+ base.send(:include, FormHelpers::Options)
14
+ base.send(:include, FormHelpers::Security)
15
+ end
16
+
17
+ ##
18
+ # Constructs a form for object using given or default form_builder.
19
+ #
20
+ # @param [Object] object
21
+ # The object for which the form is being built.
22
+ # @param [String] URL
23
+ # The url this form will submit to.
24
+ # @param [Hash] options
25
+ # The settings associated with this form.
26
+ # Accepts a :namespace option that will be prepended to the id attributes of the form's elements.
27
+ # Also accepts HTML options.
28
+ # @option settings [String] :builder ("StandardFormBuilder")
29
+ # The FormBuilder class to use such as StandardFormBuilder.
30
+ # @option settings [Symbol] :as
31
+ # Sets custom form object name.
32
+ # @param [Proc] block
33
+ # The fields and content inside this form.
34
+ #
35
+ # @yield [AbstractFormBuilder] The form builder used to compose fields.
36
+ #
37
+ # @return [String] The html object-backed form with the specified options and input fields.
38
+ #
39
+ # @example
40
+ # form_for :user, '/register' do |f| ... end
41
+ # form_for @user, '/register', :id => 'register' do |f| ... end
42
+ # form_for @user, '/register', :as => :customer do |f| ... end
43
+ #
44
+ def form_for(object, url, options={}, &block)
45
+ instance = builder_instance(object, options)
46
+ # this can erect instance.multipart flag if the block calls instance.file_field
47
+ html = capture_html(instance, &block)
48
+ options = { :multipart => instance.multipart }.update(options.except(:namespace, :as))
49
+ form_tag(url, options) { html }
50
+ end
51
+
52
+ ##
53
+ # Constructs form fields for an object using given or default form_builder.
54
+ # Used within an existing form to allow alternate objects within one form.
55
+ #
56
+ # @param [Object] object
57
+ # The object for which the fields are being built.
58
+ # @param [Hash] options
59
+ # The settings associated with these fields. Accepts HTML options.
60
+ # @param [Proc] block
61
+ # The content inside this set of fields.
62
+ #
63
+ # @return [String] The html fields with the specified options.
64
+ #
65
+ # @example
66
+ # fields_for @user.assignment do |assignment| ... end
67
+ # fields_for :assignment do |assigment| ... end
68
+ #
69
+ def fields_for(object, options={}, &block)
70
+ instance = builder_instance(object, options)
71
+ fields_html = capture_html(instance, &block)
72
+ fields_html << instance.hidden_field(:id) if instance.send(:nested_object_id)
73
+ concat_content fields_html
74
+ end
75
+
76
+ ##
77
+ # Constructs a form without object based on options.
78
+ #
79
+ # @param [String] url
80
+ # The URL this form will submit to.
81
+ # @param [Hash] options
82
+ # The html options associated with this form.
83
+ # @param [Proc] block
84
+ # The fields and content inside this form.
85
+ #
86
+ # @return [String] The HTML form with the specified options and input fields.
87
+ #
88
+ # @example
89
+ # form_tag '/register', :class => "registration_form" do ... end
90
+ #
91
+ def form_tag(url, options={}, &block)
92
+ options = {
93
+ :action => url,
94
+ :protect_from_csrf => is_protected_from_csrf?,
95
+ 'accept-charset' => 'UTF-8'
96
+ }.update(options)
97
+ options[:enctype] = 'multipart/form-data' if options.delete(:multipart)
98
+
99
+ if (desired_method = options[:method]) =~ /get/i
100
+ options.delete(:protect_from_csrf)
101
+ else
102
+ options[:method] = 'post'
103
+ end
104
+ inner_form_html = hidden_form_method_field(desired_method)
105
+ inner_form_html << csrf_token_field if options.delete(:protect_from_csrf)
106
+ concat_content content_tag(:form, inner_form_html << capture_html(&block), options)
107
+ end
108
+
109
+ ##
110
+ # Returns the hidden method field for 'put' and 'delete' forms.
111
+ # Only 'get' and 'post' are allowed within browsers;
112
+ # 'put' and 'delete' are just specified using hidden fields with form action still 'put'.
113
+ #
114
+ # @param [String] desired_method
115
+ # The method this hidden field represents (i.e put or delete).
116
+ #
117
+ # @return [String] The hidden field representing the +desired_method+ for the form.
118
+ #
119
+ # @example
120
+ # # Generate: <input name="_method" value="delete" />
121
+ # hidden_form_method_field('delete')
122
+ #
123
+ def hidden_form_method_field(desired_method)
124
+ return ActiveSupport::SafeBuffer.new if desired_method.blank? || desired_method.to_s =~ /get|post/i
125
+ hidden_field_tag(:_method, :value => desired_method)
126
+ end
127
+
128
+ ##
129
+ # Constructs a field_set to group fields with given options.
130
+ #
131
+ # @overload field_set_tag(legend=nil, options={}, &block)
132
+ # @param [String] legend The legend caption for the fieldset
133
+ # @param [Hash] options The html options for the fieldset.
134
+ # @param [Proc] block The content inside the fieldset.
135
+ # @overload field_set_tag(options={}, &block)
136
+ # @param [Hash] options The html options for the fieldset.
137
+ # @param [Proc] block The content inside the fieldset.
138
+ #
139
+ # @return [String] The html for the fieldset tag based on given +options+.
140
+ #
141
+ # @example
142
+ # field_set_tag(:class => "office-set") { }
143
+ # field_set_tag("Office", :class => 'office-set') { }
144
+ #
145
+ def field_set_tag(*args, &block)
146
+ options = args.extract_options!
147
+ legend_text = args.first
148
+ legend_html = legend_text.blank? ? ActiveSupport::SafeBuffer.new : content_tag(:legend, legend_text)
149
+ concat_content content_tag(:fieldset, legend_html << capture_html(&block), options)
150
+ end
151
+
152
+ ##
153
+ # Constructs a label tag from the given options.
154
+ #
155
+ # @param [String] name
156
+ # The name of the field to label.
157
+ # @param [Hash] options
158
+ # The html options for this label.
159
+ # @option options :caption
160
+ # The caption for this label.
161
+ # @param [Proc] block
162
+ # The content to be inserted into the label.
163
+ #
164
+ # @return [String] The html for this label with the given +options+.
165
+ #
166
+ # @example
167
+ # label_tag :username, :class => 'long-label'
168
+ # label_tag :username, :class => 'long-label' do ... end
169
+ #
170
+ def label_tag(name, options={}, &block)
171
+ options = { :caption => "#{name.to_s.humanize}: ", :for => name }.update(options)
172
+ caption_text = ActiveSupport::SafeBuffer.new << options.delete(:caption)
173
+ caption_text << "<span class='required'>*</span> ".html_safe if options.delete(:required)
174
+
175
+ if block_given?
176
+ concat_content content_tag(:label, caption_text << capture_html(&block), options)
177
+ else
178
+ content_tag(:label, caption_text, options)
179
+ end
180
+ end
181
+
182
+ ##
183
+ # Creates a text field input with the given name and options.
184
+ #
185
+ # @macro [new] text_field
186
+ # @param [Symbol] name
187
+ # The name of the input to create.
188
+ # @param [Hash] options
189
+ # The HTML options to include in this field.
190
+ #
191
+ # @option options [String] :id
192
+ # Specifies a unique identifier for the field.
193
+ # @option options [String] :class
194
+ # Specifies the stylesheet class of the field.
195
+ # @option options [String] :name
196
+ # Specifies the name of the field.
197
+ # @option options [String] :accesskey
198
+ # Specifies a shortcut key to access the field.
199
+ # @option options [Integer] :tabindex
200
+ # Specifies the tab order of the field.
201
+ # @option options [Integer] :maxlength
202
+ # Specifies the maximum length, in characters, of the field.
203
+ # @option options [Integer] :size
204
+ # Specifies the width, in characters, of the field.
205
+ # @option options [String] :placeholder
206
+ # Specifies a short hint that describes the expected value of the field.
207
+ # @option options [Boolean] :hidden
208
+ # Specifies whether or not the field is hidden from view.
209
+ # @option options [Boolean] :spellcheck
210
+ # Specifies whether or not the field should have it's spelling and grammar checked for errors.
211
+ # @option options [Boolean] :draggable
212
+ # Specifies whether or not the field is draggable. (true, false, :auto).
213
+ # @option options [String] :pattern
214
+ # Specifies the regular expression pattern that the field's value is checked against.
215
+ # @option options [Symbol] :autocomplete
216
+ # Specifies whether or not the field should have autocomplete enabled. (:on, :off).
217
+ # @option options [Boolean] :autofocus
218
+ # Specifies whether or not the field should automatically get focus when the page loads.
219
+ # @option options [Boolean] :required
220
+ # Specifies whether or not the field is required to be completed before the form is submitted.
221
+ # @option options [Boolean] :readonly
222
+ # Specifies whether or not the field is read only.
223
+ # @option options [Boolean] :disabled
224
+ # Specifies whether or not the field is disabled.
225
+ #
226
+ # @return [String]
227
+ # Generated HTML with specified +options+.
228
+ #
229
+ # @example
230
+ # text_field_tag :first_name, :maxlength => 40, :required => true
231
+ # # => <input name="first_name" maxlength="40" required type="text" />
232
+ #
233
+ # text_field_tag :last_name, :class => 'string', :size => 40
234
+ # # => <input name="last_name" class="string" size="40" type="text" />
235
+ #
236
+ # text_field_tag :username, :placeholder => 'Your Username'
237
+ # # => <input name="username" placeholder="Your Username" type="text" />
238
+ #
239
+ def text_field_tag(name, options={})
240
+ input_tag(:text, { :name => name }.update(options))
241
+ end
242
+
243
+ ##
244
+ # Creates a number field input with the given name and options.
245
+ #
246
+ # @macro [new] number_field
247
+ # @param [Symbol] name
248
+ # The name of the input to create.
249
+ # @param [Hash] options
250
+ # The HTML options to include in this field.
251
+ #
252
+ # @option options [String] :id
253
+ # Specifies a unique identifier for the field.
254
+ # @option options [String] :class
255
+ # Specifies the stylesheet class of the field.
256
+ # @option options [String] :name
257
+ # Specifies the name of the field.
258
+ # @option options [String] :accesskey
259
+ # Specifies a shortcut key to access the field.
260
+ # @option options [Integer] :tabindex
261
+ # Specifies the tab order of the field.
262
+ # @option options [Integer] :min
263
+ # Specifies the minimum value of the field.
264
+ # @option options [Integer] :max
265
+ # Specifies the maximum value of the field.
266
+ # @option options [Integer] :step
267
+ # Specifies the legal number intervals of the field.
268
+ # @option options [Boolean] :hidden
269
+ # Specifies whether or not the field is hidden from view.
270
+ # @option options [Boolean] :spellcheck
271
+ # Specifies whether or not the field should have it's spelling and grammar checked for errors.
272
+ # @option options [Boolean] :draggable
273
+ # Specifies whether or not the field is draggable. (true, false, :auto).
274
+ # @option options [String] :pattern
275
+ # Specifies the regular expression pattern that the field's value is checked against.
276
+ # @option options [Symbol] :autocomplete
277
+ # Specifies whether or not the field should have autocomplete enabled. (:on, :off).
278
+ # @option options [Boolean] :autofocus
279
+ # Specifies whether or not the field should automatically get focus when the page loads.
280
+ # @option options [Boolean] :required
281
+ # Specifies whether or not the field is required to be completeled before the form is submitted.
282
+ # @option options [Boolean] :readonly
283
+ # Specifies whether or not the field is read only.
284
+ # @option options [Boolean] :disabled
285
+ # Specifies whether or not the field is disabled.
286
+ #
287
+ # @return [String]
288
+ # Generated HTML with specified +options+.
289
+ #
290
+ # @example
291
+ # number_field_tag :quantity, :class => 'numeric'
292
+ # # => <input name="quantity" class="numeric" type="number" />
293
+ #
294
+ # number_field_tag :zip_code, :pattern => /[0-9]{5}/
295
+ # # => <input name="zip_code" pattern="[0-9]{5}" type="number" />
296
+ #
297
+ # number_field_tag :credit_card, :autocomplete => :off
298
+ # # => <input name="credit_card" autocomplete="off" type="number" />
299
+ #
300
+ # number_field_tag :age, :min => 18, :max => 120, :step => 1
301
+ # # => <input name="age" min="18" max="120" step="1" type="number" />
302
+ #
303
+ def number_field_tag(name, options={})
304
+ input_tag(:number, { :name => name }.update(options))
305
+ end
306
+
307
+ ##
308
+ # Creates a telephone field input with the given name and options.
309
+ #
310
+ # @macro text_field
311
+ #
312
+ # @example
313
+ # telephone_field_tag :phone_number, :class => 'string'
314
+ # # => <input name="phone_number" class="string" type="tel" />
315
+ #
316
+ # telephone_field_tag :cell_phone, :tabindex => 1
317
+ # telephone_field_tag :work_phone, :tabindex => 2
318
+ # telephone_field_tag :home_phone, :tabindex => 3
319
+ #
320
+ # # => <input name="cell_phone" tabindex="1" type="tel" />
321
+ # # => <input name="work_phone" tabindex="2" type="tel" />
322
+ # # => <input name="home_phone" tabindex="3" type="tel" />
323
+ #
324
+ def telephone_field_tag(name, options={})
325
+ input_tag(:tel, { :name => name }.update(options))
326
+ end
327
+ alias_method :phone_field_tag, :telephone_field_tag
328
+
329
+ ##
330
+ # Creates an email field input with the given name and options.
331
+ #
332
+ # @macro text_field
333
+ #
334
+ # @example
335
+ # email_field_tag :email, :placeholder => 'you@example.com'
336
+ # # => <input name="email" placeholder="you@example.com" type="email" />
337
+ #
338
+ # email_field_tag :email, :value => 'padrinorb@gmail.com', :readonly => true
339
+ # # => <input name="email" value="padrinorb@gmail.com" readonly type="email" />
340
+ #
341
+ def email_field_tag(name, options={})
342
+ input_tag(:email, { :name => name }.update(options))
343
+ end
344
+
345
+ ##
346
+ # Creates a search field input with the given name and options.
347
+ #
348
+ # @macro text_field
349
+ #
350
+ # @example
351
+ # search_field_tag :search, :placeholder => 'Search this website...'
352
+ # # => <input name="search" placeholder="Search this website..." type="search" />
353
+ #
354
+ # search_field_tag :search, :maxlength => 15, :class => ['search', 'string']
355
+ # # => <input name="search" maxlength="15" class="search string" />
356
+ #
357
+ # search_field_tag :search, :id => 'search'
358
+ # # => <input name="search" id="search" type="search" />
359
+ #
360
+ # search_field_tag :search, :autofocus => true
361
+ # # => <input name="search" autofocus type="search" />
362
+ #
363
+ def search_field_tag(name, options={})
364
+ input_tag(:search, { :name => name }.update(options))
365
+ end
366
+
367
+ ##
368
+ # Creates a URL field input with the given name and options.
369
+ #
370
+ # @macro text_field
371
+ #
372
+ # @example
373
+ # url_field_tag :favorite_website, :placeholder => 'http://padrinorb.com'
374
+ # <input name="favorite_website" placeholder="http://padrinorb.com." type="url" />
375
+ #
376
+ # url_field_tag :home_page, :class => 'string url'
377
+ # <input name="home_page" class="string url", type="url" />
378
+ #
379
+ def url_field_tag(name, options={})
380
+ input_tag(:url, { :name => name }.update(options))
381
+ end
382
+
383
+ ##
384
+ # Constructs a hidden field input from the given options.
385
+ #
386
+ # @example
387
+ # hidden_field_tag :session_key, :value => "__secret__"
388
+ #
389
+ def hidden_field_tag(name, options={})
390
+ input_tag(:hidden, { :name => name }.update(options))
391
+ end
392
+
393
+ ##
394
+ # Constructs a text area input from the given options.
395
+ #
396
+ # @example
397
+ # text_area_tag :username, :class => 'long', :value => "Demo?"
398
+ #
399
+ def text_area_tag(name, options={})
400
+ inner_html = "\n#{options.delete(:value)}"
401
+ options = { :name => name, :rows => "", :cols => "" }.update(options)
402
+ content_tag(:textarea, inner_html, options)
403
+ end
404
+
405
+ ##
406
+ # Constructs a password field input from the given options.
407
+ #
408
+ # @example
409
+ # password_field_tag :password, :class => 'long'
410
+ #
411
+ # @api public
412
+ def password_field_tag(name, options={})
413
+ input_tag(:password, { :name => name }.update(options))
414
+ end
415
+
416
+ ##
417
+ # Constructs a check_box from the given options.
418
+ #
419
+ # @example
420
+ # check_box_tag :remember_me, :value => 'Yes'
421
+ #
422
+ def check_box_tag(name, options={})
423
+ input_tag(:checkbox, { :name => name, :value => '1' }.update(options))
424
+ end
425
+
426
+ ##
427
+ # Constructs a radio_button from the given options.
428
+ #
429
+ # @example
430
+ # radio_button_tag :remember_me, :value => 'true'
431
+ #
432
+ def radio_button_tag(name, options={})
433
+ input_tag(:radio, { :name => name }.update(options))
434
+ end
435
+
436
+ ##
437
+ # Constructs a file field input from the given options.
438
+ #
439
+ # @example
440
+ # file_field_tag :photo, :class => 'long'
441
+ #
442
+ # @api public
443
+ def file_field_tag(name, options={})
444
+ name = "#{name}[]" if options[:multiple]
445
+ input_tag(:file, { :name => name }.update(options))
446
+ end
447
+
448
+ ##
449
+ # Constructs a select from the given options.
450
+ #
451
+ # @example
452
+ # options = [['caption', 'value'], ['Green', 'green1'], ['Blue', 'blue1'], ['Black', "black1"]]
453
+ # options = ['option', 'red', 'yellow' ]
454
+ # select_tag(:favorite_color, :options => ['red', 'yellow'], :selected => 'green1')
455
+ # select_tag(:country, :collection => @countries, :fields => [:name, :code], :include_blank => 'None')
456
+ #
457
+ # # Optgroups can be generated using :grouped_options => (Hash or nested Array)
458
+ # grouped_options = [['Friends',['Yoda',['Obiwan',1]]],['Enemies',['Palpatine',['Darth Vader',3]]]]
459
+ # grouped_options = {'Friends' => ['Yoda',['Obiwan',1]],'Enemies' => ['Palpatine',['Darth Vader',3]]}
460
+ # select_tag(:color, :grouped_options => [['warm',['red','yellow']],['cool',['blue', 'purple']]])
461
+ #
462
+ # # Optgroups can be generated using the rails-style attribute hash.
463
+ # grouped_options = {
464
+ # "Friends" => ["Yoda", ["Obiwan", 2, {:magister => 'no'}], {:lame => 'yes'}],
465
+ # "Enemies" => [["Palpatine", "Palpatine", {:scary => 'yes', :old => 'yes'}], ["Darth Vader", 3, {:disabled => true}]]
466
+ # }
467
+ # select_tag(:name, :grouped_options => grouped_options)
468
+ #
469
+ # @param [String] name
470
+ # The name of the input field.
471
+ # @param [Hash] options
472
+ # The html options for the input field.
473
+ # @option options [Array<String, Array>] :options
474
+ # Explicit options to display in the select. Can be strings or string tuples.
475
+ # @option options [Array<Array>] :grouped_options
476
+ # List of options for each group in the select. See examples for details.
477
+ # @option options [Array<Object>] :collection
478
+ # Collection of objects used as options in the select.
479
+ # @option options [Array<Symbol>] :fields
480
+ # The attributes used as "label" and "value" for each +collection+ object.
481
+ # @option options [String] :selected (nil)
482
+ # The option value initially selected.
483
+ # @option options [Boolean] :include_blank (false)
484
+ # Include a blank option in the select.
485
+ # @option options [Boolean] :multiple (false)
486
+ # Allow multiple options to be selected at once.
487
+ #
488
+ # @return [String] The HTML input field based on the +options+ specified.
489
+ #
490
+ def select_tag(name, options={})
491
+ options = { :name => name }.merge(options)
492
+ options[:name] = "#{options[:name]}[]" if options[:multiple]
493
+ content_tag(:select, extract_option_tags!(options), options)
494
+ end
495
+
496
+ ##
497
+ # Constructs a button input from the given options.
498
+ #
499
+ # @param [String] caption
500
+ # The caption for the button.
501
+ # @param [Hash] options
502
+ # The html options for the input field.
503
+ #
504
+ # @return [String] The html button based on the +options+ specified.
505
+ #
506
+ # @example
507
+ # button_tag "Cancel", :class => 'clear'
508
+ #
509
+ def button_tag(caption, options = {})
510
+ input_tag(:button, { :value => caption }.update(options))
511
+ end
512
+
513
+ ##
514
+ # Constructs a submit button from the given options.
515
+ #
516
+ # @param [String] caption (defaults to: +Submit+)
517
+ # The caption for the submit button.
518
+ # @param [Hash] options
519
+ # The html options for the input field.
520
+ #
521
+ # @return [String] The html submit button based on the +options+ specified.
522
+ #
523
+ # @example
524
+ # submit_tag "Create", :class => 'success'
525
+ # submit_tag :class => 'btn'
526
+ #
527
+ def submit_tag(*args)
528
+ options = args.extract_options!
529
+ caption = args.length >= 1 ? args.first : "Submit"
530
+ input_tag(:submit, { :value => caption }.merge(options))
531
+ end
532
+
533
+ ##
534
+ # Constructs a submit button from the given options.
535
+ #
536
+ # @param [String] source
537
+ # The source image path for the button.
538
+ # @param [Hash] options
539
+ # The html options for the input field.
540
+ #
541
+ # @return [String] The html image button based on the +options+ specified.
542
+ #
543
+ # @example
544
+ # image_submit_tag 'form/submit.png'
545
+ #
546
+ def image_submit_tag(source, options={})
547
+ input_tag(:image, { :src => image_path(source) }.update(options))
548
+ end
549
+
550
+ ##
551
+ # Creates a form containing a single button that submits to the URL.
552
+ #
553
+ # @overload button_to(name, url, options={})
554
+ # @param [String] caption The text caption.
555
+ # @param [String] url The url href.
556
+ # @param [Hash] options The html options.
557
+ # @overload button_to(name, options={}, &block)
558
+ # @param [String] url The url href.
559
+ # @param [Hash] options The html options.
560
+ # @param [Proc] block The button content.
561
+ #
562
+ # @option options [Boolean] :multipart
563
+ # If true, this form will support multipart encoding.
564
+ # @option options [String] :remote
565
+ # Instructs ujs handler to handle the submit as ajax.
566
+ # @option options [Symbol] :method
567
+ # Instructs ujs handler to use different http method (i.e :post, :delete).
568
+ # @option options [Hash] :submit_options
569
+ # Hash of any options, that you want to pass to submit_tag (i.e :id, :class)
570
+ #
571
+ # @return [String] Form and button html with specified +options+.
572
+ #
573
+ # @example
574
+ # button_to 'Delete', url(:accounts_destroy, :id => account), :method => :delete, :class => :form
575
+ # # Generates:
576
+ # # <form class="form" action="/admin/accounts/destroy/2" method="post">
577
+ # # <input type="hidden" value="delete" name="_method" />
578
+ # # <input type="submit" value="Delete" />
579
+ # # </form>
580
+ #
581
+ def button_to(*args, &block)
582
+ options = args.extract_options!.dup
583
+ name, url = *args
584
+ options['data-remote'] = 'true' if options.delete(:remote)
585
+ submit_options = options.delete(:submit_options) || {}
586
+ block ||= proc { submit_tag(name, submit_options) }
587
+ form_tag(url || name, options, &block)
588
+ end
589
+
590
+ ##
591
+ # Constructs a range tag from the given options.
592
+ #
593
+ # @example
594
+ # range_field_tag('ranger_with_min_max', :min => 1, :max => 50)
595
+ # range_field_tag('ranger_with_range', :range => 1..5)
596
+ #
597
+ # @param [String] name
598
+ # The name of the range field.
599
+ # @param [Hash] options
600
+ # The html options for the range field.
601
+ # @option options [Integer] :min
602
+ # The min range of the range field.
603
+ # @option options [Integer] :max
604
+ # The max range of the range field.
605
+ # @option options [range] :range
606
+ # The range, in lieu of :min and :max. See examples for details.
607
+ # @return [String] The html range field
608
+ #
609
+ def range_field_tag(name, options = {})
610
+ options = { :name => name }.update(options)
611
+ if range = options.delete(:range)
612
+ options[:min], options[:max] = range.min, range.max
613
+ end
614
+ input_tag(:range, options)
615
+ end
616
+
617
+ private
618
+
619
+ ##
620
+ # Returns an initialized builder instance for the given object and settings.
621
+ #
622
+ # @example
623
+ # builder_instance(@account, :nested => { ... }) => <FormBuilder>
624
+ #
625
+ def builder_instance(object, options={})
626
+ default_builder = respond_to?(:settings) && settings.default_builder || 'StandardFormBuilder'
627
+ builder_class = options.delete(:builder) || default_builder
628
+ builder_class = "Padrino::Helpers::FormBuilder::#{builder_class}".constantize if builder_class.is_a?(String)
629
+ builder_class.new(self, object, options)
630
+ end
631
+ end
632
+ end
633
+ end