roda 2.27.0 → 2.28.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: 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