roda 2.18.0 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5e850d352a98dff085d020e04fa52c55894edaf
4
- data.tar.gz: 0fb277bfbfd64699d15d6e56325464e5ba3cb5cb
3
+ metadata.gz: 3b6466dae7413eb39adad4261800c4a31373bd59
4
+ data.tar.gz: e03a612cbbcc10f0745c7c5ac90dd60ba42d2608
5
5
  SHA512:
6
- metadata.gz: a779f1e2cb695dab260f457798582014dd7bee889304de79cea0c05c43f3252b350cddb33e9d17d601f6844eaedc241290b48a2daf7ede4a89133113a2c2f353
7
- data.tar.gz: 8594f7058bd36a6cfba5cd47831b0e5267927f09e740c4c2e3b14f237a98432aaa5dde2c15b5a9d9ecb4944de9d35125adc548382383e366795ca8d4678dff1c
6
+ metadata.gz: eed87278923a1e0c6c02bf903a42e3951bc2d4f3b7107f3a7e7898287abc374185cdb1fa4456392695bd8649cdc132f31764054dd911a4bd3b4cea24652259ac
7
+ data.tar.gz: 7d79e779afcbd05769a6568920ef5854c5ae9bfdeb9cf5947177efa73a08d1350d63f758ddb747e566991dfccd7df21a1e430b35872d9bae75692b1cd29a027a
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ = 2.19.0 (2016-10-14)
2
+
3
+ * Don't add Content-Type/Content-Length headers for 1xx, 204, 205, 304 statuses (celsworth, jeremyevans) (#101, #102)
4
+
5
+ * Optimize indifferent_params plugin when using Rack 2 (jeremyevans)
6
+
7
+ * Fix assets_paths method in assets plugin when subresource integrity is used (jeremyevans, celsworth)
8
+
9
+ * Make assets plugin depend on h plugin, instead of using Rack::Utils.escape_html (jeremyevans)
10
+
11
+ * Make h plugin not escape / (celsworth, jeremyevans) (#100)
12
+
1
13
  = 2.18.0 (2016-09-13)
2
14
 
3
15
  * Add assets_preloading plugin, for creating link tags or Link header for preloading assets (celsworth, jeremyevans) (#98)
@@ -2,16 +2,16 @@
2
2
 
3
3
  * A static_routing plugin has been added, which can give a 3-4x
4
4
  increase in performance for large number of static routes, and
5
- makes routing O(1) for static routes. Static routes are routes
6
- that match full paths, with no placeholders, and are checked before
7
- using the normal routing tree.
8
-
9
- Static routes are defined via class-level static_* routing methods.
10
- There is a static_* routing method for each HTTP verb (e.g.
11
- static_get), as well as a static_route method, which will work
12
- for any HTTP verb, with the verb-specific method taking priority.
13
- By using static_route, you can get significantly faster performance
14
- while retaining some of the benefits of Roda's routing tree design
5
+ makes routing O(1) for static routes. Static routes are routes
6
+ that match full paths, with no placeholders, and are checked before
7
+ using the normal routing tree.
8
+
9
+ Static routes are defined via class-level static_* routing methods.
10
+ There is a static_* routing method for each HTTP verb (e.g.
11
+ static_get), as well as a static_route method, which will work
12
+ for any HTTP verb, with the verb-specific method taking priority.
13
+ By using static_route, you can get significantly faster performance
14
+ while retaining some of the benefits of Roda's routing tree design
15
15
  (simple shared logic with verb specific behavior). Example:
16
16
 
17
17
  plugin :static_routing
@@ -35,25 +35,25 @@
35
35
  end
36
36
 
37
37
  Because static routing routes on the full path instead of by
38
- path segment, the methods takes the full path as a string,
39
- including the leading slash.
38
+ path segment, the methods takes the full path as a string,
39
+ including the leading slash.
40
40
 
41
41
  * An assets_preloading plugin has been added, which makes it simple
42
42
  to generate HTML link tags or a Link header value to tell the
43
- browser to preload assets for performance reasons.
43
+ browser to preload assets for performance reasons.
44
44
 
45
- # In routes, using the Link header:
46
- response.headers['Link'] = preload_assets_link_header(:css)
45
+ # In routes, using the Link header:
46
+ response.headers['Link'] = preload_assets_link_header(:css)
47
47
 
48
- # In templates, using a link tag:
49
- <%= preload_assets_link_tags(:css) %>
48
+ # In templates, using a link tag:
49
+ <%= preload_assets_link_tags(:css) %>
50
50
 
51
51
  = New Features
52
52
 
53
53
  * RodaRequest#real_remaining_path has been added. This is designed
54
54
  to be overridden by plugins that modify remaining_path for internal
55
- routing purposes. RodaRequest#run now uses real_remaining_path
56
- when passing requests to other rack applications.
55
+ routing purposes. RodaRequest#run now uses real_remaining_path
56
+ when passing requests to other rack applications.
57
57
 
58
58
  * An assets_paths method has been added to the assets plugin. This
59
59
  is similar to the assets method, but it returns an array of paths
@@ -63,7 +63,7 @@
63
63
 
64
64
  * The public plugin now works correctly when used with the
65
65
  type_routing plugin, for paths ending in extensions that
66
- type_routing is configured to handle.
66
+ type_routing is configured to handle.
67
67
 
68
68
  * The head plugin now works with the not_allowed plugin if it is
69
69
  loaded after the not_allowed plugin.
@@ -0,0 +1,30 @@
1
+ = Improvements
2
+
3
+ * The indifferent_params plugin is now optimized when using Rack 2,
4
+ using Rack 2's query_parser API, and it no longer needs to do a
5
+ deep copy of the params.
6
+
7
+ * The Content-Type and Content-Length headers are no longer added
8
+ for 1xx, 204, 205, and 304 responses.
9
+
10
+ * The assets_paths method in the assets plugin now works
11
+ correctly when subresource integrity is enabled.
12
+
13
+ * The asset paths are now escaped in tags by the assets and
14
+ assets_preloading plugins. While it's unlikely a developer
15
+ would use an asset path that requires escaping, that case is
16
+ now handled correctly.
17
+
18
+ * The h plugin no longer calls Rack::Utils.escape_html, instead
19
+ implementing it's own html escaping.
20
+
21
+ * The assets plugin now uses the h plugin, instead of calling
22
+ Rack::Utils.escape_html.
23
+
24
+ = Backwards Compatibility
25
+
26
+ * The h plugin's html escaping no longer escapes "/", which is
27
+ a behavior change if you are using any recent version of rack.
28
+ The security arguments made to escape "/" could be applied to
29
+ many other characters, so if you want to escape "/", you should
30
+ probably use a separate method that escapes all \W characters.
data/lib/roda.rb CHANGED
@@ -867,6 +867,7 @@ class Roda
867
867
  # Instance methods for RodaResponse
868
868
  module ResponseMethods
869
869
  CONTENT_LENGTH = "Content-Length".freeze
870
+ CONTENT_TYPE = "Content-Type".freeze
870
871
  DEFAULT_HEADERS = {"Content-Type" => "text/html".freeze}.freeze
871
872
  LOCATION = "Location".freeze
872
873
 
@@ -932,10 +933,17 @@ class Roda
932
933
  # # []]
933
934
  def finish
934
935
  b = @body
935
- s = (@status ||= b.empty? ? 404 : default_status)
936
+ empty = b.empty?
937
+ s = (@status ||= empty ? 404 : default_status)
936
938
  set_default_headers
937
939
  h = @headers
938
- h[CONTENT_LENGTH] ||= @length.to_s
940
+
941
+ if empty && (s == 304 || s == 204 || s == 205 || (s >= 100 && s <= 199))
942
+ h.delete(CONTENT_TYPE)
943
+ else
944
+ h[CONTENT_LENGTH] ||= @length.to_s
945
+ end
946
+
939
947
  [s, h, b]
940
948
  end
941
949
 
@@ -319,15 +319,17 @@ class Roda
319
319
  CONTENT_ENCODING = 'Content-Encoding'.freeze
320
320
  GZIP = 'gzip'.freeze
321
321
  DOTGZ = '.gz'.freeze
322
+ EMPTY_ATTRS = {}.freeze
322
323
 
323
324
  # Internal exception raised when a compressor cannot be found
324
325
  CompressorNotFound = Class.new(RodaError)
325
326
 
326
- # Load the render and caching plugins plugins, since the assets plugin
327
+ # Load the render, caching, and h plugins, since the assets plugin
327
328
  # depends on them.
328
329
  def self.load_dependencies(app, _opts = nil)
329
330
  app.plugin :render
330
331
  app.plugin :caching
332
+ app.plugin :h
331
333
  end
332
334
 
333
335
  # Setup the options for the plugin. See the Assets module RDoc
@@ -605,35 +607,23 @@ class Roda
605
607
  # asset group. See the assets function documentation for details.
606
608
  def assets_paths(type)
607
609
  o = self.class.assets_opts
608
- type, *dirs = type if type.is_a?(Array)
609
- stype = type.to_s
610
- ru = Rack::Utils
610
+ if type.is_a?(Array)
611
+ ltype, *dirs = type
612
+ else
613
+ ltype = type
614
+ end
615
+ stype = ltype.to_s
611
616
 
612
617
  url_prefix = request.script_name if self.class.opts[:add_script_name]
613
618
 
614
619
  if compiled = o[:compiled]
615
- asset_host = o[:compiled_asset_host]
616
- if dirs && !dirs.empty?
617
- key = dirs.join(DOT)
618
- ckey = "#{stype}.#{key}"
619
- if hash = ukey = compiled[ckey]
620
- ukey = "#{key}.#{ukey}"
621
- end
622
- else
623
- hash = ukey = compiled[stype]
624
- end
625
-
626
- if ukey
627
- if algo = o[:sri]
628
- integrity = "\" integrity=\"#{algo}-#{ru.escape_html([[hash].pack('H*')].pack('m').tr("\n", EMPTY_STRING))}"
629
- end
630
-
631
- [ "#{asset_host}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}#{integrity}" ]
620
+ if ukey = _compiled_assets_hash(type, true)
621
+ ["#{o[:compiled_asset_host]}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}"]
632
622
  else
633
623
  []
634
624
  end
635
625
  else
636
- asset_dir = o[type]
626
+ asset_dir = o[ltype]
637
627
  if dirs && !dirs.empty?
638
628
  dirs.each{|f| asset_dir = asset_dir[f]}
639
629
  prefix = "#{dirs.join(SLASH)}/" if o[:group_subdirs]
@@ -652,19 +642,17 @@ class Roda
652
642
  # When the assets are not compiled, this will result in a separate
653
643
  # tag for each asset file. When the assets are compiled, this will
654
644
  # result in a single tag to the compiled asset file.
655
- def assets(type, attrs = nil)
656
- ru = Rack::Utils
657
- attrs = if attrs
658
- attrs.map{|k,v| "#{k}=\"#{ru.escape_html(v.to_s)}\""}.join(SPACE)
659
- else
660
- EMPTY_STRING
661
- end
645
+ def assets(type, attrs = EMPTY_ATTRS)
646
+ ltype = type.is_a?(Array) ? type[0] : type
662
647
 
663
- ltype = if type.is_a?(Array)
664
- type[0]
665
- else
666
- type
648
+ o = self.class.assets_opts
649
+ if o[:compiled] && (algo = o[:sri]) && (hash = _compiled_assets_hash(type))
650
+ attrs = Hash[attrs]
651
+ attrs[:integrity] = "#{algo}-#{h([[hash].pack('H*')].pack('m').tr("\n", EMPTY_STRING))}"
667
652
  end
653
+
654
+ attrs = attrs.map{|k,v| "#{k}=\"#{h(v)}\""}.join(SPACE)
655
+
668
656
  if ltype == :js
669
657
  tag_start = "<script type=\"text/javascript\" #{attrs} src=\""
670
658
  tag_end = JS_END
@@ -673,7 +661,7 @@ class Roda
673
661
  tag_end = CSS_END
674
662
  end
675
663
 
676
- assets_paths(type).map{|p| "#{tag_start}#{p}#{tag_end}"}.join(NEWLINE)
664
+ assets_paths(type).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join(NEWLINE)
677
665
  end
678
666
 
679
667
  # Render the asset with the given filename. When assets are compiled,
@@ -719,6 +707,24 @@ class Roda
719
707
 
720
708
  private
721
709
 
710
+ def _compiled_assets_hash(type, return_ukey=false)
711
+ compiled = self.class.assets_opts[:compiled]
712
+ type, *dirs = type if type.is_a?(Array)
713
+ stype = type.to_s
714
+
715
+ if dirs && !dirs.empty?
716
+ key = dirs.join(DOT)
717
+ ckey = "#{stype}.#{key}"
718
+ if hash = ukey = compiled[ckey]
719
+ ukey = "#{key}.#{ukey}"
720
+ end
721
+ else
722
+ hash = ukey = compiled[stype]
723
+ end
724
+
725
+ return_ukey ? ukey : hash
726
+ end
727
+
722
728
  # Return when the file was last modified. If the file depends on any
723
729
  # other files, check the modification times of all dependencies and
724
730
  # return the maximum.
@@ -61,7 +61,7 @@ class Roda
61
61
  # Return a string of <link> tags for the given asset
62
62
  # types/groups.
63
63
  def preload_assets_link_tags(*args)
64
- _preload_assets_array(args).map{|path, as| "<link href=\"#{path}\" rel=\"preload\" as=\"#{as}\">"}.join(NEWLINE)
64
+ _preload_assets_array(args).map{|path, as| "<link href=\"#{h(path)}\" rel=\"preload\" as=\"#{as}\">"}.join(NEWLINE)
65
65
  end
66
66
 
67
67
  # Return a string suitable for a Link header for the
@@ -14,10 +14,23 @@ class Roda
14
14
  # h('<foo>')
15
15
  # end
16
16
  module H
17
+ # A Hash of entities and their escaped equivalents,
18
+ # to be escaped by h().
19
+ ESCAPE_HTML = {
20
+ "&" => "&amp;".freeze,
21
+ "<" => "&lt;".freeze,
22
+ ">" => "&gt;".freeze,
23
+ "'" => "&#x27;".freeze,
24
+ '"' => "&quot;".freeze,
25
+ }.freeze
26
+
27
+ # A Regexp of HTML entities to match for escaping.
28
+ ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
29
+
17
30
  module InstanceMethods
18
31
  # HTML escape the input and return the escaped version.
19
- def h(s)
20
- ::Rack::Utils.escape_html(s.to_s)
32
+ def h(string)
33
+ string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
21
34
  end
22
35
  end
23
36
  end
@@ -4,9 +4,9 @@
4
4
  class Roda
5
5
  module RodaPlugins
6
6
  # The indifferent_params plugin adds a +params+ instance
7
- # method which returns a copy of the request params hash
8
- # that will automatically convert symbols to strings.
9
- # Example:
7
+ # method which offers indifferent access to the request
8
+ # params, allowing you to use symbols to lookup values in
9
+ # a hash where the keys are strings. Example:
10
10
  #
11
11
  # plugin :indifferent_params
12
12
  #
@@ -14,39 +14,81 @@ class Roda
14
14
  # params[:foo]
15
15
  # end
16
16
  #
17
- # The params hash is initialized lazily, so you only pay
18
- # the penalty of copying the request params if you call
19
- # the +params+ method.
17
+ # The exact behavior depends on the version of Rack in use.
18
+ # If you are using Rack 2, this plugin uses rack's API
19
+ # to set the query parser for the request to use indifferent
20
+ # access. Rack 1 doesn't support indifferent access to
21
+ # params, so if you are using Rack 1, this plugin will make
22
+ # a deep copy of the request params hash, where each level
23
+ # uses indifferent access. On Rack 1, The params hash is
24
+ # initialized lazily, so you only pay the penalty of
25
+ # copying the request params if you call the +params+ method.
20
26
  #
21
27
  # Note that there is a rack-indifferent gem that
22
- # automatically makes rack use indifferent params. Using
23
- # rack-indifferent is faster and has some other minor
24
- # advantages over the indifferent_params plugin, though
25
- # it affects rack itself instead of just the Roda app that
28
+ # monkey patches rack to always use indifferent params. If
29
+ # you are using Rack 1, it is recommended to use
30
+ # rack-indifferent instead of this plugin, as it is faster
31
+ # and has some other minor advantages, though
32
+ # it affects all rack applications instead of just the Roda app that
26
33
  # you load the plugin into.
27
34
  module IndifferentParams
28
- module InstanceMethods
29
- # A copy of the request params that will automatically
30
- # convert symbols to strings.
31
- def params
32
- @_params ||= indifferent_params(request.params)
35
+ INDIFFERENT_PROC = lambda{|h,k| h[k.to_s] if k.is_a?(Symbol)}
36
+
37
+ if Rack.release > '2'
38
+ class QueryParser < Rack::QueryParser
39
+ # Work around for invalid optimization in rack
40
+ def parse_nested_query(qs, d=nil)
41
+ return make_params.to_params_hash if qs.nil? || qs.empty?
42
+ super
43
+ end
44
+
45
+ class Params < Rack::QueryParser::Params
46
+ def initialize(limit = Rack::Utils.key_space_limit)
47
+ @limit = limit
48
+ @size = 0
49
+ @params = Hash.new(&INDIFFERENT_PROC)
50
+ end
51
+ end
52
+
33
53
  end
34
54
 
35
- private
36
-
37
- # Recursively process the request params and convert
38
- # hashes to support indifferent access, leaving
39
- # other values alone.
40
- def indifferent_params(params)
41
- case params
42
- when Hash
43
- hash = Hash.new{|h, k| h[k.to_s] if Symbol === k}
44
- params.each{|k, v| hash[k] = indifferent_params(v)}
45
- hash
46
- when Array
47
- params.map{|x| indifferent_params(x)}
48
- else
49
- params
55
+ module RequestMethods
56
+ QUERY_PARSER = Rack::Utils.default_query_parser = QueryParser.new(QueryParser::Params, 65536, 100)
57
+
58
+ def query_parser
59
+ QUERY_PARSER
60
+ end
61
+ end
62
+
63
+ module InstanceMethods
64
+ def params
65
+ @_request.params
66
+ end
67
+ end
68
+ else
69
+ module InstanceMethods
70
+ # A copy of the request params that will automatically
71
+ # convert symbols to strings.
72
+ def params
73
+ @_params ||= indifferent_params(@_request.params)
74
+ end
75
+
76
+ private
77
+
78
+ # Recursively process the request params and convert
79
+ # hashes to support indifferent access, leaving
80
+ # other values alone.
81
+ def indifferent_params(params)
82
+ case params
83
+ when Hash
84
+ hash = Hash.new(&INDIFFERENT_PROC)
85
+ params.each{|k, v| hash[k] = indifferent_params(v)}
86
+ hash
87
+ when Array
88
+ params.map{|x| indifferent_params(x)}
89
+ else
90
+ params
91
+ end
50
92
  end
51
93
  end
52
94
  end
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 18
7
+ RodaMinorVersion = 19
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -1,5 +1,21 @@
1
1
  require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
2
 
3
+ run_tests = true
4
+ begin
5
+ begin
6
+ require 'tilt/sass'
7
+ rescue LoadError
8
+ begin
9
+ for lib in %w'tilt sass'
10
+ require lib
11
+ end
12
+ rescue LoadError
13
+ warn "#{lib} not installed, skipping assets_preloading plugin test"
14
+ run_tests = false
15
+ end
16
+ end
17
+ end
18
+
3
19
  describe "assets_preloading plugin" do
4
20
  before do
5
21
  app(:bare) do
@@ -79,4 +95,4 @@ describe "assets_preloading plugin" do
79
95
  html = body('/tags-js')
80
96
  html.scan('as="script"').count.must_equal 1
81
97
  end
82
- end
98
+ end if run_tests
@@ -294,20 +294,20 @@ if run_tests
294
294
  end
295
295
 
296
296
  [[:sha256, 64, 44], [:sha384, 96, 64], [:sha512, 128, 88]].each do |algo, hex_length, base64_length|
297
- it 'should handle :sri option for subresource integrity when compiling assets' do
297
+ it "should handle :sri option for subresource integrity for #{algo} when compiling assets" do
298
298
  app.plugin :assets, :sri=>algo
299
299
  app.compile_assets
300
300
  html = body('/test')
301
301
  html.scan(/<link/).length.must_equal 1
302
- html =~ %r|integrity="#{algo}-([^"]+)" />|
303
- css_hash = $1.gsub('&#x2F;', '/')
302
+ html =~ %r|et" integrity="#{algo}-([^"]+)"|
303
+ css_hash = $1
304
304
  css_hash.length.must_equal base64_length
305
305
  html =~ %r|href="(/assets/app\.[a-f0-9]{#{hex_length}}\.css)"|
306
306
  css = body($1)
307
307
  [Digest.const_get(algo.to_s.upcase).digest(css)].pack('m').tr("\n", "").must_equal css_hash
308
308
  html.scan(/<script/).length.must_equal 1
309
- html =~ %r|integrity="#{algo}-([^"]+)"></script>|
310
- js_hash = $1.gsub('&#x2F;', '/')
309
+ html =~ %r|pt" integrity="#{algo}-([^"]+)"|
310
+ js_hash = $1
311
311
  js_hash.length.must_equal base64_length
312
312
  html =~ %r|src="(/assets/app\.head\.[a-f0-9]{#{hex_length}}\.js)"|
313
313
  js = body($1)
@@ -2,9 +2,8 @@ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
2
 
3
3
  begin
4
4
  require 'tilt/erb'
5
- require 'tilt/haml'
6
5
  rescue LoadError
7
- warn "tilt, erb, or haml not installed, skipping content_for plugin test"
6
+ warn "tilt not installed, skipping content_for plugin test"
8
7
  else
9
8
  describe "content_for plugin with erb" do
10
9
  before do
@@ -39,6 +38,37 @@ describe "content_for plugin with erb" do
39
38
  end
40
39
  end
41
40
 
41
+ describe "content_for plugin with multiple calls to the same key" do
42
+ before do
43
+ app(:bare) do
44
+ plugin :render, :views => './spec/views'
45
+ plugin :content_for
46
+
47
+ route do |r|
48
+ r.root do
49
+ view(:inline => "<% content_for :foo do %>foo<% end %><% content_for :foo do %>baz<% end %>bar", :layout => { :inline => '<%= yield %> <%= content_for(:foo) %>' })
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ it "should replace with multiple calls to the same key by default" do
56
+ body.strip.must_equal "bar baz"
57
+ end
58
+
59
+ it "should append with multiple calls to the same key if :append plugin option is used" do
60
+ app.plugin :content_for, :append => true
61
+ body.strip.must_equal "bar foobaz"
62
+ end
63
+ end
64
+ end
65
+
66
+ begin
67
+ require 'tilt/erb'
68
+ require 'tilt/haml'
69
+ rescue LoadError
70
+ warn "tilt or haml not installed, skipping content_for plugin haml tests"
71
+ else
42
72
  describe "content_for plugin with haml" do
43
73
  before do
44
74
  app(:bare) do
@@ -107,28 +137,4 @@ describe "content_for plugin when overriding :engine" do
107
137
  body('/a').strip.must_equal "bar\nfoo"
108
138
  end
109
139
  end
110
-
111
- describe "content_for plugin with multiple calls to the same key" do
112
- before do
113
- app(:bare) do
114
- plugin :render, :views => './spec/views'
115
- plugin :content_for
116
-
117
- route do |r|
118
- r.root do
119
- view(:inline => "<% content_for :foo do %>foo<% end %><% content_for :foo do %>baz<% end %>bar", :layout => { :inline => '<%= yield %> <%= content_for(:foo) %>' })
120
- end
121
- end
122
- end
123
- end
124
-
125
- it "should replace with multiple calls to the same key by default" do
126
- body.strip.must_equal "bar baz"
127
- end
128
-
129
- it "should append with multiple calls to the same key if :append plugin option is used" do
130
- app.plugin :content_for, :append => true
131
- body.strip.must_equal "bar foobaz"
132
- end
133
- end
134
140
  end
@@ -3,9 +3,9 @@ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
3
3
  describe "h plugin" do
4
4
  it "adds h method for html escaping" do
5
5
  app(:h) do |r|
6
- h("<form>") + h(:form)
6
+ h("<form>") + h(:form) + h("test&<>/'")
7
7
  end
8
8
 
9
- body.must_equal '&lt;form&gt;form'
9
+ body.must_equal '&lt;form&gt;formtest&amp;&lt;&gt;/&#x27;'
10
10
  end
11
11
  end
@@ -9,5 +9,6 @@ describe "indifferent_params plugin" do
9
9
  end
10
10
 
11
11
  body('QUERY_STRING'=>'a=2&b[][c]=3', 'rack.input'=>StringIO.new).must_equal '2/3'
12
+ body('REQUEST_METHOD'=>'POST', 'rack.input'=>StringIO.new('a=2&b[][c]=3')).must_equal '2/3'
12
13
  end
13
14
  end
@@ -65,6 +65,16 @@ describe "response #finish" do
65
65
  header('Content-Length').must_equal '1'
66
66
  end
67
67
 
68
+ it "should not set Content-Type header on a 204 response" do
69
+ app do |r|
70
+ response.status = 204
71
+ throw :halt, response.finish
72
+ end
73
+
74
+ header('Content-Type').must_equal nil
75
+ header('Content-Length').must_equal nil
76
+ end
77
+
68
78
  it "should not overwrite existing status" do
69
79
  app do |r|
70
80
  response.status = 500
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.18.0
4
+ version: 2.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-13 00:00:00.000000000 Z
11
+ date: 2016-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -184,6 +184,7 @@ extra_rdoc_files:
184
184
  - doc/release_notes/2.16.0.txt
185
185
  - doc/release_notes/2.17.0.txt
186
186
  - doc/release_notes/2.18.0.txt
187
+ - doc/release_notes/2.19.0.txt
187
188
  files:
188
189
  - CHANGELOG
189
190
  - MIT-LICENSE
@@ -205,6 +206,7 @@ files:
205
206
  - doc/release_notes/2.16.0.txt
206
207
  - doc/release_notes/2.17.0.txt
207
208
  - doc/release_notes/2.18.0.txt
209
+ - doc/release_notes/2.19.0.txt
208
210
  - doc/release_notes/2.2.0.txt
209
211
  - doc/release_notes/2.3.0.txt
210
212
  - doc/release_notes/2.4.0.txt
@@ -420,7 +422,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
420
422
  version: '0'
421
423
  requirements: []
422
424
  rubyforge_project:
423
- rubygems_version: 2.5.1
425
+ rubygems_version: 2.6.6
424
426
  signing_key:
425
427
  specification_version: 4
426
428
  summary: Routing tree web toolkit