roda 2.18.0 → 2.19.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.
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