roda 2.27.0 → 2.28.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: e2d73336ee24aee0fa3afe57096ef4d2f47c14f5
4
- data.tar.gz: 9c0dbc60da0f5d73a24ad48a807f413c0f84e300
3
+ metadata.gz: ca75e717d558d6b3c163517c6e8e3678a33b68e0
4
+ data.tar.gz: a08ab3922fc609e9413f6151444bc9a92186777e
5
5
  SHA512:
6
- metadata.gz: d0e19332b1d9c17b18d4d9e3514a90389e65373508874b41521c3fb4fcfa4f0dc97da1fdfbb09273c9b84ac08addf4c286d85c4467538a1481e459a5307c6ae2
7
- data.tar.gz: 4d7de5e3b2331e0e9a6c1983a2b79ba2a5f6d5e105b6d7fd8f5acf5d52128f34762cabf68627e5f2866415667fbfcd41b23f7fc315a0ea14ab52cb88b19217ba
6
+ metadata.gz: 29684100b4d5dd39d11e034a9158442b9e7379c11fe8c0169033fab127394855e79016aa0101f7abdb79cc544dbfcedc98c0fa3fd6fad7559678ab43f69b9dbe
7
+ data.tar.gz: 77d6d7bb57535d375a46103b4ba5f803231172ecf6b98cdc3ebba85600be1e02dfe06dc907758ffbdbe6123911f436e15fbf87fe3c2a4f2d4a0742a36af94471
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ = 2.28.0 (2017-07-14)
2
+
3
+ * Deprecate unneeded internal constants (jeremyevans)
4
+
5
+ * Optimize for ruby 2.3+ using frozen string literals instead of constants (jeremyevans)
6
+
7
+ * Move 303 default redirect status from sinatra_helpers to status_303 plugin, so it can be loaded separately (plujon) (#122)
8
+
1
9
  = 2.27.0 (2017-06-14)
2
10
 
3
11
  * Add class_matchers plugin for matching other classes (in addition to String/Integer), with user specified regexps and type conversion (jeremyevans)
@@ -0,0 +1,17 @@
1
+ = New Features
2
+
3
+ * A status_303 plugin has been added, which changes the default
4
+ redirect status from 302 to 303 if the HTTP version is 1.1 and
5
+ the request is not a GET request.
6
+
7
+ = Other Improvements
8
+
9
+ * Roda is now optimized for ruby 2.3+ using frozen string literals
10
+ instead of constant references. This improves performance on ruby
11
+ 2.3+, and decreases performance on ruby <2.3.
12
+
13
+ = Backwards Compatibility
14
+
15
+ * Many now unused internal constants are now deprecated, and
16
+ attempting to access them will result in deprecation warnings
17
+ on ruby 2.3+.
@@ -89,6 +89,14 @@ class Roda
89
89
  @plugins[name] = mod
90
90
  end
91
91
 
92
+ # Deprecate the constant with the given name in the given module,
93
+ # if the ruby version supports it.
94
+ def self.deprecate_constant(mod, name)
95
+ if RUBY_VERSION >= '2.3'
96
+ mod.deprecate_constant(name)
97
+ end
98
+ end
99
+
92
100
  # The base plugin for Roda, implementing all default functionality.
93
101
  # Methods are put into a plugin so future plugins can easily override
94
102
  # them and call super to get the default behavior.
@@ -340,19 +348,29 @@ class Roda
340
348
  # for the request.
341
349
  module RequestMethods
342
350
  PATH_INFO = "PATH_INFO".freeze
351
+ RodaPlugins.deprecate_constant(self, :PATH_INFO)
343
352
  SCRIPT_NAME = "SCRIPT_NAME".freeze
353
+ RodaPlugins.deprecate_constant(self, :SCRIPT_NAME)
344
354
  REQUEST_METHOD = "REQUEST_METHOD".freeze
355
+ RodaPlugins.deprecate_constant(self, :REQUEST_METHOD)
345
356
  EMPTY_STRING = "".freeze
357
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
346
358
  SLASH = "/".freeze
359
+ RodaPlugins.deprecate_constant(self, :SLASH)
347
360
  COLON = ":".freeze
361
+ RodaPlugins.deprecate_constant(self, :COLON)
348
362
  SEGMENT = "([^\\/]+)".freeze
363
+ RodaPlugins.deprecate_constant(self, :SEGMENT)
349
364
  TERM_INSPECT = "TERM".freeze
365
+ RodaPlugins.deprecate_constant(self, :TERM_INSPECT)
350
366
  GET_REQUEST_METHOD = 'GET'.freeze
367
+ RodaPlugins.deprecate_constant(self, :GET_REQUEST_METHOD)
351
368
  SESSION_KEY = 'rack.session'.freeze
369
+ RodaPlugins.deprecate_constant(self, :SESSION_KEY)
352
370
 
353
371
  TERM = Object.new
354
372
  def TERM.inspect
355
- TERM_INSPECT
373
+ "TERM"
356
374
  end
357
375
  TERM.freeze
358
376
 
@@ -408,7 +426,7 @@ class Roda
408
426
  # r.inspect
409
427
  # # => '#<Roda::RodaRequest GET /foo/bar>'
410
428
  def inspect
411
- "#<#{self.class.inspect} #{@env[REQUEST_METHOD]} #{path}>"
429
+ "#<#{self.class.inspect} #{@env["REQUEST_METHOD"]} #{path}>"
412
430
  end
413
431
 
414
432
  # Does a terminal match on the current path, matching only if the arguments
@@ -469,7 +487,7 @@ class Roda
469
487
  # Similar to the default Rack::Request get? method, but can be
470
488
  # overridden without changing rack's behavior.
471
489
  def is_get?
472
- @env[REQUEST_METHOD] == GET_REQUEST_METHOD
490
+ @env["REQUEST_METHOD"] == 'GET'
473
491
  end
474
492
 
475
493
  # Does a match on the path, matching only if the arguments
@@ -510,7 +528,7 @@ class Roda
510
528
  # The already matched part of the path, including the original SCRIPT_NAME.
511
529
  def matched_path
512
530
  e = @env
513
- e[SCRIPT_NAME] + e[PATH_INFO].chomp(@remaining_path)
531
+ e["SCRIPT_NAME"] + e["PATH_INFO"].chomp(@remaining_path)
514
532
  end
515
533
 
516
534
  # This an an optimized version of Rack::Request#path.
@@ -521,7 +539,7 @@ class Roda
521
539
  # # => '/foo/bar'
522
540
  def path
523
541
  e = @env
524
- "#{e[SCRIPT_NAME]}#{e[PATH_INFO]}"
542
+ "#{e["SCRIPT_NAME"]}#{e["PATH_INFO"]}"
525
543
  end
526
544
 
527
545
  # The current path to match requests against.
@@ -631,7 +649,7 @@ class Roda
631
649
  # Use <tt>r.get true</tt> to handle +GET+ requests where the current
632
650
  # path is empty.
633
651
  def root(&block)
634
- if remaining_path == SLASH && is_get?
652
+ if remaining_path == "/" && is_get?
635
653
  always(&block)
636
654
  end
637
655
  end
@@ -650,8 +668,8 @@ class Roda
650
668
  def run(app)
651
669
  e = @env
652
670
  path = real_remaining_path
653
- sn = SCRIPT_NAME
654
- pi = PATH_INFO
671
+ sn = "SCRIPT_NAME"
672
+ pi = "PATH_INFO"
655
673
  script_name = e[sn]
656
674
  path_info = e[pi]
657
675
  begin
@@ -667,7 +685,7 @@ class Roda
667
685
  # The session for the current request. Raises a RodaError if
668
686
  # a session handler has not been loaded.
669
687
  def session
670
- @env[SESSION_KEY] || raise(RodaError, "You're missing a session handler. You can get started by adding use Rack::Session::Cookie")
688
+ @env['rack.session'] || raise(RodaError, "You're missing a session handler. You can get started by adding use Rack::Session::Cookie")
671
689
  end
672
690
 
673
691
  private
@@ -721,21 +739,21 @@ class Roda
721
739
  # string so that regexp metacharacters are not matched, and recognizes
722
740
  # colon tokens for placeholders.
723
741
  def _match_string(str)
724
- if str.index(COLON) && placeholder_string_matcher?
742
+ if str.index(":") && placeholder_string_matcher?
725
743
  consume(self.class.cached_matcher(str){Regexp.escape(str).gsub(/:(\w+)/){|m| _match_symbol_regexp($1)}})
726
744
  else
727
745
  rp = @remaining_path
728
746
  if rp.start_with?("/#{str}")
729
747
  last = str.length + 1
730
748
  case rp[last]
731
- when SLASH
749
+ when "/"
732
750
  @remaining_path = rp[last, rp.length]
733
751
  when nil
734
- @remaining_path = EMPTY_STRING
752
+ @remaining_path = ""
735
753
  when Integer
736
754
  # :nocov:
737
755
  # Ruby 1.8 support
738
- if rp[last].chr == SLASH
756
+ if rp[last].chr == "/"
739
757
  @remaining_path = rp[last, rp.length]
740
758
  end
741
759
  # :nocov:
@@ -747,7 +765,7 @@ class Roda
747
765
  # Match the given symbol if any segment matches.
748
766
  def _match_symbol(sym=nil)
749
767
  rp = @remaining_path
750
- if rp[0, 1] == SLASH
768
+ if rp[0, 1] == "/"
751
769
  if last = rp.index('/', 1)
752
770
  if last > 1
753
771
  @captures << rp[1, last-1]
@@ -755,7 +773,7 @@ class Roda
755
773
  end
756
774
  elsif rp.length > 1
757
775
  @captures << rp[1,rp.length]
758
- @remaining_path = EMPTY_STRING
776
+ @remaining_path = ""
759
777
  end
760
778
  end
761
779
  end
@@ -766,12 +784,12 @@ class Roda
766
784
  # The regular expression to use for matching symbols. By default, any non-empty
767
785
  # segment matches.
768
786
  def _match_symbol_regexp(s)
769
- SEGMENT
787
+ "([^\\/]+)"
770
788
  end
771
789
 
772
790
  # The base remaining path to use.
773
791
  def _remaining_path(env)
774
- env[PATH_INFO]
792
+ env["PATH_INFO"]
775
793
  end
776
794
 
777
795
  # Backbone of the verb method support, using a terminal match if
@@ -841,7 +859,7 @@ class Roda
841
859
 
842
860
  # Whether the current path is considered empty.
843
861
  def empty_path?
844
- remaining_path == EMPTY_STRING
862
+ remaining_path == ""
845
863
  end
846
864
 
847
865
  # If all of the arguments match, yields to the match block and
@@ -900,7 +918,7 @@ class Roda
900
918
  if type.is_a?(Array)
901
919
  type.any?{|t| match_method(t)}
902
920
  else
903
- type.to_s.upcase == @env[REQUEST_METHOD]
921
+ type.to_s.upcase == @env["REQUEST_METHOD"]
904
922
  end
905
923
  end
906
924
 
@@ -934,10 +952,14 @@ class Roda
934
952
 
935
953
  # Instance methods for RodaResponse
936
954
  module ResponseMethods
955
+ DEFAULT_HEADERS = {"Content-Type" => "text/html".freeze}.freeze
956
+
937
957
  CONTENT_LENGTH = "Content-Length".freeze
958
+ RodaPlugins.deprecate_constant(self, :CONTENT_LENGTH)
938
959
  CONTENT_TYPE = "Content-Type".freeze
939
- DEFAULT_HEADERS = {"Content-Type" => "text/html".freeze}.freeze
960
+ RodaPlugins.deprecate_constant(self, :CONTENT_TYPE)
940
961
  LOCATION = "Location".freeze
962
+ RodaPlugins.deprecate_constant(self, :LOCATION)
941
963
 
942
964
  # The body for the current response.
943
965
  attr_reader :body
@@ -1007,9 +1029,9 @@ class Roda
1007
1029
  h = @headers
1008
1030
 
1009
1031
  if empty && (s == 304 || s == 204 || s == 205 || (s >= 100 && s <= 199))
1010
- h.delete(CONTENT_TYPE)
1032
+ h.delete("Content-Type")
1011
1033
  else
1012
- h[CONTENT_LENGTH] ||= @length.to_s
1034
+ h["Content-Length"] ||= @length.to_s
1013
1035
  end
1014
1036
 
1015
1037
  [s, h, b]
@@ -1042,7 +1064,7 @@ class Roda
1042
1064
  # response.redirect('foo', 301)
1043
1065
  # response.redirect('bar')
1044
1066
  def redirect(path, status = 302)
1045
- @headers[LOCATION] = path
1067
+ @headers["Location"] = path
1046
1068
  @status = status
1047
1069
  nil
1048
1070
  end
@@ -306,20 +306,34 @@ class Roda
306
306
  :compiled_css_dir => nil,
307
307
  :compiled_js_dir => nil,
308
308
  }.freeze
309
+ EMPTY_ATTRS = {}.freeze
310
+
309
311
  JS_END = "\"></script>".freeze
312
+ RodaPlugins.deprecate_constant(self, :JS_END)
310
313
  CSS_END = "\" />".freeze
314
+ RodaPlugins.deprecate_constant(self, :CSS_END)
311
315
  SPACE = ' '.freeze
316
+ RodaPlugins.deprecate_constant(self, :SPACE)
312
317
  DOT = '.'.freeze
318
+ RodaPlugins.deprecate_constant(self, :DOT)
313
319
  SLASH = '/'.freeze
320
+ RodaPlugins.deprecate_constant(self, :SLASH)
314
321
  NEWLINE = "\n".freeze
322
+ RodaPlugins.deprecate_constant(self, :NEWLINE)
315
323
  EMPTY_STRING = ''.freeze
324
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
316
325
  JS_SUFFIX = '.js'.freeze
326
+ RodaPlugins.deprecate_constant(self, :JS_SUFFIX)
317
327
  CSS_SUFFIX = '.css'.freeze
328
+ RodaPlugins.deprecate_constant(self, :CSS_SUFFIX)
318
329
  HTTP_ACCEPT_ENCODING = 'HTTP_ACCEPT_ENCODING'.freeze
330
+ RodaPlugins.deprecate_constant(self, :HTTP_ACCEPT_ENCODING)
319
331
  CONTENT_ENCODING = 'Content-Encoding'.freeze
332
+ RodaPlugins.deprecate_constant(self, :CONTENT_ENCODING)
320
333
  GZIP = 'gzip'.freeze
334
+ RodaPlugins.deprecate_constant(self, :GZIP)
321
335
  DOTGZ = '.gz'.freeze
322
- EMPTY_ATTRS = {}.freeze
336
+ RodaPlugins.deprecate_constant(self, :DOTGZ)
323
337
 
324
338
  # Internal exception raised when a compressor cannot be found
325
339
  CompressorNotFound = Class.new(RodaError)
@@ -419,8 +433,8 @@ class Roda
419
433
  opts[:css_prefix] = sj.call(:prefix, :css_route)
420
434
  opts[:compiled_js_prefix] = j.call(:prefix, :compiled_js_route, :compiled_name)
421
435
  opts[:compiled_css_prefix] = j.call(:prefix, :compiled_css_route, :compiled_name)
422
- opts[:js_suffix] = opts[:add_suffix] ? JS_SUFFIX : EMPTY_STRING
423
- opts[:css_suffix] = opts[:add_suffix] ? CSS_SUFFIX : EMPTY_STRING
436
+ opts[:js_suffix] = (opts[:add_suffix] ? '.js' : '').freeze
437
+ opts[:css_suffix] = (opts[:add_suffix] ? '.css' : '').freeze
424
438
 
425
439
  opts.freeze
426
440
  end
@@ -626,7 +640,7 @@ class Roda
626
640
  asset_dir = o[ltype]
627
641
  if dirs && !dirs.empty?
628
642
  dirs.each{|f| asset_dir = asset_dir[f]}
629
- prefix = "#{dirs.join(SLASH)}/" if o[:group_subdirs]
643
+ prefix = "#{dirs.join('/')}/" if o[:group_subdirs]
630
644
  end
631
645
  Array(asset_dir).map{|f| "#{url_prefix}/#{o[:"#{stype}_prefix"]}#{prefix}#{f}#{o[:"#{stype}_suffix"]}"}
632
646
  end
@@ -648,20 +662,20 @@ class Roda
648
662
  o = self.class.assets_opts
649
663
  if o[:compiled] && (algo = o[:sri]) && (hash = _compiled_assets_hash(type))
650
664
  attrs = Hash[attrs]
651
- attrs[:integrity] = "#{algo}-#{h([[hash].pack('H*')].pack('m').tr("\n", EMPTY_STRING))}"
665
+ attrs[:integrity] = "#{algo}-#{h([[hash].pack('H*')].pack('m').tr("\n", ''))}"
652
666
  end
653
667
 
654
- attrs = attrs.map{|k,v| "#{k}=\"#{h(v)}\""}.join(SPACE)
668
+ attrs = attrs.map{|k,v| "#{k}=\"#{h(v)}\""}.join(' ')
655
669
 
656
670
  if ltype == :js
657
671
  tag_start = "<script type=\"text/javascript\" #{attrs} src=\""
658
- tag_end = JS_END
672
+ tag_end = "\"></script>"
659
673
  else
660
674
  tag_start = "<link rel=\"stylesheet\" #{attrs} href=\""
661
- tag_end = CSS_END
675
+ tag_end = "\" />"
662
676
  end
663
677
 
664
- assets_paths(type).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join(NEWLINE)
678
+ assets_paths(type).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n")
665
679
  end
666
680
 
667
681
  # Render the asset with the given filename. When assets are compiled,
@@ -676,9 +690,9 @@ class Roda
676
690
  if o[:compiled]
677
691
  file = "#{o[:"compiled_#{type}_path"]}#{file}"
678
692
 
679
- if o[:gzip] && env[HTTP_ACCEPT_ENCODING] =~ /\bgzip\b/
680
- @_response[CONTENT_ENCODING] = GZIP
681
- file += DOTGZ
693
+ if o[:gzip] && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
694
+ @_response['Content-Encoding'] = 'gzip'
695
+ file += '.gz'
682
696
  end
683
697
 
684
698
  check_asset_request(file, type, ::File.stat(file).mtime)
@@ -713,7 +727,7 @@ class Roda
713
727
  stype = type.to_s
714
728
 
715
729
  if dirs && !dirs.empty?
716
- key = dirs.join(DOT)
730
+ key = dirs.join('.')
717
731
  ckey = "#{stype}.#{key}"
718
732
  if hash = ukey = compiled[ckey]
719
733
  ukey = "#{key}.#{ukey}"
@@ -49,8 +49,11 @@ class Roda
49
49
  :css => 'style'.freeze,
50
50
  :js => 'script'.freeze,
51
51
  }.freeze
52
+
52
53
  COMMA = ",".freeze
54
+ RodaPlugins.deprecate_constant(self, :COMMA)
53
55
  NEWLINE= "\n".freeze
56
+ RodaPlugins.deprecate_constant(self, :NEWLINE)
54
57
 
55
58
  # Depend on the assets plugin, as we'll be calling some functions in it.
56
59
  def self.load_dependencies(app)
@@ -61,13 +64,13 @@ class Roda
61
64
  # Return a string of <link> tags for the given asset
62
65
  # types/groups.
63
66
  def preload_assets_link_tags(*args)
64
- _preload_assets_array(args).map{|path, as| "<link href=\"#{h(path)}\" rel=\"preload\" as=\"#{as}\">"}.join(NEWLINE)
67
+ _preload_assets_array(args).map{|path, as| "<link href=\"#{h(path)}\" rel=\"preload\" as=\"#{as}\">"}.join("\n")
65
68
  end
66
69
 
67
70
  # Return a string suitable for a Link header for the
68
71
  # given asset types/groups.
69
72
  def preload_assets_link_header(*args)
70
- _preload_assets_array(args).map{|path, as| "<#{path}>;rel=preload;as=#{as}"}.join(COMMA)
73
+ _preload_assets_array(args).map{|path, as| "<#{path}>;rel=preload;as=#{as}"}.join(",")
71
74
  end
72
75
 
73
76
  private
@@ -27,7 +27,9 @@ class Roda
27
27
  module BacktrackingArray
28
28
  module RequestMethods
29
29
  PATH_INFO = "PATH_INFO".freeze
30
+ RodaPlugins.deprecate_constant(self, :PATH_INFO)
30
31
  SCRIPT_NAME = "SCRIPT_NAME".freeze
32
+ RodaPlugins.deprecate_constant(self, :SCRIPT_NAME)
31
33
 
32
34
  private
33
35
 
@@ -73,12 +73,19 @@ class Roda
73
73
 
74
74
  module RequestMethods
75
75
  LAST_MODIFIED = 'Last-Modified'.freeze
76
+ RodaPlugins.deprecate_constant(self, :LAST_MODIFIED)
76
77
  HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
78
+ RodaPlugins.deprecate_constant(self, :HTTP_IF_NONE_MATCH)
77
79
  HTTP_IF_MATCH = 'HTTP_IF_MATCH'.freeze
80
+ RodaPlugins.deprecate_constant(self, :HTTP_IF_MATCH)
78
81
  HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
82
+ RodaPlugins.deprecate_constant(self, :HTTP_IF_MODIFIED_SINCE)
79
83
  HTTP_IF_UNMODIFIED_SINCE = 'HTTP_IF_UNMODIFIED_SINCE'.freeze
84
+ RodaPlugins.deprecate_constant(self, :HTTP_IF_UNMODIFIED_SINCE)
80
85
  ETAG = 'ETag'.freeze
86
+ RodaPlugins.deprecate_constant(self, :ETAG)
81
87
  STAR = '*'.freeze
88
+ RodaPlugins.deprecate_constant(self, :STAR)
82
89
 
83
90
  # Set the last modified time of the resource using the Last-Modified header.
84
91
  # The +time+ argument should be a Time instance.
@@ -94,16 +101,16 @@ class Roda
94
101
  return unless time
95
102
  res = response
96
103
  e = env
97
- res[LAST_MODIFIED] = time.httpdate
98
- return if e[HTTP_IF_NONE_MATCH]
104
+ res['Last-Modified'] = time.httpdate
105
+ return if e['HTTP_IF_NONE_MATCH']
99
106
  status = res.status
100
107
 
101
- if (!status || status == 200) && (ims = time_from_header(e[HTTP_IF_MODIFIED_SINCE])) && ims >= time.to_i
108
+ if (!status || status == 200) && (ims = time_from_header(e['HTTP_IF_MODIFIED_SINCE'])) && ims >= time.to_i
102
109
  res.status = 304
103
110
  halt
104
111
  end
105
112
 
106
- if (!status || (status >= 200 && status < 300) || status == 412) && (ius = time_from_header(e[HTTP_IF_UNMODIFIED_SINCE])) && ius < time.to_i
113
+ if (!status || (status >= 200 && status < 300) || status == 412) && (ius = time_from_header(e['HTTP_IF_UNMODIFIED_SINCE'])) && ius < time.to_i
107
114
  res.status = 412
108
115
  halt
109
116
  end
@@ -130,16 +137,16 @@ class Roda
130
137
 
131
138
  res = response
132
139
  e = env
133
- res[ETAG] = etag = "#{'W/' if weak}\"#{value}\""
140
+ res['ETag'] = etag = "#{'W/' if weak}\"#{value}\""
134
141
  status = res.status
135
142
 
136
143
  if (!status || (status >= 200 && status < 300) || status == 304)
137
- if etag_matches?(e[HTTP_IF_NONE_MATCH], etag, new_resource)
144
+ if etag_matches?(e['HTTP_IF_NONE_MATCH'], etag, new_resource)
138
145
  res.status = (request_method =~ /\AGET|HEAD|OPTIONS|TRACE\z/i ? 304 : 412)
139
146
  halt
140
147
  end
141
148
 
142
- if ifm = e[HTTP_IF_MATCH]
149
+ if ifm = e['HTTP_IF_MATCH']
143
150
  unless etag_matches?(ifm, etag, new_resource)
144
151
  res.status = 412
145
152
  halt
@@ -153,7 +160,7 @@ class Roda
153
160
  # Helper method checking if a ETag value list includes the current ETag.
154
161
  def etag_matches?(list, etag, new_resource)
155
162
  return unless list
156
- return !new_resource if list == STAR
163
+ return !new_resource if list == '*'
157
164
  list.to_s.split(/\s*,\s*/).include?(etag)
158
165
  end
159
166
 
@@ -145,11 +145,16 @@ class Roda
145
145
  # method is not called until template rendering, the flash may not be
146
146
  # rotated.
147
147
  module Chunked
148
+ OPTS = {}.freeze
149
+
148
150
  HTTP_VERSION = 'HTTP_VERSION'.freeze
151
+ RodaPlugins.deprecate_constant(self, :HTTP_VERSION)
149
152
  HTTP11 = "HTTP/1.1".freeze
153
+ RodaPlugins.deprecate_constant(self, :HTTP11)
150
154
  TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
155
+ RodaPlugins.deprecate_constant(self, :TRANSFER_ENCODING)
151
156
  CHUNKED = 'chunked'.freeze
152
- OPTS = {}.freeze
157
+ RodaPlugins.deprecate_constant(self, :CHUNKED)
153
158
 
154
159
  # Depend on the render plugin
155
160
  def self.load_dependencies(app, opts=OPTS)
@@ -215,7 +220,7 @@ class Roda
215
220
  # an overview. If a block is given, it is passed to #delay.
216
221
  def chunked(template, opts=OPTS, &block)
217
222
  unless defined?(@_chunked)
218
- @_chunked = env[HTTP_VERSION] == HTTP11
223
+ @_chunked = env['HTTP_VERSION'] == "HTTP/1.1"
219
224
  end
220
225
 
221
226
  if block
@@ -245,7 +250,7 @@ class Roda
245
250
  if chunk_headers = self.opts[:chunk_headers]
246
251
  headers.merge!(chunk_headers)
247
252
  end
248
- headers[TRANSFER_ENCODING] = CHUNKED
253
+ headers['Transfer-Encoding'] = 'chunked'
249
254
 
250
255
  throw :halt, res.finish_with_body(Body.new(self))
251
256
  end
@@ -12,8 +12,11 @@ class Roda
12
12
  module ResponseMethods
13
13
  DROP_BODY_STATUSES = [100, 101, 102, 204, 205, 304].freeze
14
14
  EMPTY_BODY = [].freeze
15
+
15
16
  CONTENT_LENGTH = "Content-Length".freeze
17
+ RodaPlugins.deprecate_constant(self, :CONTENT_LENGTH)
16
18
  CONTENT_TYPE = "Content-Type".freeze
19
+ RodaPlugins.deprecate_constant(self, :CONTENT_TYPE)
17
20
 
18
21
  # If the response status indicates a body should not be
19
22
  # returned, use an empty body and remove the Content-Length
@@ -23,8 +26,8 @@ class Roda
23
26
  if DROP_BODY_STATUSES.include?(r[0])
24
27
  r[2] = EMPTY_BODY
25
28
  h = r[1]
26
- h.delete(CONTENT_LENGTH)
27
- h.delete(CONTENT_TYPE)
29
+ h.delete("Content-Length")
30
+ h.delete("Content-Type")
28
31
  end
29
32
  r
30
33
  end
@@ -32,6 +32,7 @@ class Roda
32
32
  # it is empty.
33
33
  module EmptyRoot
34
34
  EMPTY_STRING = ''.freeze
35
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
35
36
 
36
37
  module RequestMethods
37
38
  # Match when the remaining path is the empty string,
@@ -39,7 +40,7 @@ class Roda
39
40
  # the remaining path is +/+.
40
41
  def root(&block)
41
42
  super
42
- if remaining_path == EMPTY_STRING && is_get?
43
+ if remaining_path == '' && is_get?
43
44
  always(&block)
44
45
  end
45
46
  end
@@ -94,7 +94,7 @@ END
94
94
 
95
95
  message
96
96
  end
97
- }
97
+ }#.freeze # RODA3
98
98
 
99
99
  # Set default opts for plugin. See ErrorEmail module RDoc for options.
100
100
  def self.configure(app, opts=OPTS)
@@ -38,6 +38,7 @@ class Roda
38
38
  module Flash
39
39
  # The internal session key used to store the flash.
40
40
  KEY = :_flash
41
+ RodaPlugins.deprecate_constant(self, :KEY)
41
42
 
42
43
  # Simple flash hash, where assiging to the hash updates the flash
43
44
  # used in the following request.
@@ -94,7 +95,7 @@ class Roda
94
95
  # Access the flash hash for the current request, loading
95
96
  # it from the session if it is not already loaded.
96
97
  def flash
97
- @_flash ||= FlashHash.new(session[KEY])
98
+ @_flash ||= FlashHash.new(session[:_flash])
98
99
  end
99
100
 
100
101
  # If the routing doesn't raise an error, rotate the flash
@@ -103,7 +104,7 @@ class Roda
103
104
  res = super
104
105
 
105
106
  if f = @_flash
106
- session[KEY] = f.next
107
+ session[:_flash] = f.next
107
108
  end
108
109
 
109
110
  res
@@ -18,7 +18,7 @@ class Roda
18
18
  require 'cgi/escape'
19
19
  unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
20
20
  CGI = Object.new
21
- CGI.extend(::CGI::Util)
21
+ CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
22
22
  end
23
23
 
24
24
  module InstanceMethods
@@ -15,9 +15,11 @@ class Roda
15
15
  # plugin :heartbeat, :path=>'/status'
16
16
  module Heartbeat
17
17
  OPTS = {}.freeze
18
- PATH_INFO = 'PATH_INFO'.freeze
19
18
  HEARTBEAT_RESPONSE = [200, {'Content-Type'=>'text/plain'}.freeze, ['OK'.freeze].freeze].freeze
20
19
 
20
+ PATH_INFO = 'PATH_INFO'.freeze
21
+ RodaPlugins.deprecate_constant(self, :PATH_INFO)
22
+
21
23
  # Set the heartbeat path to the given path.
22
24
  def self.configure(app, opts=OPTS)
23
25
  app.opts[:heartbeat_path] = (opts[:path] || app.opts[:heartbeat_path] || "/heartbeat").dup.freeze
@@ -26,7 +28,7 @@ class Roda
26
28
  module InstanceMethods
27
29
  # If the request is for a heartbeat path, return the heartbeat response.
28
30
  def call
29
- if env[PATH_INFO] == opts[:heartbeat_path]
31
+ if env['PATH_INFO'] == opts[:heartbeat_path]
30
32
  response = HEARTBEAT_RESPONSE.dup
31
33
  response[1] = Hash[response[1]]
32
34
  response
@@ -54,8 +54,10 @@ class Roda
54
54
  # plugin :json, :content_type=>'application/xml'
55
55
  module Json
56
56
  OPTS = {}.freeze
57
- DEFAULT_SERIALIZER = lambda{|o| o.to_json}
57
+ DEFAULT_SERIALIZER = :to_json.to_proc
58
+
58
59
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
60
+ RodaPlugins.deprecate_constant(self, :DEFAULT_CONTENT_TYPE)
59
61
 
60
62
  # Set the classes to automatically convert to JSON, and the serializer to use.
61
63
  def self.configure(app, opts=OPTS)
@@ -69,7 +71,7 @@ class Roda
69
71
 
70
72
  app.opts[:json_result_include_request] = opts[:include_request] || app.opts[:json_result_include_request]
71
73
 
72
- app.opts[:json_result_content_type] = opts[:content_type] || DEFAULT_CONTENT_TYPE
74
+ app.opts[:json_result_content_type] = opts[:content_type] || 'application/json'.freeze
73
75
  end
74
76
 
75
77
  module ClassMethods
@@ -12,12 +12,17 @@ class Roda
12
12
  # header for the request includes "json".
13
13
  module JsonParser
14
14
  OPTS = {}.freeze
15
+ DEFAULT_ERROR_HANDLER = proc{|r| r.halt [400, {}, []]}
16
+ DEFAULT_PARSER = JSON.method(:parse)
17
+
15
18
  JSON_PARAMS_KEY = "roda.json_params".freeze
19
+ RodaPlugins.deprecate_constant(self, :JSON_PARAMS_KEY)
16
20
  INPUT_KEY = "rack.input".freeze
21
+ RodaPlugins.deprecate_constant(self, :INPUT_KEY)
17
22
  FORM_HASH_KEY = "rack.request.form_hash".freeze
23
+ RodaPlugins.deprecate_constant(self, :FORM_HASH_KEY)
18
24
  FORM_INPUT_KEY = "rack.request.form_input".freeze
19
- DEFAULT_ERROR_HANDLER = proc{|r| r.halt [400, {}, []]}
20
- DEFAULT_PARSER = JSON.method(:parse)
25
+ RodaPlugins.deprecate_constant(self, :FORM_INPUT_KEY)
21
26
 
22
27
  # Handle options for the json_parser plugin:
23
28
  # :error_handler :: A proc to call if an exception is raised when
@@ -41,18 +46,18 @@ class Roda
41
46
  # parse the request body as JSON. Ignore an empty request body.
42
47
  def POST
43
48
  env = @env
44
- if post_params = (env[JSON_PARAMS_KEY] || env[FORM_HASH_KEY])
49
+ if post_params = (env["roda.json_params"] || env["rack.request.form_hash"])
45
50
  post_params
46
- elsif (input = env[INPUT_KEY]) && content_type =~ /json/
51
+ elsif (input = env["rack.input"]) && content_type =~ /json/
47
52
  str = input.read
48
53
  input.rewind
49
54
  return super if str.empty?
50
55
  begin
51
- json_params = env[JSON_PARAMS_KEY] = parse_json(str)
56
+ json_params = env["roda.json_params"] = parse_json(str)
52
57
  rescue
53
58
  roda_class.opts[:json_parser_error_handler].call(self)
54
59
  end
55
- env[FORM_INPUT_KEY] = input
60
+ env["rack.request.form_input"] = input
56
61
  json_params
57
62
  else
58
63
  super
@@ -110,17 +110,28 @@ class Roda
110
110
  # Roda application if you want your helper methods to automatically be
111
111
  # available in your email views.
112
112
  module Mailer
113
+ OPTS = {}.freeze
114
+
113
115
  REQUEST_METHOD = "REQUEST_METHOD".freeze
116
+ RodaPlugins.deprecate_constant(self, :REQUEST_METHOD)
114
117
  PATH_INFO = "PATH_INFO".freeze
118
+ RodaPlugins.deprecate_constant(self, :PATH_INFO)
115
119
  SCRIPT_NAME = 'SCRIPT_NAME'.freeze
120
+ RodaPlugins.deprecate_constant(self, :SCRIPT_NAME)
116
121
  EMPTY_STRING = ''.freeze
122
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
117
123
  RACK_INPUT = 'rack.input'.freeze
124
+ RodaPlugins.deprecate_constant(self, :RACK_INPUT)
118
125
  RODA_MAIL = 'roda.mail'.freeze
126
+ RodaPlugins.deprecate_constant(self, :RODA_MAIL)
119
127
  RODA_MAIL_ARGS = 'roda.mail_args'.freeze
128
+ RodaPlugins.deprecate_constant(self, :RODA_MAIL_ARGS)
120
129
  MAIL = "MAIL".freeze
130
+ RodaPlugins.deprecate_constant(self, :MAIL)
121
131
  CONTENT_TYPE = 'Content-Type'.freeze
132
+ RodaPlugins.deprecate_constant(self, :CONTENT_TYPE)
122
133
  TEXT_PLAIN = "text/plain".freeze
123
- OPTS = {}.freeze
134
+ RodaPlugins.deprecate_constant(self, :TEXT_PLAIN)
124
135
 
125
136
  # Error raised when the using the mail class method, but the routing
126
137
  # tree doesn't return the mail object.
@@ -139,7 +150,7 @@ class Roda
139
150
  def mail(path, *args)
140
151
  mail = ::Mail.new
141
152
  catch(:no_mail) do
142
- unless mail.equal?(new(PATH_INFO=>path, SCRIPT_NAME=>EMPTY_STRING, REQUEST_METHOD=>MAIL, RACK_INPUT=>StringIO.new, RODA_MAIL=>mail, RODA_MAIL_ARGS=>args).call(&route_block))
153
+ unless mail.equal?(new("PATH_INFO"=>path, 'SCRIPT_NAME'=>'', "REQUEST_METHOD"=>"MAIL", 'rack.input'=>StringIO.new, 'roda.mail'=>mail, 'roda.mail_args'=>args).call(&route_block))
143
154
  raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
144
155
  end
145
156
  mail
@@ -161,9 +172,9 @@ class Roda
161
172
  # the request. This yields any of the captures to the block, as well as
162
173
  # any arguments passed to the +mail+ or +sendmail+ Roda class methods.
163
174
  def mail(*args)
164
- if @env[REQUEST_METHOD] == MAIL
175
+ if @env["REQUEST_METHOD"] == "MAIL"
165
176
  if_match(args) do |*vs|
166
- yield(*(vs + @env[RODA_MAIL_ARGS]))
177
+ yield(*(vs + @env['roda.mail_args']))
167
178
  end
168
179
  end
169
180
  end
@@ -179,7 +190,7 @@ class Roda
179
190
  # that the routing tree did not handle the request.
180
191
  def finish
181
192
  if m = mail
182
- header_content_type = @headers.delete(CONTENT_TYPE)
193
+ header_content_type = @headers.delete('Content-Type')
183
194
  m.headers(@headers)
184
195
  m.body(@body.join) unless @body.empty?
185
196
  mail_attachments.each do |a, block|
@@ -191,7 +202,7 @@ class Roda
191
202
  if mail.multipart?
192
203
  if mail.content_type =~ /multipart\/mixed/ &&
193
204
  mail.parts.length >= 2 &&
194
- (part = mail.parts.find{|p| !p.attachment && p.content_type == TEXT_PLAIN})
205
+ (part = mail.parts.find{|p| !p.attachment && p.content_type == "text/plain"})
195
206
  part.content_type = content_type
196
207
  end
197
208
  else
@@ -217,7 +228,7 @@ class Roda
217
228
  # Add delegates for common email methods.
218
229
  [:from, :to, :cc, :bcc, :subject].each do |meth|
219
230
  define_method(meth) do |*args|
220
- env[RODA_MAIL].send(meth, *args)
231
+ env['roda.mail'].send(meth, *args)
221
232
  nil
222
233
  end
223
234
  end
@@ -231,10 +242,10 @@ class Roda
231
242
  # as the default content_type for the email.
232
243
  def initialize(env)
233
244
  super
234
- if mail = env[RODA_MAIL]
245
+ if mail = env['roda.mail']
235
246
  res = @_response
236
247
  res.mail = mail
237
- res.headers.delete(CONTENT_TYPE)
248
+ res.headers.delete('Content-Type')
238
249
  end
239
250
  end
240
251
 
@@ -256,7 +267,7 @@ class Roda
256
267
  # Set the text_part or html_part (depending on the method) in the related email,
257
268
  # using the given body and optional headers.
258
269
  def _mail_part(meth, body, headers=nil)
259
- env[RODA_MAIL].send(meth) do
270
+ env['roda.mail'].send(meth) do
260
271
  body(body)
261
272
  headers(headers) if headers
262
273
  end
@@ -27,7 +27,9 @@ class Roda
27
27
  # at the end of the path only.
28
28
  module MatchAffix
29
29
  PREFIX = "/".freeze
30
+ RodaPlugins.deprecate_constant(self, :PREFIX)
30
31
  SUFFIX = "(?=\/|\z)".freeze
32
+ RodaPlugins.deprecate_constant(self, :SUFFIX)
31
33
 
32
34
  # Set the default prefix and suffix to use in match patterns, if a non-nil value
33
35
  # is given.
@@ -42,7 +44,7 @@ class Roda
42
44
  # Use the match prefix and suffix provided when loading the plugin, or fallback
43
45
  # to Roda's default prefix/suffix if one was not provided.
44
46
  def consume_pattern(pattern)
45
- /\A#{roda_class.opts[:match_prefix] || PREFIX}(?:#{pattern})#{roda_class.opts[:match_suffix] || SUFFIX}/
47
+ /\A#{roda_class.opts[:match_prefix] || "/"}(?:#{pattern})#{roda_class.opts[:match_suffix] || "(?=\/|\z)"}/
46
48
  end
47
49
  end
48
50
 
@@ -25,6 +25,7 @@ class Roda
25
25
  # match blocks.
26
26
  module OptimizedStringMatchers
27
27
  EMPTY_STRING = ''.freeze
28
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
28
29
 
29
30
  module RequestMethods
30
31
  # Optimized version of +on+ that only supports a single string.
@@ -36,7 +37,7 @@ class Roda
36
37
  def is_exactly(s)
37
38
  rp = @remaining_path
38
39
  if _match_string(s)
39
- if @remaining_path == EMPTY_STRING
40
+ if @remaining_path == ''
40
41
  always{yield}
41
42
  else
42
43
  @remaining_path = rp
@@ -21,7 +21,9 @@ class Roda
21
21
  # Note that this plugin automatically loads the :render plugin.
22
22
  module Partials
23
23
  OPTS = {}.freeze
24
+
24
25
  SLASH = '/'.freeze
26
+ RodaPlugins.deprecate_constant(self, :SLASH)
25
27
 
26
28
  # Depend on the render plugin, since this overrides
27
29
  # some of its methods.
@@ -36,9 +38,9 @@ class Roda
36
38
  def partial(template, opts=OPTS)
37
39
  opts = parse_template_opts(template, opts)
38
40
  if opts[:template]
39
- template = opts[:template].split(SLASH)
41
+ template = opts[:template].split('/')
40
42
  template[-1] = "_#{template[-1]}"
41
- opts[:template] = template.join(SLASH)
43
+ opts[:template] = template.join('/')
42
44
  end
43
45
  render_template(opts)
44
46
  end
@@ -45,9 +45,11 @@ class Roda
45
45
  # it can be rewritten again by a later rewrite. Note that PATH_INFO rewrites are
46
46
  # processed before remaining_path rewrites.
47
47
  module PathRewriter
48
- PATH_INFO = 'PATH_INFO'.freeze
49
48
  OPTS={}.freeze
50
49
 
50
+ PATH_INFO = 'PATH_INFO'.freeze
51
+ RodaPlugins.deprecate_constant(self, :PATH_INFO)
52
+
51
53
  def self.configure(app)
52
54
  app.instance_exec do
53
55
  app.opts[:remaining_path_rewrites] ||= []
@@ -86,7 +88,7 @@ class Roda
86
88
  module RequestMethods
87
89
  # Rewrite remaining_path and/or PATH_INFO based on the path rewrites.
88
90
  def initialize(scope, env)
89
- path_info = env[PATH_INFO]
91
+ path_info = env['PATH_INFO']
90
92
 
91
93
  rewrite_path(scope.class.opts[:path_info_rewrites], path_info)
92
94
  super
@@ -21,10 +21,12 @@ class Roda
21
21
  # plugin :public
22
22
  # plugin :public, :root=>'static'
23
23
  module Public
24
- NULL_BYTE = "\0".freeze
25
24
  SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
26
25
  PARSER = RUBY_VERSION >= '1.9' ? URI::DEFAULT_PARSER : URI
27
26
 
27
+ NULL_BYTE = "\0".freeze
28
+ RodaPlugins.deprecate_constant(self, :NULL_BYTE)
29
+
28
30
  # Use options given to setup a Rack::File instance for serving files. Options:
29
31
  # :default_mime :: The default mime type to use if the mime type is not recognized.
30
32
  # :gzip :: Whether to serve already gzipped files with a .gz extension for clients
@@ -42,7 +44,7 @@ class Roda
42
44
  def public
43
45
  if is_get?
44
46
  path = PARSER.unescape(real_remaining_path)
45
- return if path.include?(NULL_BYTE)
47
+ return if path.include?("\0")
46
48
 
47
49
  roda_opts = roda_class.opts
48
50
  server = roda_opts[:public_server]
@@ -22,6 +22,7 @@ class Roda
22
22
  # # GET /a/ => App gets "/" as PATH_INFO
23
23
  module RunAppendSlash
24
24
  OPTS = {}.freeze
25
+
25
26
  # Set plugin specific options. Options:
26
27
  # :use_redirects :: Whether to issue 302 redirects when appending the
27
28
  # trailing slash.
@@ -48,6 +48,7 @@ class Roda
48
48
  # end
49
49
  module SharedVars
50
50
  KEY = 'roda.shared'.freeze
51
+ RodaPlugins.deprecate_constant(self, :KEY)
51
52
 
52
53
  module InstanceMethods
53
54
  # Returns the current shared vars for the request. These are
@@ -61,15 +62,15 @@ class Roda
61
62
  # only make the changes to the shared vars for the duration of the
62
63
  # block, restoring the previous shared vars before the block returns.
63
64
  def shared(vars=nil)
64
- h = env[KEY] ||= {}
65
+ h = env['roda.shared'] ||= {}
65
66
 
66
67
  if block_given?
67
68
  if vars
68
69
  begin
69
- env[KEY] = Hash[h].merge!(vars)
70
+ env['roda.shared'] = Hash[h].merge!(vars)
70
71
  yield
71
72
  ensure
72
- env[KEY] = h
73
+ env['roda.shared'] = h
73
74
  end
74
75
  else
75
76
  raise RodaError, "must pass a vars hash when calling shared with a block"
@@ -211,20 +211,39 @@ class Roda
211
211
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
212
212
  # OTHER DEALINGS IN THE SOFTWARE.
213
213
  module SinatraHelpers
214
+ OPTS = {}.freeze
215
+
214
216
  CONTENT_TYPE = "Content-Type".freeze
217
+ RodaPlugins.deprecate_constant(self, :CONTENT_TYPE)
215
218
  CONTENT_DISPOSITION = "Content-Disposition".freeze
219
+ RodaPlugins.deprecate_constant(self, :CONTENT_DISPOSITION)
216
220
  CONTENT_LENGTH = "Content-Length".freeze
221
+ RodaPlugins.deprecate_constant(self, :CONTENT_LENGTH)
217
222
  OCTET_STREAM = 'application/octet-stream'.freeze
223
+ RodaPlugins.deprecate_constant(self, :OCTET_STREAM)
218
224
  ATTACHMENT = 'attachment'.freeze
225
+ RodaPlugins.deprecate_constant(self, :ATTACHMENT)
219
226
  HTTP_VERSION = 'HTTP_VERSION'.freeze
227
+ RodaPlugins.deprecate_constant(self, :HTTP_VERSION)
220
228
  HTTP11 = "HTTP/1.1".freeze
229
+ RodaPlugins.deprecate_constant(self, :HTTP11)
221
230
  HTTP_X_FORWARDED_HOST = "HTTP_X_FORWARDED_HOST".freeze
231
+ RodaPlugins.deprecate_constant(self, :HTTP_X_FORWARDED_HOST)
222
232
  EMPTY_STRING = ''.freeze
233
+ RodaPlugins.deprecate_constant(self, :EMPTY_STRING)
223
234
  SLASH = '/'.freeze
235
+ RodaPlugins.deprecate_constant(self, :SLASH)
224
236
  SEMICOLON = ';'.freeze
237
+ RodaPlugins.deprecate_constant(self, :SEMICOLON)
225
238
  COMMA = ', '.freeze
239
+ RodaPlugins.deprecate_constant(self, :COMMA)
226
240
  CHARSET = 'charset'.freeze
227
- OPTS = {}.freeze
241
+ RodaPlugins.deprecate_constant(self, :CHARSET)
242
+
243
+ # Depend on the status_303 plugin.
244
+ def self.load_dependencies(app, _opts = nil)
245
+ app.plugin :status_303
246
+ end
228
247
 
229
248
  # Add delegate methods to the route block scope
230
249
  # calling request or response methods, unless the
@@ -323,14 +342,14 @@ class Roda
323
342
  def send_file(path, opts = OPTS)
324
343
  res = response
325
344
  headers = res.headers
326
- if opts[:type] || !headers[CONTENT_TYPE]
327
- res.content_type(opts[:type] || ::File.extname(path), :default => OCTET_STREAM)
345
+ if opts[:type] || !headers["Content-Type"]
346
+ res.content_type(opts[:type] || ::File.extname(path), :default => 'application/octet-stream')
328
347
  end
329
348
 
330
349
  disposition = opts[:disposition]
331
350
  filename = opts[:filename]
332
351
  if disposition || filename
333
- disposition ||= ATTACHMENT
352
+ disposition ||= 'attachment'
334
353
  filename = path if filename.nil?
335
354
  res.attachment(filename, disposition)
336
355
  end
@@ -350,7 +369,7 @@ class Roda
350
369
  end
351
370
 
352
371
  res.status = opts[:status] || s
353
- headers.delete(CONTENT_LENGTH)
372
+ headers.delete("Content-Length")
354
373
  headers.replace(h.merge!(headers))
355
374
  res.body = b
356
375
 
@@ -365,14 +384,14 @@ class Roda
365
384
  addr = addr.to_s if addr
366
385
  return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
367
386
  uri = if absolute
368
- h = if @env.has_key?(HTTP_X_FORWARDED_HOST) || port != (ssl? ? 443 : 80)
387
+ h = if @env.has_key?("HTTP_X_FORWARDED_HOST") || port != (ssl? ? 443 : 80)
369
388
  host_with_port
370
389
  else
371
390
  host
372
391
  end
373
392
  ["http#{'s' if ssl?}://#{h}"]
374
393
  else
375
- [EMPTY_STRING]
394
+ ['']
376
395
  end
377
396
  uri << script_name.to_s if add_script_name
378
397
  uri << (addr || path_info)
@@ -380,17 +399,6 @@ class Roda
380
399
  end
381
400
  alias url uri
382
401
  alias to uri
383
-
384
- private
385
-
386
- # Use a 303 response for non-GET responses if client uses HTTP 1.1.
387
- def default_redirect_status
388
- if @env[HTTP_VERSION] == HTTP11 && !is_get?
389
- 303
390
- else
391
- super
392
- end
393
- end
394
402
  end
395
403
 
396
404
  module ResponseMethods
@@ -416,7 +424,7 @@ class Roda
416
424
 
417
425
  # If the body is a DelayedBody, set the appropriate length for it.
418
426
  def finish
419
- @length = @body.length if @body.is_a?(DelayedBody) && !@headers[CONTENT_LENGTH]
427
+ @length = @body.length if @body.is_a?(DelayedBody) && !@headers["Content-Length"]
420
428
  super
421
429
  end
422
430
 
@@ -433,35 +441,35 @@ class Roda
433
441
 
434
442
  # Set the Content-Type of the response body given a media type or file
435
443
  # extension. See plugin documentation for options.
436
- def content_type(type = (return @headers[CONTENT_TYPE]; nil), opts = OPTS)
444
+ def content_type(type = (return @headers["Content-Type"]; nil), opts = OPTS)
437
445
  unless (mime_type = mime_type(type) || opts[:default])
438
446
  raise RodaError, "Unknown media type: #{type}"
439
447
  end
440
448
 
441
449
  unless opts.empty?
442
450
  opts.each do |key, val|
443
- next if key == :default || (key == :charset && mime_type.include?(CHARSET))
451
+ next if key == :default || (key == :charset && mime_type.include?('charset'))
444
452
  val = val.inspect if val =~ /[";,]/
445
- mime_type += "#{mime_type.include?(SEMICOLON) ? COMMA : SEMICOLON}#{key}=#{val}"
453
+ mime_type += "#{mime_type.include?(';') ? ', ' : ';'}#{key}=#{val}"
446
454
  end
447
455
  end
448
456
 
449
- @headers[CONTENT_TYPE] = mime_type
457
+ @headers["Content-Type"] = mime_type
450
458
  end
451
459
 
452
460
  # Set the Content-Disposition to "attachment" with the specified filename,
453
461
  # instructing the user agents to prompt to save.
454
- def attachment(filename = nil, disposition=ATTACHMENT)
462
+ def attachment(filename = nil, disposition='attachment')
455
463
  if filename
456
464
  params = "; filename=#{File.basename(filename).inspect}"
457
- unless @headers[CONTENT_TYPE]
465
+ unless @headers["Content-Type"]
458
466
  ext = File.extname(filename)
459
467
  unless ext.empty?
460
468
  content_type(ext)
461
469
  end
462
470
  end
463
471
  end
464
- @headers[CONTENT_DISPOSITION] = "#{disposition}#{params}"
472
+ @headers["Content-Disposition"] = "#{disposition}#{params}"
465
473
  end
466
474
 
467
475
  # Whether or not the status is set to 1xx. Returns nil if status not yet set.
@@ -500,7 +508,7 @@ class Roda
500
508
  # If only a type is given, lookup the type in Rack's MIME registry and
501
509
  # return it.
502
510
  def mime_type(type=(return; nil), value = nil)
503
- return type.to_s if type.to_s.include?(SLASH)
511
+ return type.to_s if type.to_s.include?('/')
504
512
  type = ".#{type}" unless type.to_s[0] == ?.
505
513
  if value
506
514
  Rack::Mime::MIME_TYPES[type] = value
@@ -12,13 +12,14 @@ class Roda
12
12
  # where a trailing "/" in the path should be ignored.
13
13
  module SlashPathEmpty
14
14
  SLASH = "/".freeze
15
+ RodaPlugins.deprecate_constant(self, :SLASH)
15
16
 
16
17
  module RequestMethods
17
18
  private
18
19
 
19
20
  # Consider the path empty if it is "/".
20
21
  def empty_path?
21
- super || remaining_path == SLASH
22
+ super || remaining_path == '/'
22
23
  end
23
24
  end
24
25
  end
@@ -0,0 +1,32 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The status_303 plugin sets the default redirect status to be 303
7
+ # rather than 302 when the request is not a GET and the
8
+ # redirection occurs on an HTTP 1.1 connection as per RFC 7231.
9
+ # The author knows of no cases where this actually matters in
10
+ # practice.
11
+ #
12
+ # Example:
13
+ #
14
+ # plugin :status_303
15
+ module Status303
16
+ module RequestMethods
17
+
18
+ private
19
+
20
+ def default_redirect_status
21
+ if env['HTTP_VERSION'] == 'HTTP/1.1' && !is_get?
22
+ 303
23
+ else
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ register_plugin(:status_303, Status303)
31
+ end
32
+ end
@@ -82,7 +82,9 @@ class Roda
82
82
  # Default is +true+.
83
83
  module TypeRouting
84
84
  ACCEPT_HEADER = 'HTTP_ACCEPT'.freeze
85
+ RodaPlugins.deprecate_constant(self, :ACCEPT_HEADER)
85
86
  CONTENT_TYPE_HEADER = 'Content-Type'.freeze
87
+ RodaPlugins.deprecate_constant(self, :CONTENT_TYPE_HEADER)
86
88
 
87
89
  CONFIGURATION = {
88
90
  :mimes => {
@@ -146,7 +148,7 @@ class Roda
146
148
  # the request afterwards, returning the result of the block.
147
149
  def on_type(type, &block)
148
150
  return unless type == requested_type
149
- response[CONTENT_TYPE_HEADER] ||= @scope.opts[:type_routing][:types][type]
151
+ response['Content-Type'] ||= @scope.opts[:type_routing][:types][type]
150
152
  always(&block)
151
153
  end
152
154
 
@@ -191,7 +193,7 @@ class Roda
191
193
  def accept_response_type
192
194
  mimes = @scope.opts[:type_routing][:mimes]
193
195
 
194
- @env[ACCEPT_HEADER].to_s.split(/\s*,\s*/).map do |part|
196
+ @env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do |part|
195
197
  mime, _= part.split(/\s*;\s*/, 2)
196
198
  if sym = mimes[mime]
197
199
  return sym
@@ -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 = 27
7
+ RodaMinorVersion = 28
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -0,0 +1,28 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "status_303 plugin" do
4
+ it 'uses a 302 for get requests' do
5
+ app(:status_303) do
6
+ request.redirect '/foo'
7
+ fail 'redirect should halt'
8
+ end
9
+ status.must_equal 302
10
+ body.must_equal ''
11
+ header('Location').must_equal '/foo'
12
+ end
13
+
14
+ it 'uses the code given when specified' do
15
+ app(:status_303) do
16
+ request.redirect '/foo', 301
17
+ end
18
+ status.must_equal 301
19
+ end
20
+
21
+ it 'uses 303 for post requests if request is HTTP 1.1, 302 for 1.0' do
22
+ app(:status_303) do
23
+ request.redirect '/foo'
24
+ end
25
+ status('HTTP_VERSION' => 'HTTP/1.1', 'REQUEST_METHOD'=>'POST').must_equal 303
26
+ status('HTTP_VERSION' => 'HTTP/1.0', 'REQUEST_METHOD'=>'POST').must_equal 302
27
+ end
28
+ end
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.27.0
4
+ version: 2.28.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: 2017-06-14 00:00:00.000000000 Z
11
+ date: 2017-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -207,6 +207,7 @@ extra_rdoc_files:
207
207
  - doc/release_notes/2.25.0.txt
208
208
  - doc/release_notes/2.26.0.txt
209
209
  - doc/release_notes/2.27.0.txt
210
+ - doc/release_notes/2.28.0.txt
210
211
  files:
211
212
  - CHANGELOG
212
213
  - MIT-LICENSE
@@ -238,6 +239,7 @@ files:
238
239
  - doc/release_notes/2.25.0.txt
239
240
  - doc/release_notes/2.26.0.txt
240
241
  - doc/release_notes/2.27.0.txt
242
+ - doc/release_notes/2.28.0.txt
241
243
  - doc/release_notes/2.3.0.txt
242
244
  - doc/release_notes/2.4.0.txt
243
245
  - doc/release_notes/2.5.0.txt
@@ -316,6 +318,7 @@ files:
316
318
  - lib/roda/plugins/static.rb
317
319
  - lib/roda/plugins/static_path_info.rb
318
320
  - lib/roda/plugins/static_routing.rb
321
+ - lib/roda/plugins/status_303.rb
319
322
  - lib/roda/plugins/status_handler.rb
320
323
  - lib/roda/plugins/streaming.rb
321
324
  - lib/roda/plugins/strip_path_prefix.rb
@@ -407,6 +410,7 @@ files:
407
410
  - spec/plugin/slash_path_empty_spec.rb
408
411
  - spec/plugin/static_routing_spec.rb
409
412
  - spec/plugin/static_spec.rb
413
+ - spec/plugin/status_303_spec.rb
410
414
  - spec/plugin/status_handler_spec.rb
411
415
  - spec/plugin/streaming_spec.rb
412
416
  - spec/plugin/strip_path_prefix_spec.rb