forme 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06d7e50cfae657572071affb86ef307636c65345
4
+ data.tar.gz: 5eef25b91f833f3a57caef3903d25886cb48f00d
5
+ SHA512:
6
+ metadata.gz: a19e5579fc8f0bfe11c5136f2ecc69db057a23f76d08bc01aad4680c1ebeb9fbd27476a04d60265efe5de1d1e5ab0b16dd615f8bc0081c7b733ef9f188e31581
7
+ data.tar.gz: ba0fe6ba4e18fe182c0a93eb9b33f28e0d0f30729ec537f2a3b468d8b9db56a75d657aadb17b4c164a3128d901017d7460a7ce8ecd9bd8a923d5017bc1e4d051
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ === 0.8.0 (2013-10-30)
2
+
3
+ * form calls without block or :inputs or :button options are now handled correctly in the Sinatra integration (jeremyevans)
4
+
5
+ * CSRF token tags are now automatically added to forms in Rails and Sinatra if using rack_csrf (jeremyevans) (#5)
6
+
7
+ * Form objects now support a :hidden_tags option for automatically adding hidden tags (jeremyevans)
8
+
9
+ * Sequel many_to_one associations with existing and required values no longer have a blank option added by default (jeremyevans)
10
+
11
+ * ActiveSupport::SafeBuffer objects are now automatically treated as raw in the Rails integration (jeremyevans)
12
+
1
13
  === 0.7.0 (2012-05-02)
2
14
 
3
15
  * Support :label_position option in both of the labelers, can be set to :before or :after to override the default (jeremyevans)
data/README.rdoc CHANGED
@@ -76,7 +76,7 @@ You can even do everything in a single method call:
76
76
 
77
77
  = Basic Design
78
78
 
79
- Interally, Forme builds an abstract syntax tree of objects that
79
+ Internally, Forme builds an abstract syntax tree of objects that
80
80
  represent the form. The abstract syntax tree goes through a
81
81
  series of transformations that convert it from high level
82
82
  abstract forms to low level abstract forms and finally to
data/lib/forme.rb CHANGED
@@ -99,7 +99,7 @@ module Forme
99
99
  # +obj+ or +block+, but not both. If +obj+ is given, should be
100
100
  # either a +Class+ instance or it should respond to +call+. If a
101
101
  # +Class+ instance is given, instances of that class should respond
102
- # to +call+, and the a new instance of that class should be used
102
+ # to +call+, and a new instance of that class should be used
103
103
  # for each transformation.
104
104
  def self.register_transformer(type, sym, obj=nil, &block)
105
105
  raise Error, "Not a valid transformer type" unless TRANSFORMERS.has_key?(type)
@@ -145,6 +145,7 @@ module Forme
145
145
  # :obj :: Sets the +obj+ attribute
146
146
  # :error_handler :: Sets the +error_handler+ for the form
147
147
  # :formatter :: Sets the +formatter+ for the form
148
+ # :hidden_tags :: Sets the hidden tags to automatically add to this form.
148
149
  # :inputs_wrapper :: Sets the +inputs_wrapper+ for the form
149
150
  # :labeler :: Sets the +labeler+ for the form
150
151
  # :wrapper :: Sets the +wrapper+ for the form
@@ -175,6 +176,15 @@ module Forme
175
176
  # Must respond to +call+ or be a registered symbol.
176
177
  attr_reader :serializer
177
178
 
179
+ # The hidden tags to automatically add to the form. If set, this should be an
180
+ # array, where elements are one of the following types:
181
+ # String, Array, Forme::Tag :: Added directly as a child of the form tag.
182
+ # Hash :: Adds a hidden tag for each entry, with keys as the name of the hidden
183
+ # tag and values as the value of the hidden tag.
184
+ # Proc :: Will be called with the form tag object, and should return an instance
185
+ # of one of the handled types (or nil to not add a tag).
186
+ attr_reader :hidden_tags
187
+
178
188
  # Create a +Form+ instance and yield it to the block,
179
189
  # injecting the opening form tag before yielding and
180
190
  # the closing form tag after yielding.
@@ -232,6 +242,7 @@ module Forme
232
242
  end
233
243
  config = CONFIGURATIONS[@opts[:config]||Forme.default_config]
234
244
  TRANSFORMER_TYPES.each{|k| instance_variable_set(:"@#{k}", transformer(k, @opts.fetch(k, config[k])))}
245
+ @hidden_tags = @opts[:hidden_tags]
235
246
  @nesting = []
236
247
  end
237
248
 
@@ -285,7 +296,7 @@ module Forme
285
296
 
286
297
  # Create a form tag with the given attributes.
287
298
  def form(attr={}, &block)
288
- tag(:form, attr, &block)
299
+ tag(:form, attr, method(:hidden_form_tags), &block)
289
300
  end
290
301
 
291
302
  # Formats the +input+ using the +formatter+.
@@ -429,6 +440,35 @@ module Forme
429
440
 
430
441
  private
431
442
 
443
+ # Return array of hidden tags to use for this form,
444
+ # or nil if the form does not have hidden tags added automatically.
445
+ def hidden_form_tags(form_tag)
446
+ if hidden_tags
447
+ tags = []
448
+ hidden_tags.each do |hidden_tag|
449
+ hidden_tag = hidden_tag.call(form_tag) if hidden_tag.respond_to?(:call)
450
+ tags.concat(parse_hidden_tags(hidden_tag))
451
+ end
452
+ tags
453
+ end
454
+ end
455
+
456
+ # Handle various types of hidden tags for the form.
457
+ def parse_hidden_tags(hidden_tag)
458
+ case hidden_tag
459
+ when Array
460
+ hidden_tag
461
+ when Tag, String
462
+ [hidden_tag]
463
+ when Hash
464
+ hidden_tag.map{|k,v| _tag(:input, :type=>:hidden, :name=>k, :value=>v)}
465
+ when nil
466
+ []
467
+ else
468
+ raise Error, "unhandled hidden_tag response: #{hidden_tag.inspect}"
469
+ end
470
+ end
471
+
432
472
  # Extend +obj+ with +Serialized+ and associate it with the receiver, such
433
473
  # that calling +to_s+ on the object will use the receiver's serializer
434
474
  # to generate the resulting string.
@@ -530,15 +570,8 @@ module Forme
530
570
 
531
571
  # Set the +form+, +type+, +attr+, and +children+.
532
572
  def initialize(form, type, attr={}, children=nil)
533
- case children
534
- when Array
535
- @children = children
536
- when nil
537
- @children = nil
538
- else
539
- @children = [children]
540
- end
541
573
  @form, @type, @attr = form, type, (attr||{})
574
+ @children = parse_children(children)
542
575
  end
543
576
 
544
577
  # Adds a child to the array of receiver's children.
@@ -560,6 +593,22 @@ module Forme
560
593
  def to_s
561
594
  form.serialize(self)
562
595
  end
596
+
597
+ private
598
+
599
+ # Convert children constructor argument into the children to use for the tag.
600
+ def parse_children(children)
601
+ case children
602
+ when Array
603
+ children
604
+ when Proc, Method
605
+ parse_children(children.call(self))
606
+ when nil
607
+ nil
608
+ else
609
+ [children]
610
+ end
611
+ end
563
612
  end
564
613
 
565
614
  # Module that can extend objects associating them with a specific
data/lib/forme/rails.rb CHANGED
@@ -1,7 +1,28 @@
1
1
  require 'forme'
2
2
 
3
+ class ActiveSupport::SafeBuffer
4
+ include Forme::Raw
5
+ end
6
+
3
7
  module Forme
4
- module Rails # :nodoc:
8
+ module Rails
9
+ HIDDEN_TAGS = []
10
+
11
+ # Add a hidden tag proc that will be used for all forms created via Forme::Rails::ERB#form.
12
+ # The block is yielded the Forme::Tag object for the form tag.
13
+ # The block should return either nil if hidden tag should be added, or a Forme::Tag object (or an array of them),
14
+ # or a hash with keys specifying the name of the tags and the values specifying the values of the tags .
15
+ def self.add_hidden_tag(&block)
16
+ HIDDEN_TAGS << block
17
+ end
18
+
19
+ # Add CSRF token tag by default for POST forms
20
+ add_hidden_tag do |tag|
21
+ if (form = tag.form) && (template = form.template) && template.protect_against_forgery? && tag.attr[:method].to_s.upcase == 'POST'
22
+ {template.request_forgery_protection_token=>template.form_authenticity_token}
23
+ end
24
+ end
25
+
5
26
  # Subclass used when using Forme/Rails ERB integration,
6
27
  # handling integration with the view template.
7
28
  class Form < ::Forme::Form
@@ -66,7 +87,7 @@ module Forme
66
87
  def tag_(type, attr={}, children=[])
67
88
  tag = _tag(type, attr, children)
68
89
  emit(serializer.serialize_open(tag)) if serializer.respond_to?(:serialize_open)
69
- Array(children).each{|c| emit(c)}
90
+ Array(tag.children).each{|c| emit(c)}
70
91
  yield self if block_given?
71
92
  emit(serializer.serialize_close(tag)) if serializer.respond_to?(:serialize_close)
72
93
  end
@@ -90,7 +111,7 @@ module Forme
90
111
  # opening attributes, third if provided is
91
112
  # +Form+'s options.
92
113
  def forme(obj=nil, attr={}, opts={}, &block)
93
- h = {:template=>self}
114
+ h = {:template=>self, :hidden_tags=>Forme::Rails::HIDDEN_TAGS}
94
115
  (obj.is_a?(Hash) ? attr = attr.merge(h) : opts = opts.merge(h))
95
116
  Form.form(obj, attr, opts, &block)
96
117
  end
data/lib/forme/sinatra.rb CHANGED
@@ -1,7 +1,24 @@
1
1
  require 'forme'
2
2
 
3
3
  module Forme
4
- module Sinatra # :nodoc:
4
+ module Sinatra
5
+ HIDDEN_TAGS = []
6
+
7
+ # Add a hidden tag proc that will be used for all forms created via Forme::Sinatra::ERB#form.
8
+ # The block is yielded the Forme::Tag object for the form tag.
9
+ # The block should return either nil if hidden tag should be added, or a Forme::Tag object (or an array of them),
10
+ # or a hash with keys specifying the name of the tags and the values specifying the values of the tags .
11
+ def self.add_hidden_tag(&block)
12
+ HIDDEN_TAGS << block
13
+ end
14
+
15
+ # Add CSRF token tag by default for POST forms
16
+ add_hidden_tag do |tag|
17
+ if defined?(::Rack::Csrf) && (form = tag.form) && (env = form.opts[:env]) && tag.attr[:method].to_s.upcase == 'POST'
18
+ {::Rack::Csrf.field=>::Rack::Csrf.token(env)}
19
+ end
20
+ end
21
+
5
22
  # Subclass used when using Forme/Sinatra ERB integration.
6
23
  # Handles integrating into the view template so that
7
24
  # methods with blocks can inject strings into the output.
@@ -37,7 +54,7 @@ module Forme
37
54
  if block
38
55
  capture(block){super}
39
56
  else
40
- capture{super}
57
+ super
41
58
  end
42
59
  end
43
60
 
@@ -50,7 +67,7 @@ module Forme
50
67
  if block
51
68
  capture(block) do
52
69
  emit(serializer.serialize_open(tag)) if serializer.respond_to?(:serialize_open)
53
- children.each{|c| emit(c)}
70
+ Array(tag.children).each{|c| emit(c)}
54
71
  yield self
55
72
  emit(serializer.serialize_close(tag)) if serializer.respond_to?(:serialize_close)
56
73
  end
@@ -92,7 +109,7 @@ module Forme
92
109
  # +Form+'s options.
93
110
  def form(obj=nil, attr={}, opts={}, &block)
94
111
  if block
95
- h = {:output=>@_out_buf}
112
+ h = {:output=>@_out_buf, :hidden_tags=>Forme::Sinatra::HIDDEN_TAGS, :env=>env}
96
113
  (obj.is_a?(Hash) ? attr = attr.merge(h) : opts = opts.merge(h))
97
114
  Form.form(obj, attr, opts, &block)
98
115
  else
data/lib/forme/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Forme
2
2
  # Version constant, use <tt>Forme.version</tt> instead.
3
- VERSION = '0.7.0'.freeze
3
+ VERSION = '0.8.0'.freeze
4
4
 
5
5
  # Returns the version as a frozen string (e.g. '0.1.0')
6
6
  def self.version
@@ -309,8 +309,8 @@ module Sequel # :nodoc:
309
309
  wrapper ? wrapper.call(radios, _input(:radio, opts)) : radios
310
310
  else
311
311
  opts[:id] = form.namespaced_id(key) unless opts.has_key?(:id)
312
- opts[:add_blank] = true if !opts.has_key?(:add_blank)
313
312
  opts[:required] = true if !opts.has_key?(:required) && (sch = obj.model.db_schema[key]) && !sch[:allow_null]
313
+ opts[:add_blank] = true if !opts.has_key?(:add_blank) && !(opts[:required] && opts[:value])
314
314
  handle_label(field)
315
315
  ::Forme.attr_classes(opts[:wrapper_attr], "required") if opts[:required]
316
316
  _input(:select, opts)
data/spec/forme_spec.rb CHANGED
@@ -246,6 +246,16 @@ describe "Forme plain forms" do
246
246
  @f.tag(:textarea, {:name=>:foo}, :bar).to_s.should == '<textarea name="foo">bar</textarea>'
247
247
  end
248
248
 
249
+ specify "#tag should accept children as procs" do
250
+ @f.tag(:div, {:class=>"foo"}, lambda{|t| t.form.tag(:input, :class=>t.attr[:class])}).to_s.should == '<div class="foo"><input class="foo"/></div>'
251
+ end
252
+
253
+ specify "#tag should accept children as methods" do
254
+ o = Object.new
255
+ def o.foo(t) t.form.tag(:input, :class=>t.attr[:class]) end
256
+ @f.tag(:div, {:class=>"foo"}, o.method(:foo)).to_s.should == '<div class="foo"><input class="foo"/></div>'
257
+ end
258
+
249
259
  specify "should have an #inputs method for multiple inputs wrapped in a fieldset" do
250
260
  @f.inputs([:textarea, :text]).to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
251
261
  end
@@ -325,6 +335,10 @@ describe "Forme plain forms" do
325
335
  @f.input(:checkbox, :labeler=>:explicit, :label=>'Foo', :value=>'foo', :name=>'a', :id=>'bar', :label_position=>:before).to_s.should == '<label for="bar">Foo</label><input id="bar_hidden" name="a" type="hidden" value="0"/><input id="bar" name="a" type="checkbox" value="foo"/>'
326
336
  end
327
337
 
338
+ specify "inputs handle implicit labels or checkboxes without hidden fields with :label_position=>:before" do
339
+ @f.input(:checkbox, :label=>'Foo', :value=>'foo', :name=>'a', :id=>'bar', :label_position=>:before, :no_hidden=>true).to_s.should == '<label>Foo <input id="bar" name="a" type="checkbox" value="foo"/></label>'
340
+ end
341
+
328
342
  specify "inputs should accept a :error_handler option to use a custom error_handler" do
329
343
  @f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.should == '<textarea class="error" id="foo"></textarea>!!! bar'
330
344
  end
@@ -360,6 +374,36 @@ describe "Forme plain forms" do
360
374
  end
361
375
  end
362
376
 
377
+ describe "Forme::Form :hidden_tags option " do
378
+ before do
379
+ @f = Forme::Form.new
380
+ end
381
+
382
+ specify "should handle hash" do
383
+ Forme.form({}, :hidden_tags=>[{:a=>'b'}]).to_s.should == '<form><input name="a" type="hidden" value="b"/></form>'
384
+ end
385
+
386
+ specify "should handle array" do
387
+ Forme.form({}, :hidden_tags=>[["a ", "b"]]).to_s.should == '<form>a b</form>'
388
+ end
389
+
390
+ specify "should handle string" do
391
+ Forme.form({}, :hidden_tags=>["a "]).to_s.should == '<form>a </form>'
392
+ end
393
+
394
+ specify "should handle proc return hash" do
395
+ Forme.form({}, :hidden_tags=>[lambda{|tag| {:a=>'b'}}]).to_s.should == '<form><input name="a" type="hidden" value="b"/></form>'
396
+ end
397
+
398
+ specify "should handle proc return tag" do
399
+ Forme.form({:method=>'post'}, :hidden_tags=>[lambda{|tag| tag.form._tag(tag.attr[:method])}]).to_s.should == '<form method="post"><post></post></form>'
400
+ end
401
+
402
+ specify "should raise error for unhandled object" do
403
+ proc{Forme.form({}, :hidden_tags=>[Object.new])}.should raise_error
404
+ end
405
+ end
406
+
363
407
  describe "Forme custom" do
364
408
  specify "formatters can be specified as a proc" do
365
409
  Forme::Form.new(:formatter=>proc{|i| i.form._tag(:textarea, i.opts.map{|k,v| [v.upcase, k.to_s.downcase]})}).input(:text, :name=>'foo').to_s.should == '<textarea FOO="name"></textarea>'
@@ -6,10 +6,12 @@ require 'action_controller/railtie'
6
6
  require 'forme/rails'
7
7
 
8
8
  class FormeRails < Rails::Application
9
- config.secret_token = routes.append { match ':action' , :controller=>'forme' }.inspect
9
+ config.secret_token = routes.append { get ':action' , :controller=>'forme' }.inspect
10
10
  config.active_support.deprecation = :stderr
11
11
  config.middleware.delete(ActionDispatch::ShowExceptions)
12
- config.threadsafe!
12
+ config.middleware.delete("Rack::Lock")
13
+ config.secret_key_base = 'foo'
14
+ config.eager_load = true
13
15
  initialize!
14
16
  end
15
17
 
@@ -139,6 +141,10 @@ END
139
141
  def noblock
140
142
  render :inline => "<%= forme([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'123') %>"
141
143
  end
144
+
145
+ def safe_buffer
146
+ render :inline => "<%= forme([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'<b>foo</b>'.html_safe) %>"
147
+ end
142
148
  end
143
149
 
144
150
  describe "Forme Rails integration" do
@@ -174,7 +180,7 @@ describe "Forme Rails integration" do
174
180
  end
175
181
 
176
182
  specify "#form should correctly handle situation Sequel integration with subforms where multiple templates are used with same form object" do
177
- sin_get('/nest_seq').should == "0 <form action=\"/baz\" class=\"forme album\" method=\"post\"> 1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Foo</legend><label>Name: <input id=\"album_artist_attributes_name\" name=\"album[artist_attributes][name]\" type=\"text\" value=\"A\"/></label></fieldset> 2 n1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/> n2 <label>Name2: <input id=\"album_artist_attributes_name2\" name=\"album[artist_attributes][name2]\" type=\"text\" value=\"A2\"/></label> n3 n4 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Bar</legend><label>Name3: <input id=\"album_artist_attributes_name3\" name=\"album[artist_attributes][name3]\" type=\"text\" value=\"A3\"/></label></fieldset> n5 3 </form>4"
183
+ sin_get('/nest_seq').sub(%r{<input name=\"authenticity_token\" type=\"hidden\" value=\"([^\"]+)\"/>}, "<input name=\"authenticity_token\" type=\"hidden\" value=\"csrf\"/>").should == "0 <form action=\"/baz\" class=\"forme album\" method=\"post\"><input name=\"authenticity_token\" type=\"hidden\" value=\"csrf\"/> 1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Foo</legend><label>Name: <input id=\"album_artist_attributes_name\" name=\"album[artist_attributes][name]\" type=\"text\" value=\"A\"/></label></fieldset> 2 n1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/> n2 <label>Name2: <input id=\"album_artist_attributes_name2\" name=\"album[artist_attributes][name2]\" type=\"text\" value=\"A2\"/></label> n3 n4 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Bar</legend><label>Name3: <input id=\"album_artist_attributes_name3\" name=\"album[artist_attributes][name3]\" type=\"text\" value=\"A3\"/></label></fieldset> n5 3 </form>4"
178
184
  end
179
185
 
180
186
  specify "#form should accept two hashes instead of requiring obj as first argument" do
@@ -192,4 +198,8 @@ describe "Forme Rails integration" do
192
198
  specify "#form should work without a block" do
193
199
  sin_get('/noblock').should == '<form action="/baz"><fieldset class="inputs"><legend>123</legend><input id="first" name="first" type="text" value="foo"/></fieldset><input type="submit" value="xyz"/></form>'
194
200
  end
201
+
202
+ specify "#form should handle Rails SafeBuffers" do
203
+ sin_get('/safe_buffer').should == '<form action="/baz"><fieldset class="inputs"><legend><b>foo</b></legend><input id="first" name="first" type="text" value="foo"/></fieldset><input type="submit" value="xyz"/></form>'
204
+ end
195
205
  end
@@ -17,6 +17,13 @@ describe "Forme Sequel::Model forms" do
17
17
  @b.form(:class=>:foo, :method=>:get).to_s.should == '<form class="foo forme album" method="get"></form>'
18
18
  end
19
19
 
20
+ specify "should handle invalid methods" do
21
+ def @ab.db_schema
22
+ super.merge(:foo=>{:type=>:bar})
23
+ end
24
+ @b.input(:foo, :value=>'baz').to_s.should == '<label>Foo: <input id="album_foo" name="album[foo]" type="text" value="baz"/></label>'
25
+ end
26
+
20
27
  specify "should allow an array of classes" do
21
28
  @b.form(:class=>[:foo, :bar]).to_s.should == '<form class="foo bar forme album" method="post"></form>'
22
29
  @b.form(:class=>[:foo, [:bar, :baz]]).to_s.should == '<form class="foo bar baz forme album" method="post"></form>'
@@ -122,12 +129,16 @@ describe "Forme Sequel::Model forms" do
122
129
  @c.input(:artist).to_s.should == '<label>Artist: <select id="album_artist_id" name="album[artist_id]"><option value=""></option><option value="1">a</option><option selected="selected" value="2">d</option></select></label>'
123
130
  end
124
131
 
132
+ specify "should not add a blank option by default if there is a default value and it is required" do
133
+ @b.input(:artist, :required=>true).to_s.should == '<label>Artist<abbr title="required">*</abbr>: <select id="album_artist_id" name="album[artist_id]" required="required"><option selected="selected" value="1">a</option><option value="2">d</option></select></label>'
134
+ end
135
+
125
136
  specify "should allow overriding default input type using a :type option" do
126
137
  @b.input(:artist, :type=>:string, :value=>nil).to_s.should == '<label>Artist: <input id="album_artist" name="album[artist]" type="text"/></label>'
127
138
  end
128
139
 
129
140
  specify "should use a required wrapper tag for many_to_one required associations" do
130
- @b.input(:artist, :required=>true, :wrapper=>:li).to_s.should == '<li class="many_to_one required"><label>Artist<abbr title="required">*</abbr>: <select id="album_artist_id" name="album[artist_id]" required="required"><option value=""></option><option selected="selected" value="1">a</option><option value="2">d</option></select></label></li>'
141
+ @b.input(:artist, :required=>true, :wrapper=>:li).to_s.should == '<li class="many_to_one required"><label>Artist<abbr title="required">*</abbr>: <select id="album_artist_id" name="album[artist_id]" required="required"><option selected="selected" value="1">a</option><option value="2">d</option></select></label></li>'
131
142
  end
132
143
 
133
144
  specify "should use a set of radio buttons for many_to_one associations with :as=>:radio option" do
@@ -5,11 +5,14 @@ require 'rubygems'
5
5
  require 'sinatra/base'
6
6
  require 'forme/sinatra'
7
7
  require(ENV['ERUBIS'] ? 'erubis' : 'erb')
8
+ require 'rack/csrf'
8
9
 
9
10
  class FormeSinatraTest < Sinatra::Base
10
11
  helpers(Forme::Sinatra::ERB)
11
12
  disable :show_exceptions
12
13
  enable :raise_errors
14
+ enable :sessions
15
+ use Rack::Csrf
13
16
 
14
17
  get '/' do
15
18
  erb <<END
@@ -24,21 +27,17 @@ END
24
27
 
25
28
  get '/inputs_block' do
26
29
  erb <<END
27
- <% form([:foo, :bar], :action=>'/baz') do |f| %>
28
- <% f.inputs(:legend=>'FBB') do %>
30
+ <% form([:foo, :bar], :action=>'/baz') do |f| %><% f.inputs(:legend=>'FBB') do %>
29
31
  <%= f.input(:last) %>
30
- <% end %>
31
- <% end %>
32
+ <% end %><% end %>
32
33
  END
33
34
  end
34
35
 
35
36
  get '/inputs_block_wrapper' do
36
37
  erb <<END
37
- <% form([:foo, :bar], {:action=>'/baz'}, :inputs_wrapper=>:fieldset_ol) do |f| %>
38
- <% f.inputs(:legend=>'FBB') do %>
38
+ <% form([:foo, :bar], {:action=>'/baz'}, :inputs_wrapper=>:fieldset_ol) do |f| %><% f.inputs(:legend=>'FBB') do %>
39
39
  <%= f.input(:last) %>
40
- <% end %>
41
- <% end %>
40
+ <% end %><% end %>
42
41
  END
43
42
  end
44
43
 
@@ -134,11 +133,17 @@ END
134
133
  get '/noblock' do
135
134
  erb "<%= form([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'123') %>"
136
135
  end
136
+
137
+ get '/noblock_empty' do
138
+ erb "<%= form(:action=>'/baz') %>"
139
+ end
137
140
  end
138
141
 
139
142
  describe "Forme Sinatra ERB integration" do
140
143
  def sin_get(path)
141
- FormeSinatraTest.new.call(@rack.merge('PATH_INFO'=>path))[2].join.gsub(/\s+/, ' ').strip
144
+ s = ''
145
+ FormeSinatraTest.new.call(@rack.merge('PATH_INFO'=>path))[2].each{|str| s << str}
146
+ s.gsub(/\s+/, ' ').strip
142
147
  end
143
148
  before do
144
149
  o = Object.new
@@ -167,7 +172,7 @@ describe "Forme Sinatra ERB integration" do
167
172
  end
168
173
 
169
174
  specify "#form should correctly handle situation Sequel integration with subforms where multiple templates are used with same form object" do
170
- sin_get('/nest_seq').should == "0 <form action=\"/baz\" class=\"forme album\" method=\"post\"> 1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Foo</legend><label>Name: <input id=\"album_artist_attributes_name\" name=\"album[artist_attributes][name]\" type=\"text\" value=\"A\"/></label></fieldset> 2 n1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/> n2 <label>Name2: <input id=\"album_artist_attributes_name2\" name=\"album[artist_attributes][name2]\" type=\"text\" value=\"A2\"/></label> n3 n4 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Bar</legend><label>Name3: <input id=\"album_artist_attributes_name3\" name=\"album[artist_attributes][name3]\" type=\"text\" value=\"A3\"/></label></fieldset> n5 3 </form>4"
175
+ sin_get('/nest_seq').sub(%r{<input name=\"_csrf\" type=\"hidden\" value=\"([^\"]+)\"/>}, "<input name=\"_csrf\" type=\"hidden\" value=\"csrf\"/>").should == "0 <form action=\"/baz\" class=\"forme album\" method=\"post\"><input name=\"_csrf\" type=\"hidden\" value=\"csrf\"/> 1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Foo</legend><label>Name: <input id=\"album_artist_attributes_name\" name=\"album[artist_attributes][name]\" type=\"text\" value=\"A\"/></label></fieldset> 2 n1 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/> n2 <label>Name2: <input id=\"album_artist_attributes_name2\" name=\"album[artist_attributes][name2]\" type=\"text\" value=\"A2\"/></label> n3 n4 <input id=\"album_artist_attributes_id\" name=\"album[artist_attributes][id]\" type=\"hidden\" value=\"2\"/><fieldset class=\"inputs\"><legend>Bar</legend><label>Name3: <input id=\"album_artist_attributes_name3\" name=\"album[artist_attributes][name3]\" type=\"text\" value=\"A3\"/></label></fieldset> n5 3 </form>4"
171
176
  end
172
177
 
173
178
  specify "#form should accept two hashes instead of requiring obj as first argument" do
@@ -185,4 +190,8 @@ describe "Forme Sinatra ERB integration" do
185
190
  specify "#form should work without a block" do
186
191
  sin_get('/noblock').should == '<form action="/baz"><fieldset class="inputs"><legend>123</legend><input id="first" name="first" type="text" value="foo"/></fieldset><input type="submit" value="xyz"/></form>'
187
192
  end
193
+
194
+ specify "#form with an empty form should work" do
195
+ sin_get('/noblock_empty').should == '<form action="/baz"></form>'
196
+ end
188
197
  end
metadata CHANGED
@@ -1,28 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
5
- prerelease:
4
+ version: 0.8.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jeremy Evans
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-05-02 00:00:00.000000000 Z
11
+ date: 2013-10-30 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: ! 'Forme is a forms library with the following goals:
15
-
13
+ description: |
14
+ Forme is a forms library with the following goals:
16
15
 
17
16
  1) Have no external dependencies
18
-
19
17
  2) Have a simple API
20
-
21
18
  3) Support forms both with and without related objects
22
-
23
19
  4) Allow compiling down to different types of output
24
-
25
- '
26
20
  email: code@jeremyevans.net
27
21
  executables: []
28
22
  extensions: []
@@ -48,33 +42,32 @@ files:
48
42
  - lib/sequel/plugins/forme.rb
49
43
  homepage: http://gihub.com/jeremyevans/forme
50
44
  licenses: []
45
+ metadata: {}
51
46
  post_install_message:
52
47
  rdoc_options:
53
48
  - --quiet
54
49
  - --line-numbers
55
50
  - --inline-source
56
51
  - --title
57
- - ! 'Forme: HTML forms library'
52
+ - 'Forme: HTML forms library'
58
53
  - --main
59
54
  - README.rdoc
60
55
  require_paths:
61
56
  - lib
62
57
  required_ruby_version: !ruby/object:Gem::Requirement
63
- none: false
64
58
  requirements:
65
- - - ! '>='
59
+ - - '>='
66
60
  - !ruby/object:Gem::Version
67
61
  version: '0'
68
62
  required_rubygems_version: !ruby/object:Gem::Requirement
69
- none: false
70
63
  requirements:
71
- - - ! '>='
64
+ - - '>='
72
65
  - !ruby/object:Gem::Version
73
66
  version: '0'
74
67
  requirements: []
75
68
  rubyforge_project:
76
- rubygems_version: 1.8.11
69
+ rubygems_version: 2.0.3
77
70
  signing_key:
78
- specification_version: 3
71
+ specification_version: 4
79
72
  summary: HTML forms library
80
73
  test_files: []