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 +4 -4
- data/CHANGELOG +8 -0
- data/doc/release_notes/2.28.0.txt +17 -0
- data/lib/roda.rb +45 -23
- data/lib/roda/plugins/assets.rb +27 -13
- data/lib/roda/plugins/assets_preloading.rb +5 -2
- data/lib/roda/plugins/backtracking_array.rb +2 -0
- data/lib/roda/plugins/caching.rb +15 -8
- data/lib/roda/plugins/chunked.rb +8 -3
- data/lib/roda/plugins/drop_body.rb +5 -2
- data/lib/roda/plugins/empty_root.rb +2 -1
- data/lib/roda/plugins/error_email.rb +1 -1
- data/lib/roda/plugins/flash.rb +3 -2
- data/lib/roda/plugins/h.rb +1 -1
- data/lib/roda/plugins/heartbeat.rb +4 -2
- data/lib/roda/plugins/json.rb +4 -2
- data/lib/roda/plugins/json_parser.rb +11 -6
- data/lib/roda/plugins/mailer.rb +21 -10
- data/lib/roda/plugins/match_affix.rb +3 -1
- data/lib/roda/plugins/optimized_string_matchers.rb +2 -1
- data/lib/roda/plugins/partials.rb +4 -2
- data/lib/roda/plugins/path_rewriter.rb +4 -2
- data/lib/roda/plugins/public.rb +4 -2
- data/lib/roda/plugins/run_append_slash.rb +1 -0
- data/lib/roda/plugins/shared_vars.rb +4 -3
- data/lib/roda/plugins/sinatra_helpers.rb +35 -27
- data/lib/roda/plugins/slash_path_empty.rb +2 -1
- data/lib/roda/plugins/status_303.rb +32 -0
- data/lib/roda/plugins/type_routing.rb +4 -2
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/status_303_spec.rb +28 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca75e717d558d6b3c163517c6e8e3678a33b68e0
|
4
|
+
data.tar.gz: a08ab3922fc609e9413f6151444bc9a92186777e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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+.
|
data/lib/roda.rb
CHANGED
@@ -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
|
-
|
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] ==
|
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 ==
|
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[
|
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(
|
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
|
749
|
+
when "/"
|
732
750
|
@remaining_path = rp[last, rp.length]
|
733
751
|
when nil
|
734
|
-
@remaining_path =
|
752
|
+
@remaining_path = ""
|
735
753
|
when Integer
|
736
754
|
# :nocov:
|
737
755
|
# Ruby 1.8 support
|
738
|
-
if rp[last].chr ==
|
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] ==
|
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 =
|
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
|
-
|
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 ==
|
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
|
-
|
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(
|
1032
|
+
h.delete("Content-Type")
|
1011
1033
|
else
|
1012
|
-
h[
|
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[
|
1067
|
+
@headers["Location"] = path
|
1046
1068
|
@status = status
|
1047
1069
|
nil
|
1048
1070
|
end
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -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
|
-
|
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] ?
|
423
|
-
opts[:css_suffix] = opts[:add_suffix] ?
|
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(
|
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",
|
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(
|
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 =
|
672
|
+
tag_end = "\"></script>"
|
659
673
|
else
|
660
674
|
tag_start = "<link rel=\"stylesheet\" #{attrs} href=\""
|
661
|
-
tag_end =
|
675
|
+
tag_end = "\" />"
|
662
676
|
end
|
663
677
|
|
664
|
-
assets_paths(type).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join(
|
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[
|
681
|
-
file +=
|
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(
|
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(
|
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(
|
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
|
|
data/lib/roda/plugins/caching.rb
CHANGED
@@ -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[
|
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[
|
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 ==
|
163
|
+
return !new_resource if list == '*'
|
157
164
|
list.to_s.split(/\s*,\s*/).include?(etag)
|
158
165
|
end
|
159
166
|
|
data/lib/roda/plugins/chunked.rb
CHANGED
@@ -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
|
-
|
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] ==
|
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[
|
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(
|
27
|
-
h.delete(
|
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 ==
|
43
|
+
if remaining_path == '' && is_get?
|
43
44
|
always(&block)
|
44
45
|
end
|
45
46
|
end
|
data/lib/roda/plugins/flash.rb
CHANGED
@@ -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[
|
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[
|
107
|
+
session[:_flash] = f.next
|
107
108
|
end
|
108
109
|
|
109
110
|
res
|
data/lib/roda/plugins/h.rb
CHANGED
@@ -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
|
data/lib/roda/plugins/json.rb
CHANGED
@@ -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 =
|
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] ||
|
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
|
-
|
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[
|
49
|
+
if post_params = (env["roda.json_params"] || env["rack.request.form_hash"])
|
45
50
|
post_params
|
46
|
-
elsif (input = env[
|
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[
|
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[
|
60
|
+
env["rack.request.form_input"] = input
|
56
61
|
json_params
|
57
62
|
else
|
58
63
|
super
|
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -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
|
-
|
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=>
|
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[
|
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(
|
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 ==
|
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[
|
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[
|
245
|
+
if mail = env['roda.mail']
|
235
246
|
res = @_response
|
236
247
|
res.mail = mail
|
237
|
-
res.headers.delete(
|
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[
|
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] ||
|
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 ==
|
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(
|
41
|
+
template = opts[:template].split('/')
|
40
42
|
template[-1] = "_#{template[-1]}"
|
41
|
-
opts[:template] = template.join(
|
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
|
data/lib/roda/plugins/public.rb
CHANGED
@@ -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?(
|
47
|
+
return if path.include?("\0")
|
46
48
|
|
47
49
|
roda_opts = roda_class.opts
|
48
50
|
server = roda_opts[:public_server]
|
@@ -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[
|
65
|
+
h = env['roda.shared'] ||= {}
|
65
66
|
|
66
67
|
if block_given?
|
67
68
|
if vars
|
68
69
|
begin
|
69
|
-
env[
|
70
|
+
env['roda.shared'] = Hash[h].merge!(vars)
|
70
71
|
yield
|
71
72
|
ensure
|
72
|
-
env[
|
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
|
-
|
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[
|
327
|
-
res.content_type(opts[:type] || ::File.extname(path), :default =>
|
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 ||=
|
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(
|
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
|
-
[
|
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[
|
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[
|
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?(
|
451
|
+
next if key == :default || (key == :charset && mime_type.include?('charset'))
|
444
452
|
val = val.inspect if val =~ /[";,]/
|
445
|
-
mime_type += "#{mime_type.include?(
|
453
|
+
mime_type += "#{mime_type.include?(';') ? ', ' : ';'}#{key}=#{val}"
|
446
454
|
end
|
447
455
|
end
|
448
456
|
|
449
|
-
@headers[
|
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=
|
462
|
+
def attachment(filename = nil, disposition='attachment')
|
455
463
|
if filename
|
456
464
|
params = "; filename=#{File.basename(filename).inspect}"
|
457
|
-
unless @headers[
|
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[
|
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?(
|
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 ==
|
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[
|
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[
|
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
|
data/lib/roda/version.rb
CHANGED
@@ -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.
|
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-
|
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
|