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 +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
|