forme 1.9.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +70 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +396 -202
  5. data/lib/forme/bs3.rb +19 -5
  6. data/lib/forme/erb.rb +18 -16
  7. data/lib/forme/form.rb +151 -118
  8. data/lib/forme/input.rb +1 -1
  9. data/lib/forme/rails.rb +41 -72
  10. data/lib/forme/raw.rb +2 -2
  11. data/lib/forme/sinatra.rb +6 -2
  12. data/lib/forme/tag.rb +3 -12
  13. data/lib/forme/template.rb +118 -0
  14. data/lib/forme/transformers/error_handler.rb +46 -1
  15. data/lib/forme/transformers/formatter.rb +36 -35
  16. data/lib/forme/transformers/helper.rb +0 -1
  17. data/lib/forme/transformers/inputs_wrapper.rb +6 -6
  18. data/lib/forme/transformers/labeler.rb +19 -0
  19. data/lib/forme/transformers/wrapper.rb +1 -1
  20. data/lib/forme/version.rb +2 -2
  21. data/lib/forme.rb +15 -2
  22. data/lib/roda/plugins/forme.rb +1 -1
  23. data/lib/roda/plugins/forme_erubi_capture.rb +62 -0
  24. data/lib/roda/plugins/forme_route_csrf.rb +16 -20
  25. data/lib/roda/plugins/forme_set.rb +177 -0
  26. data/lib/sequel/plugins/forme.rb +42 -55
  27. data/lib/sequel/plugins/forme_i18n.rb +3 -1
  28. data/lib/sequel/plugins/forme_set.rb +50 -28
  29. data/spec/all.rb +1 -1
  30. data/spec/bs3_reference_spec.rb +18 -18
  31. data/spec/bs3_sequel_plugin_spec.rb +7 -7
  32. data/spec/bs3_spec.rb +23 -11
  33. data/spec/erb_helper.rb +73 -58
  34. data/spec/erubi_capture_helper.rb +202 -0
  35. data/spec/forme_spec.rb +80 -29
  36. data/spec/rails_integration_spec.rb +47 -24
  37. data/spec/roda_integration_spec.rb +459 -48
  38. data/spec/sequel_helper.rb +0 -1
  39. data/spec/sequel_i18n_helper.rb +1 -1
  40. data/spec/sequel_i18n_plugin_spec.rb +3 -2
  41. data/spec/sequel_plugin_spec.rb +25 -8
  42. data/spec/sequel_set_plugin_spec.rb +10 -3
  43. data/spec/shared_erb_specs.rb +75 -0
  44. data/spec/sinatra_integration_spec.rb +5 -6
  45. data/spec/spec_helper.rb +23 -5
  46. metadata +30 -8
  47. data/lib/forme/erb_form.rb +0 -74
data/lib/forme/bs3.rb CHANGED
@@ -80,12 +80,26 @@ module Forme
80
80
  copy_options_to_attributes(ATTRIBUTE_OPTIONS)
81
81
  copy_boolean_options_to_attributes(ATTRIBUTE_BOOLEAN_OPTIONS)
82
82
  handle_key_option
83
+ handle_errors_option
83
84
 
84
85
  Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
85
- # Forme.attr_classes(@attr, 'error') if @opts[:error]
86
+
87
+ if @opts[:error]
88
+ # Forme.attr_classes(@attr, 'error')
89
+ @attr["aria-invalid"] = "true"
90
+ if @opts.fetch(:error_handler, true)
91
+ unless @opts[:error_id]
92
+ if id = @attr[:id] || @attr['id']
93
+ error_id = @attr['aria-describedby'] ||= "#{id}_error_message"
94
+ @opts[:error_id] = error_id
95
+ end
96
+ end
97
+ end
98
+ end
86
99
 
87
100
  if data = opts[:data]
88
101
  data.each do |k, v|
102
+ k = k.to_s.tr("_", "-") if k.is_a?(Symbol) && input.opts[:dasherize_data]
89
103
  sym = :"data-#{k}"
90
104
  @attr[sym] = v unless @attr.has_key?(sym)
91
105
  end
@@ -210,11 +224,11 @@ module Forme
210
224
  Forme.attr_classes(attr, 'inputs')
211
225
  if legend = opts[:legend]
212
226
  form.tag(:fieldset, attr) do
213
- form.emit(form.tag(:legend, opts[:legend_attr], legend))
227
+ form.tag(:legend, opts[:legend_attr], legend)
214
228
  yield
215
229
  end
216
230
  else
217
- form.tag(:fieldset, attr, &Proc.new)
231
+ form.tag(:fieldset, attr, &block)
218
232
  end
219
233
  end
220
234
  end
@@ -230,11 +244,11 @@ module Forme
230
244
  attr = opts[:attr] ? opts[:attr].dup : { :class=>'table table-bordered'}
231
245
  form.tag(:table, attr) do
232
246
  if legend = opts[:legend]
233
- form.emit(form.tag(:caption, opts[:legend_attr], legend))
247
+ form.tag(:caption, opts[:legend_attr], legend)
234
248
  end
235
249
 
236
250
  if (labels = opts[:labels]) && !labels.empty?
237
- form.emit(form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)}))
251
+ form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)})
238
252
  end
239
253
 
240
254
  yield
data/lib/forme/erb.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'forme/erb_form'
3
+ require_relative 'template'
4
4
 
5
5
  module Forme
6
6
  module ERB
@@ -14,23 +14,25 @@ module Forme
14
14
  HIDDEN_TAGS << block
15
15
  end
16
16
 
17
- # Add CSRF token tag by default for POST forms
18
- add_hidden_tag do |tag|
19
- if defined?(::Rack::Csrf) && (form = tag.form) && (env = form.opts[:env]) && env['rack.session'] && (tag.attr[:method] || tag.attr['method']).to_s.upcase == 'POST'
20
- {::Rack::Csrf.field=>::Rack::Csrf.token(env)}
17
+ # This is the module used to add the Forme integration to ERB templates, with optional support for
18
+ # rack_csrf for CSRF handling.
19
+ module Helper
20
+ include Template::Helper
21
+
22
+ private
23
+
24
+ def _forme_form_options(obj, attr, opts)
25
+ super
26
+
27
+ if defined?(::Rack::Csrf) && env['rack.session']
28
+ opts[:_before_post] = lambda do |form|
29
+ form.tag(:input, :type=>:hidden, :name=>::Rack::Csrf.field, :value=>::Rack::Csrf.token(env))
30
+ end
31
+ end
21
32
  end
22
- end
23
33
 
24
- # This is the module used to add the Forme integration
25
- # to ERB.
26
- module Helper
27
- # Create a +Form+ object tied to the current output buffer,
28
- # using the standard ERB hidden tags.
29
- def form(obj=nil, attr={}, opts={}, &block)
30
- h = {:hidden_tags=>Forme::ERB::HIDDEN_TAGS, :env=>env}
31
- h[:output] = @_out_buf if block
32
- (obj.is_a?(Hash) ? attr = attr.merge(h) : opts = opts.merge(h))
33
- Form.form(obj, attr, opts, &block)
34
+ def _forme_form_hidden_tags
35
+ HIDDEN_TAGS
34
36
  end
35
37
  end
36
38
  end
data/lib/forme/form.rb CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  module Forme
4
4
  # The +Form+ class is the main entry point to the library.
5
- # Using the +form+, +input+, +tag+, and +inputs+ methods, one can easily build
6
- # an abstract syntax tree of +Tag+ and +Input+ instances, which can be serialized
7
- # to a string using +to_s+.
5
+ # Using the +form+, +input+, +tag+, and +inputs+ methods, one can return HTML
6
+ # form tag string (or fragments of an HTML form tag).
8
7
  class Form
9
8
  # A hash of options for the form.
10
9
  attr_reader :opts
@@ -16,10 +15,17 @@ module Forme
16
15
  # The hidden tags to automatically add to the form.
17
16
  attr_reader :hidden_tags
18
17
 
18
+ # The attributes used for the form tag for this form.
19
+ attr_reader :form_tag_attributes
20
+
19
21
  # The +serializer+ determines how +Tag+ objects are transformed into strings.
20
22
  # Must respond to +call+ or be a registered symbol.
21
23
  attr_reader :serializer
22
24
 
25
+ # The contents of the form as a string. This should not be mutated by
26
+ # external code.
27
+ attr_reader :to_s
28
+
23
29
  # Use appropriate Form subclass for object based on the current class, if the
24
30
  # object responds to +forme_form_class+.
25
31
  def self.new(obj=nil, opts={})
@@ -30,23 +36,9 @@ module Forme
30
36
  end
31
37
  end
32
38
 
33
- # Create a +Form+ instance and yield it to the block,
34
- # injecting the opening form tag before yielding and
35
- # the closing form tag after yielding.
36
- #
37
- # Argument Handling:
38
- # No args :: Creates a +Form+ object with no options and not associated
39
- # to an +obj+, and with no attributes in the opening tag.
40
- # 1 hash arg :: Treated as opening form tag attributes, creating a
41
- # +Form+ object with no options.
42
- # 1 non-hash arg :: Treated as the +Form+'s +obj+, with empty options
43
- # and no attributes in the opening tag.
44
- # 2 hash args :: First hash is opening attributes, second hash is +Form+
45
- # options.
46
- # 1 non-hash arg, 1-2 hash args :: First argument is +Form+'s obj, second is
47
- # opening attributes, third if provided is
48
- # +Form+'s options.
49
- def self.form(obj=nil, attr={}, opts={}, &block)
39
+ # Parse the args given to #form and return Form instance, form tag attributes,
40
+ # and block for form.
41
+ def self.form_args(obj, attr, opts, &block)
50
42
  f = if obj.is_a?(Hash)
51
43
  raise Error, "Can't provide 3 hash arguments to form" unless opts.empty?
52
44
  opts = attr
@@ -59,13 +51,33 @@ module Forme
59
51
  ins = opts[:inputs]
60
52
  button = opts[:button]
61
53
  if ins || button
62
- block = Proc.new do |form|
63
- form._inputs(ins, opts) if ins
54
+ block = proc do |form|
55
+ form.inputs(ins, opts) if ins
64
56
  yield form if block_given?
65
- form.emit(form.button(button)) if button
57
+ form.button(button) if button
66
58
  end
67
59
  end
68
60
 
61
+ [f, attr, block]
62
+ end
63
+
64
+ # Create a +Form+ instance and yield it to the block. Returns an HTML string
65
+ # for the form tag.
66
+ #
67
+ # Argument Handling:
68
+ # No args :: Creates a +Form+ object with no options and not associated
69
+ # to an +obj+, and with no attributes in the opening tag.
70
+ # 1 hash arg :: Treated as opening form tag attributes, creating a
71
+ # +Form+ object with no options.
72
+ # 1 non-hash arg :: Treated as the +Form+'s +obj+, with empty options
73
+ # and no attributes in the opening tag.
74
+ # 2 hash args :: First hash is opening attributes, second hash is +Form+
75
+ # options.
76
+ # 1 non-hash arg, 1-2 hash args :: First argument is +Form+'s obj, second is
77
+ # opening attributes, third if provided is
78
+ # +Form+'s options.
79
+ def self.form(obj=nil, attr={}, opts={}, &block)
80
+ f, attr, block = form_args(obj, attr, opts, &block)
69
81
  f.form(attr, &block)
70
82
  end
71
83
 
@@ -98,26 +110,38 @@ module Forme
98
110
  @serializer = @opts[:serializer]
99
111
  @input_defaults = @opts[:input_defaults] || {}
100
112
  @hidden_tags = @opts[:hidden_tags]
101
- @nesting = []
113
+ if @hidden_tags && !@hidden_tags.empty? && RUBY_VERSION >= '2'
114
+ uplevel = 6
115
+ uplevel += @opts[:hidden_tags_uplevel] if @opts[:hidden_tags_uplevel]
116
+ warn("The Forme::Form :hidden_tags option is deprecated, please switch to using the :before option", :uplevel=>uplevel)
117
+ end
118
+ @to_s = String.new
102
119
  end
103
120
 
104
- # Create a form tag with the given attributes.
105
- def form(attr={}, &block)
121
+ # Create a form tag with the given attributes. Returns an HTML string for
122
+ # the generated form tag.
123
+ def form(attr={})
106
124
  if obj && !attr[:method] && !attr['method'] && obj.respond_to?(:forme_default_request_method)
107
- attr = attr.merge('method'=>obj.forme_default_request_method)
125
+ attr = Hash[attr]
126
+ attr['method'] = obj.forme_default_request_method
127
+ end
128
+ @form_tag_attributes = attr
129
+
130
+ tag(:form, attr, method(:hidden_form_tags)) do
131
+ before_form_yield
132
+ yield self if block_given?
133
+ after_form_yield
108
134
  end
109
- tag(:form, attr, method(:hidden_form_tags), &block)
110
135
  end
111
136
 
112
- # Empty method designed to ease integration with other libraries where
113
- # Forme is used in template code and some output implicitly
114
- # created by Forme needs to be injected into the template output.
115
- def emit(tag)
137
+ # Whether the method for this form is POST. Only callable after calling
138
+ # #form.
139
+ def post?
140
+ (form_tag_attributes[:method] || form_tag_attributes['method']).to_s.upcase == 'POST'
116
141
  end
117
142
 
118
143
  # Creates an +Input+ with the given +field+ and +opts+ associated with
119
- # the receiver, and add it to the list of children to the currently
120
- # open tag.
144
+ # the receiver. Returns the HTML generated by the given input.
121
145
  #
122
146
  # If the form is associated with an +obj+, or the :obj key exists in
123
147
  # the +opts+ argument, treats the +field+ as a call to the +obj+. If
@@ -129,36 +153,39 @@ module Forme
129
153
  # type (e.g. <tt>:text</tt>, <tt>:textarea</tt>, <tt>:select</tt>), and
130
154
  # an input is created directly with the +field+ and +opts+.
131
155
  def input(field, opts={})
132
- if opts.has_key?(:obj)
133
- opts = opts.dup
134
- obj = opts.delete(:obj)
135
- else
136
- obj = self.obj
137
- end
138
- input = if obj
139
- if obj.respond_to?(:forme_input)
140
- obj.forme_input(self, field, opts.dup)
141
- else
156
+ content_added do
157
+ if opts.has_key?(:obj)
142
158
  opts = opts.dup
143
- opts[:key] = field unless opts.has_key?(:key)
144
- type = opts.delete(:type) || :text
145
- unless opts.has_key?(:value) || type == :file
146
- opts[:value] = if obj.is_a?(Hash)
147
- obj[field]
148
- else
149
- obj.send(field)
159
+ obj = opts.delete(:obj)
160
+ else
161
+ obj = self.obj
162
+ end
163
+
164
+ input = if obj
165
+ if obj.respond_to?(:forme_input)
166
+ obj.forme_input(self, field, opts.dup)
167
+ else
168
+ opts = opts.dup
169
+ opts[:key] = field unless opts.has_key?(:key)
170
+ type = opts.delete(:type) || :text
171
+ unless opts.has_key?(:value) || type == :file
172
+ opts[:value] = if obj.is_a?(Hash)
173
+ obj[field]
174
+ else
175
+ obj.send(field)
176
+ end
150
177
  end
178
+ _input(type, opts)
151
179
  end
152
- _input(type, opts)
180
+ else
181
+ _input(field, opts)
153
182
  end
154
- else
155
- _input(field, opts)
183
+
184
+ self << input
156
185
  end
157
- self << input
158
- input
159
186
  end
160
187
 
161
- # Create a new +Input+ associated with the receiver with the given
188
+ # Return a new +Input+ associated with the receiver with the given
162
189
  # arguments, doing no other processing.
163
190
  def _input(*a)
164
191
  Input.new(self, *a)
@@ -182,6 +209,8 @@ module Forme
182
209
  # wrapper to use for this inputs call. You can use the :nested_inputs_wrapper
183
210
  # option to set the default :inputs_wrapper option for the duration of the block.
184
211
  #
212
+ # Returns the HTML generated by the inputs added to the form.
213
+ #
185
214
  # This can also be called with a single hash argument to just use an options hash:
186
215
  #
187
216
  # f.inputs(:legend=>'Foo') do
@@ -193,32 +222,28 @@ module Forme
193
222
  # f.inputs do
194
223
  # # ...
195
224
  # end
196
- def inputs(inputs=[], opts={}, &block)
197
- _inputs(inputs, opts, &block)
198
- end
199
-
200
- # Internals of #inputs, should be used internally by the library, where #inputs
201
- # is designed for external use.
202
- def _inputs(inputs=[], opts={}) # :nodoc:
203
- if inputs.is_a?(Hash)
204
- opts = inputs.merge(opts)
205
- inputs = []
206
- end
225
+ def inputs(inputs=[], opts={})
226
+ content_added do
227
+ if inputs.is_a?(Hash)
228
+ opts = inputs.merge(opts)
229
+ inputs = []
230
+ end
207
231
 
208
- form_opts = {}
209
- form_opts[:inputs_wrapper] = opts[:nested_inputs_wrapper] if opts[:nested_inputs_wrapper]
210
- TRANSFORMER_TYPES.each do |t|
211
- if opts.has_key?(t) && t != :inputs_wrapper
212
- form_opts[t] = opts[t]
232
+ form_opts = {}
233
+ form_opts[:inputs_wrapper] = opts[:nested_inputs_wrapper] if opts[:nested_inputs_wrapper]
234
+ TRANSFORMER_TYPES.each do |t|
235
+ if opts.has_key?(t) && t != :inputs_wrapper
236
+ form_opts[t] = opts[t]
237
+ end
213
238
  end
214
- end
215
239
 
216
- Forme.transform(:inputs_wrapper, opts, @opts, self, opts) do
217
- with_opts(form_opts) do
218
- inputs.each do |i|
219
- emit(input(*i))
240
+ Forme.transform(:inputs_wrapper, opts, @opts, self, opts) do
241
+ with_opts(form_opts) do
242
+ inputs.each do |i|
243
+ input(*i)
244
+ end
245
+ yield if block_given?
220
246
  end
221
- yield if block_given?
222
247
  end
223
248
  end
224
249
  end
@@ -255,35 +280,34 @@ module Forme
255
280
  end
256
281
 
257
282
  # Creates a +Tag+ associated to the receiver with the given arguments.
258
- # Add the tag to the the list of children for the currently open tag.
259
- # If a block is given, make this tag the currently open tag while inside
260
- # the block.
283
+ # If a block is given, yield to the block inside the generated tag.
284
+ # Returns the HTML added to the form by the addition of this tag.
261
285
  def tag(*a, &block)
262
- tag = _tag(*a)
263
- self << tag
264
- nest(tag, &block) if block
265
- tag
266
- end
267
-
268
- # Aliased for tag. Workaround for issue with rails plugin.
269
- def tag_(*a, &block) # :nodoc:
270
- tag(*a, &block)
286
+ content_added do
287
+ tag = _tag(*a)
288
+ if block
289
+ self << serialize_open(tag)
290
+ if children = tag.children
291
+ children.each{|child| self << child}
292
+ end
293
+ yield self
294
+ self << serialize_close(tag)
295
+ else
296
+ self << tag
297
+ end
298
+ end
271
299
  end
272
300
 
273
- # Creates a :submit +Input+ with the given opts, adding it to the list
274
- # of children for the currently open tag.
301
+ # Creates a :submit +Input+ with the given opts. Returns the generated
302
+ # HTML for the input.
275
303
  def button(opts={})
276
304
  opts = {:value=>opts} if opts.is_a?(String)
277
- input = _input(:submit, opts)
278
- self << input
279
- input
305
+ content_added{self << _input(:submit, opts)}
280
306
  end
281
307
 
282
- # Add the +Input+/+Tag+ instance given to the currently open tag.
308
+ # Add the +Input+/+Tag+ instance to the HTML buffer.
283
309
  def <<(tag)
284
- if n = @nesting.last
285
- n << tag
286
- end
310
+ @to_s << tag.to_s
287
311
  end
288
312
 
289
313
  # Calls the block for each object in objs, using with_obj with the given namespace
@@ -296,18 +320,11 @@ module Forme
296
320
  end
297
321
  end
298
322
 
299
- # Return a new string that will not be html escaped by the default serializer.
323
+ # Return a new string that will not be HTML escaped by the default serializer.
300
324
  def raw(s)
301
325
  Forme.raw(s)
302
326
  end
303
327
 
304
- # Marks the string as containing already escaped output. Returns string given
305
- # by default, but subclasses for specific web frameworks can handle automatic
306
- # html escaping by overriding this.
307
- def raw_output(s)
308
- s
309
- end
310
-
311
328
  # Temporarily override the given object and namespace for the form. Any given
312
329
  # namespaces are appended to the form's current namespace.
313
330
  def with_obj(obj, namespace=nil)
@@ -367,24 +384,40 @@ module Forme
367
384
  end
368
385
  end
369
386
 
370
- # Make the given tag the currently open tag, and yield. After the
371
- # block returns, make the previously open tag the currently open
372
- # tag.
373
- def nest(tag)
374
- @nesting << tag
375
- yield self
376
- ensure
377
- @nesting.pop
387
+ # Return the HTML content added by the block.
388
+ def content_added
389
+ offset = @to_s.length
390
+ yield
391
+ @to_s[offset, @to_s.length]
378
392
  end
379
393
 
380
394
  # Return a serialized opening tag for the given tag.
381
395
  def serialize_open(tag)
382
- raw_output(serializer.serialize_open(tag)) if serializer.respond_to?(:serialize_open)
396
+ serializer.serialize_open(tag) if serializer.respond_to?(:serialize_open)
383
397
  end
384
398
 
385
399
  # Return a serialized closing tag for the given tag.
386
400
  def serialize_close(tag)
387
- raw_output(serializer.serialize_close(tag)) if serializer.respond_to?(:serialize_close)
401
+ serializer.serialize_close(tag) if serializer.respond_to?(:serialize_close)
402
+ end
403
+
404
+ # Call before hooks if defined.
405
+ def before_form_yield
406
+ # _before_post and _before hooks are only for internal use
407
+ opts[:_before_post].call(self) if opts[:_before_post] && post?
408
+ opts[:_before].call(self) if opts[:_before]
409
+
410
+ # before hook is for external use
411
+ opts[:before].call(self) if opts[:before]
412
+ end
413
+
414
+ # Call after hooks if defined.
415
+ def after_form_yield
416
+ # after hook is for external use
417
+ opts[:after].call(self) if opts[:after]
418
+
419
+ # _after hook is only for internal use
420
+ opts[:_after].call(self) if opts[:_after]
388
421
  end
389
422
  end
390
423
  end
data/lib/forme/input.rb CHANGED
@@ -32,7 +32,7 @@ module Forme
32
32
 
33
33
  # Return a string containing the serialized content of the receiver.
34
34
  def to_s
35
- form.raw_output(Forme.transform(:serializer, @opts, @form_opts, self))
35
+ Forme.transform(:serializer, @opts, @form_opts, self)
36
36
  end
37
37
 
38
38
  # Transform the receiver into a lower level +Tag+ form (or an array
data/lib/forme/rails.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'forme'
3
+ require_relative 'template'
4
4
 
5
5
  class ActiveSupport::SafeBuffer
6
6
  include Forme::Raw
@@ -18,94 +18,63 @@ module Forme
18
18
  HIDDEN_TAGS << block
19
19
  end
20
20
 
21
- # Add CSRF token tag by default for POST forms
22
- add_hidden_tag do |tag|
23
- if (form = tag.form) && (template = form.template) && template.protect_against_forgery? && (tag.attr[:method] || tag.attr['method']).to_s.upcase == 'POST'
24
- {template.request_forgery_protection_token=>template.form_authenticity_token}
21
+ class TemplateForm < ::Forme::Template::Form
22
+ %w'inputs tag subform'.each do |meth|
23
+ class_eval(<<-END, __FILE__, __LINE__+1)
24
+ def #{meth}(*)
25
+ if block_given?
26
+ @scope.send(:with_output_buffer){super}
27
+ else
28
+ @scope.raw(super)
29
+ end
30
+ end
31
+ END
25
32
  end
26
- end
27
-
28
- # Subclass used when using Forme/Rails ERB integration,
29
- # handling integration with the view template.
30
- class Form < ::Forme::Form
31
- # The Rails template that created this form.
32
- attr_reader :template
33
33
 
34
- # Set the template object when initializing.
35
- def initialize(*)
36
- super
37
- @template = @opts[:template]
34
+ %w'button input'.each do |meth|
35
+ class_eval(<<-END, __FILE__, __LINE__+1)
36
+ def #{meth}(*)
37
+ @scope.raw(super)
38
+ end
39
+ END
38
40
  end
39
41
 
40
- # Serialize and mark as already escaped the string version of
41
- # the input.
42
42
  def emit(tag)
43
- template.output_buffer << tag.to_s
43
+ @scope.output_buffer << @scope.raw(tag)
44
44
  end
45
+ end
46
+
47
+ ERB = Template::Helper.clone
48
+ module ERB
49
+ alias _forme form
50
+ remove_method :form
45
51
 
46
- # Capture the inputs into a new output buffer, and return
47
- # the buffer if not given a block
48
- def inputs(*)
52
+ def forme(*a, &block)
49
53
  if block_given?
50
- super
54
+ with_output_buffer{_forme(*a, &block)}
51
55
  else
52
- template.send(:with_output_buffer){super}
56
+ raw(_forme(*a, &block))
53
57
  end
54
58
  end
55
-
56
- # If a block is not given, emit the inputs into the current output
57
- # buffer.
58
- def _inputs(inputs=[], opts={}) # :nodoc:
59
- if block_given? && !opts[:subform]
60
- super
61
- else
62
- emit(super)
63
- end
64
- end
65
-
66
- # Return a string version of the input that is already marked as safe.
67
- def input(*)
68
- super.to_s
69
- end
70
59
 
71
- # Return a string version of the button that is already marked as safe.
72
- def button(*)
73
- super.to_s
74
- end
60
+ private
75
61
 
76
- # Use the template's raw method to mark the given string as html safe.
77
- def raw_output(s)
78
- template.raw(s)
79
- end
80
-
81
- # If a block is given, create a new output buffer and make sure all the
82
- # output of the tag goes into that buffer, and return the buffer.
83
- # Otherwise, just return a string version of the tag that is already
84
- # marked as safe.
85
- def tag(type, attr={}, children=[], &block)
86
- if block_given?
87
- template.send(:with_output_buffer){tag_(type, attr, children, &block)}
88
- else
89
- _tag(type, attr, children).to_s
62
+ def _forme_form_options(obj, attr, opts)
63
+ if protect_against_forgery?
64
+ opts[:_before_post] = lambda do |form|
65
+ form.tag(:input, :type=>:hidden, :name=>request_forgery_protection_token, :value=>form_authenticity_token)
66
+ end
90
67
  end
68
+ opts[:hidden_tags_uplevel] = 2
91
69
  end
92
-
93
- def tag_(type, attr={}, children=[]) # :nodoc:
94
- tag = _tag(type, attr, children)
95
- emit(serialize_open(tag))
96
- Array(tag.children).each{|c| emit(c)}
97
- yield self if block_given?
98
- emit(serialize_close(tag))
70
+
71
+ # The class to use for forms
72
+ def _forme_form_class
73
+ TemplateForm
99
74
  end
100
- end
101
75
 
102
- module ERB
103
- # Create a +Form+ object tied to the current template, and using the standard
104
- # Rails hidden tags.
105
- def forme(obj=nil, attr={}, opts={}, &block)
106
- h = {:template=>self, :hidden_tags=>Forme::Rails::HIDDEN_TAGS}
107
- (obj.is_a?(Hash) ? attr = attr.merge(h) : opts = opts.merge(h))
108
- Form.form(obj, attr, opts, &block)
76
+ def _forme_form_hidden_tags
77
+ Forme::Rails::HIDDEN_TAGS
109
78
  end
110
79
  end
111
80
  end
data/lib/forme/raw.rb CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Forme
4
4
  # Empty module for marking objects as "raw", where they will no longer
5
- # html escaped by the default serializer.
5
+ # HTML escaped by the default serializer.
6
6
  module Raw
7
7
  end
8
8
 
9
9
  # A String subclass that includes Raw, which will cause the default
10
- # serializer to no longer html escape the string.
10
+ # serializer to no longer HTML escape the string.
11
11
  class RawString < ::String
12
12
  include Raw
13
13
  end