webmachine 1.2.2 → 1.6.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 +7 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +20 -15
- data/README.md +89 -91
- data/RELEASING.md +21 -0
- data/Rakefile +5 -21
- data/documentation/adapters.md +41 -0
- data/documentation/authentication-and-authorization.md +37 -0
- data/documentation/configurator.md +19 -0
- data/documentation/error-handling.md +86 -0
- data/documentation/examples.md +224 -0
- data/documentation/how-it-works.md +76 -0
- data/documentation/routes.md +112 -0
- data/documentation/validation.md +159 -0
- data/documentation/versioning-apis.md +74 -0
- data/documentation/visual-debugger.md +38 -0
- data/examples/application.rb +2 -2
- data/examples/debugger.rb +1 -1
- data/lib/webmachine.rb +3 -1
- data/lib/webmachine/adapter.rb +7 -13
- data/lib/webmachine/adapters.rb +1 -2
- data/lib/webmachine/adapters/httpkit.rb +74 -0
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
- data/lib/webmachine/adapters/rack.rb +70 -25
- data/lib/webmachine/adapters/rack_mapped.rb +42 -0
- data/lib/webmachine/adapters/reel.rb +22 -23
- data/lib/webmachine/adapters/webrick.rb +16 -16
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +3 -4
- data/lib/webmachine/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +75 -0
- data/lib/webmachine/decision/conneg.rb +12 -10
- data/lib/webmachine/decision/flow.rb +42 -32
- data/lib/webmachine/decision/fsm.rb +14 -21
- data/lib/webmachine/decision/helpers.rb +10 -38
- data/lib/webmachine/dispatcher.rb +13 -10
- data/lib/webmachine/dispatcher/route.rb +45 -9
- data/lib/webmachine/errors.rb +9 -3
- data/lib/webmachine/events.rb +2 -2
- data/lib/webmachine/header_negotiation.rb +25 -0
- data/lib/webmachine/headers.rb +8 -3
- data/lib/webmachine/locale/en.yml +7 -5
- data/lib/webmachine/media_type.rb +10 -8
- data/lib/webmachine/request.rb +67 -26
- data/lib/webmachine/rescueable_exception.rb +62 -0
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +11 -9
- data/lib/webmachine/response.rb +3 -5
- data/lib/webmachine/spec/IO_response.body +1 -0
- data/lib/webmachine/spec/adapter_lint.rb +83 -37
- data/lib/webmachine/spec/test_resource.rb +15 -4
- data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
- data/lib/webmachine/streaming/io_encoder.rb +7 -1
- data/lib/webmachine/trace.rb +1 -0
- data/lib/webmachine/trace/fsm.rb +20 -10
- data/lib/webmachine/trace/resource_proxy.rb +2 -0
- data/lib/webmachine/translation.rb +2 -1
- data/lib/webmachine/version.rb +3 -3
- data/memory_test.rb +37 -0
- data/spec/spec_helper.rb +17 -9
- data/spec/webmachine/adapter_spec.rb +14 -15
- data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
- data/spec/webmachine/adapters/rack_mapped_spec.rb +71 -0
- data/spec/webmachine/adapters/rack_spec.rb +32 -6
- data/spec/webmachine/adapters/reel_spec.rb +16 -12
- data/spec/webmachine/adapters/webrick_spec.rb +2 -2
- data/spec/webmachine/application_spec.rb +18 -17
- data/spec/webmachine/chunked_body_spec.rb +3 -3
- data/spec/webmachine/configuration_spec.rb +5 -5
- data/spec/webmachine/cookie_spec.rb +13 -13
- data/spec/webmachine/decision/conneg_spec.rb +49 -43
- data/spec/webmachine/decision/falsey_spec.rb +4 -4
- data/spec/webmachine/decision/flow_spec.rb +195 -145
- data/spec/webmachine/decision/fsm_spec.rb +81 -19
- data/spec/webmachine/decision/helpers_spec.rb +20 -20
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +22 -0
- data/spec/webmachine/dispatcher/route_spec.rb +114 -32
- data/spec/webmachine/dispatcher_spec.rb +49 -24
- data/spec/webmachine/errors_spec.rb +1 -1
- data/spec/webmachine/etags_spec.rb +19 -19
- data/spec/webmachine/events_spec.rb +6 -6
- data/spec/webmachine/headers_spec.rb +14 -14
- data/spec/webmachine/media_type_spec.rb +36 -36
- data/spec/webmachine/request_spec.rb +70 -39
- data/spec/webmachine/rescueable_exception_spec.rb +15 -0
- data/spec/webmachine/resource/authentication_spec.rb +6 -6
- data/spec/webmachine/response_spec.rb +18 -12
- data/spec/webmachine/trace/fsm_spec.rb +8 -8
- data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
- data/spec/webmachine/trace/trace_store_spec.rb +5 -5
- data/spec/webmachine/trace_spec.rb +3 -3
- data/webmachine.gemspec +2 -6
- metadata +78 -228
- data/lib/webmachine/adapters/hatetepe.rb +0 -108
- data/lib/webmachine/adapters/mongrel.rb +0 -127
- data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
- data/lib/webmachine/fiber18.rb +0 -88
- data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
- data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
require 'stringio'
|
|
1
|
+
require 'stringio'
|
|
2
2
|
require 'time'
|
|
3
3
|
require 'webmachine/streaming'
|
|
4
4
|
require 'webmachine/media_type'
|
|
5
5
|
require 'webmachine/quoted_string'
|
|
6
6
|
require 'webmachine/etags'
|
|
7
|
+
require 'webmachine/header_negotiation'
|
|
8
|
+
require 'webmachine/constants'
|
|
7
9
|
|
|
8
10
|
module Webmachine
|
|
9
11
|
module Decision
|
|
@@ -11,6 +13,7 @@ module Webmachine
|
|
|
11
13
|
module Helpers
|
|
12
14
|
include QuotedString
|
|
13
15
|
include Streaming
|
|
16
|
+
include HeaderNegotiation
|
|
14
17
|
|
|
15
18
|
# Determines if the response has a body/entity set.
|
|
16
19
|
def has_response_body?
|
|
@@ -26,8 +29,8 @@ module Webmachine
|
|
|
26
29
|
# Encodes the body in the selected charset and encoding.
|
|
27
30
|
def encode_body
|
|
28
31
|
body = response.body
|
|
29
|
-
chosen_charset = metadata[
|
|
30
|
-
chosen_encoding = metadata[
|
|
32
|
+
chosen_charset = metadata[CHARSET]
|
|
33
|
+
chosen_encoding = metadata[CONTENT_ENCODING]
|
|
31
34
|
charsetter = resource.charsets_provided && resource.charsets_provided.find {|c,_| c == chosen_charset }.last || :charset_nop
|
|
32
35
|
encoder = resource.encodings_provided[chosen_encoding]
|
|
33
36
|
response.body = case body
|
|
@@ -47,10 +50,10 @@ module Webmachine
|
|
|
47
50
|
end
|
|
48
51
|
end
|
|
49
52
|
if body_is_fixed_length?
|
|
50
|
-
|
|
53
|
+
ensure_content_length(response)
|
|
51
54
|
else
|
|
52
|
-
response.headers.delete
|
|
53
|
-
response.headers[
|
|
55
|
+
response.headers.delete CONTENT_LENGTH
|
|
56
|
+
response.headers[TRANSFER_ENCODING] = 'chunked'
|
|
54
57
|
end
|
|
55
58
|
end
|
|
56
59
|
|
|
@@ -88,42 +91,11 @@ module Webmachine
|
|
|
88
91
|
end
|
|
89
92
|
end
|
|
90
93
|
|
|
91
|
-
# Ensures that responses have an appropriate Content-Length
|
|
92
|
-
# header
|
|
93
|
-
def ensure_content_length
|
|
94
|
-
case
|
|
95
|
-
when response.headers['Transfer-Encoding']
|
|
96
|
-
return
|
|
97
|
-
when [204, 205, 304].include?(response.code)
|
|
98
|
-
response.headers.delete 'Content-Length'
|
|
99
|
-
when has_response_body?
|
|
100
|
-
set_content_length
|
|
101
|
-
else
|
|
102
|
-
response.headers['Content-Length'] = '0'
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Ensures that responses have an appropriate Date header
|
|
107
|
-
def ensure_date_header
|
|
108
|
-
if (200..499).include?(response.code)
|
|
109
|
-
response.headers['Date'] ||= Time.now.httpdate
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Sets the Content-Length header on the response
|
|
114
|
-
def set_content_length
|
|
115
|
-
if response.body.respond_to?(:bytesize)
|
|
116
|
-
response.headers['Content-Length'] = response.body.bytesize.to_s
|
|
117
|
-
else
|
|
118
|
-
response.headers['Content-Length'] = response.body.length.to_s
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
94
|
# Determines whether the response is of a fixed lenghth, i.e. it
|
|
123
95
|
# is a String or IO with known size.
|
|
124
96
|
def body_is_fixed_length?
|
|
125
97
|
response.body.respond_to?(:bytesize) &&
|
|
126
|
-
|
|
98
|
+
Integer === response.body.bytesize
|
|
127
99
|
end
|
|
128
100
|
end # module Helpers
|
|
129
101
|
end # module Decision
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
require 'forwardable'
|
|
1
|
+
require 'forwardable'
|
|
2
2
|
require 'webmachine/decision'
|
|
3
3
|
require 'webmachine/dispatcher/route'
|
|
4
|
-
require 'webmachine/dispatcher/not_found_resource'
|
|
5
4
|
|
|
6
5
|
module Webmachine
|
|
7
6
|
# Handles dispatching incoming requests to the proper registered
|
|
8
7
|
# resources and initializing the decision logic.
|
|
9
8
|
class Dispatcher
|
|
9
|
+
WM_DISPATCH = 'wm.dispatch'.freeze
|
|
10
|
+
|
|
10
11
|
# @return [Array<Route>] the list of routes that will be
|
|
11
12
|
# dispatched to
|
|
12
13
|
# @see #add_route
|
|
@@ -40,12 +41,16 @@ module Webmachine
|
|
|
40
41
|
# @param [Request] request the request object
|
|
41
42
|
# @param [Response] response the response object
|
|
42
43
|
def dispatch(request, response)
|
|
43
|
-
resource = find_resource(request, response)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
if resource = find_resource(request, response)
|
|
45
|
+
Webmachine::Events.instrument(WM_DISPATCH) do |payload|
|
|
46
|
+
Webmachine::Decision::FSM.new(resource, request, response).run
|
|
47
|
+
|
|
48
|
+
payload[:resource] = resource.class.name
|
|
49
|
+
payload[:request] = request.dup
|
|
50
|
+
payload[:code] = response.code
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
Webmachine.render_error(404, request, response)
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
@@ -61,8 +66,6 @@ module Webmachine
|
|
|
61
66
|
def find_resource(request, response)
|
|
62
67
|
if route = find_route(request)
|
|
63
68
|
prepare_resource(route, request, response)
|
|
64
|
-
else
|
|
65
|
-
NotFoundResource.new(request, response)
|
|
66
69
|
end
|
|
67
70
|
end
|
|
68
71
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
require 'webmachine/resource'
|
|
1
|
+
require 'webmachine/resource'
|
|
2
2
|
require 'webmachine/translation'
|
|
3
|
+
require 'webmachine/constants'
|
|
3
4
|
|
|
4
5
|
module Webmachine
|
|
5
6
|
class Dispatcher
|
|
6
7
|
# Pairs URIs with {Resource} classes in the {Dispatcher}. To
|
|
7
8
|
# create routes, use {Dispatcher#add_route}.
|
|
8
9
|
class Route
|
|
9
|
-
include
|
|
10
|
+
include Translation
|
|
10
11
|
|
|
11
12
|
# @return [Class] the resource this route will dispatch to, a
|
|
12
13
|
# subclass of {Resource}
|
|
@@ -22,13 +23,16 @@ module Webmachine
|
|
|
22
23
|
|
|
23
24
|
# When used in a path specification, will match all remaining
|
|
24
25
|
# segments
|
|
25
|
-
MATCH_ALL =
|
|
26
|
+
MATCH_ALL = :*
|
|
27
|
+
|
|
28
|
+
# String version of MATCH_ALL, deprecated. Use the symbol instead.
|
|
29
|
+
MATCH_ALL_STR = '*'.freeze
|
|
26
30
|
|
|
27
31
|
# Creates a new Route that will associate a pattern to a
|
|
28
32
|
# {Resource}.
|
|
29
33
|
#
|
|
30
34
|
# @example Standard route
|
|
31
|
-
# Route.new([
|
|
35
|
+
# Route.new([:*], MyResource)
|
|
32
36
|
#
|
|
33
37
|
# @example Guarded route
|
|
34
38
|
# Route.new ["/notes"],
|
|
@@ -65,6 +69,8 @@ module Webmachine
|
|
|
65
69
|
guards = args
|
|
66
70
|
guards << Proc.new if block_given?
|
|
67
71
|
|
|
72
|
+
warn t('match_all_symbol') if path_spec.include? MATCH_ALL_STR
|
|
73
|
+
|
|
68
74
|
@path_spec = path_spec
|
|
69
75
|
@guards = guards
|
|
70
76
|
@resource = resource
|
|
@@ -77,7 +83,7 @@ module Webmachine
|
|
|
77
83
|
# should be dispatched to the {#resource}.
|
|
78
84
|
# @param [Reqeust] request the request object
|
|
79
85
|
def match?(request)
|
|
80
|
-
tokens = request.
|
|
86
|
+
tokens = request.routing_tokens
|
|
81
87
|
bind(tokens, {}) && guards.all? { |guard| guard.call(request) }
|
|
82
88
|
end
|
|
83
89
|
|
|
@@ -85,9 +91,9 @@ module Webmachine
|
|
|
85
91
|
# route, including path bindings.
|
|
86
92
|
# @param [Request] request the request object
|
|
87
93
|
def apply(request)
|
|
88
|
-
request.disp_path = request.
|
|
94
|
+
request.disp_path = request.routing_tokens.join(SLASH)
|
|
89
95
|
request.path_info = @bindings.dup
|
|
90
|
-
tokens = request.
|
|
96
|
+
tokens = request.routing_tokens
|
|
91
97
|
depth, trailing = bind(tokens, request.path_info)
|
|
92
98
|
request.path_tokens = trailing || []
|
|
93
99
|
end
|
|
@@ -97,7 +103,7 @@ module Webmachine
|
|
|
97
103
|
# accumulating variable bindings.
|
|
98
104
|
# @param [Array<String>] tokens the list of path segments
|
|
99
105
|
# @param [Hash] bindings where path bindings will be stored
|
|
100
|
-
# @return [
|
|
106
|
+
# @return [Integer, Array<Integer, Array>, false] either the depth
|
|
101
107
|
# that the path matched at, the depth and tokens matched by
|
|
102
108
|
# {MATCH_ALL}, or false if it didn't match.
|
|
103
109
|
def bind(tokens, bindings)
|
|
@@ -107,12 +113,28 @@ module Webmachine
|
|
|
107
113
|
case
|
|
108
114
|
when spec.empty? && tokens.empty?
|
|
109
115
|
return depth
|
|
116
|
+
when spec == [MATCH_ALL_STR]
|
|
117
|
+
return [depth, tokens]
|
|
110
118
|
when spec == [MATCH_ALL]
|
|
111
119
|
return [depth, tokens]
|
|
112
120
|
when tokens.empty?
|
|
113
121
|
return false
|
|
122
|
+
when Regexp === spec.first
|
|
123
|
+
matches = spec.first.match Route.rfc3986_percent_decode(tokens.first)
|
|
124
|
+
if matches
|
|
125
|
+
if spec.first.named_captures.empty?
|
|
126
|
+
bindings[:captures] = (bindings[:captures] || []) + matches.captures
|
|
127
|
+
else
|
|
128
|
+
spec.first.named_captures.reduce(bindings) do |bindings, (name, idxs)|
|
|
129
|
+
bindings[name.to_sym] = matches.captures[idxs.first-1]
|
|
130
|
+
bindings
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
return false
|
|
135
|
+
end
|
|
114
136
|
when Symbol === spec.first
|
|
115
|
-
bindings[spec.first] = tokens.first
|
|
137
|
+
bindings[spec.first] = Route.rfc3986_percent_decode(tokens.first)
|
|
116
138
|
when spec.first == tokens.first
|
|
117
139
|
else
|
|
118
140
|
return false
|
|
@@ -123,6 +145,20 @@ module Webmachine
|
|
|
123
145
|
end
|
|
124
146
|
end
|
|
125
147
|
|
|
148
|
+
# Decode a string using the scheme described in RFC 3986 2.1. Percent-Encoding (https://www.ietf.org/rfc/rfc3986.txt)
|
|
149
|
+
def self.rfc3986_percent_decode(value)
|
|
150
|
+
s = StringScanner.new(value)
|
|
151
|
+
result = ''
|
|
152
|
+
until s.eos?
|
|
153
|
+
encoded_val = s.scan(/%([0-9a-fA-F]){2}/)
|
|
154
|
+
result << if encoded_val.nil?
|
|
155
|
+
s.getch
|
|
156
|
+
else
|
|
157
|
+
[encoded_val[1..-1]].pack('H*')
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
result
|
|
161
|
+
end
|
|
126
162
|
end # class Route
|
|
127
163
|
end # module Dispatcher
|
|
128
164
|
end # module Webmachine
|
data/lib/webmachine/errors.rb
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
require 'webmachine/header_negotiation'
|
|
1
2
|
require 'webmachine/translation'
|
|
3
|
+
require 'webmachine/constants'
|
|
2
4
|
require 'webmachine/version'
|
|
3
5
|
|
|
4
6
|
module Webmachine
|
|
7
|
+
extend HeaderNegotiation
|
|
5
8
|
extend Translation
|
|
6
9
|
|
|
7
10
|
# Renders a standard error message body for the response. The
|
|
8
11
|
# standard messages are defined in localization files.
|
|
9
|
-
# @param [
|
|
12
|
+
# @param [Integer] code the response status code
|
|
10
13
|
# @param [Request] req the request object
|
|
11
14
|
# @param [Response] req the response object
|
|
12
15
|
# @param [Hash] options keys to override the defaults when rendering
|
|
@@ -21,10 +24,13 @@ module Webmachine
|
|
|
21
24
|
{:title => title,
|
|
22
25
|
:message => message,
|
|
23
26
|
:version => Webmachine::SERVER_STRING}.merge(options))
|
|
24
|
-
res.headers[
|
|
27
|
+
res.headers[CONTENT_TYPE] = TEXT_HTML
|
|
25
28
|
end
|
|
29
|
+
ensure_content_length(res)
|
|
30
|
+
ensure_date_header(res)
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
|
|
28
34
|
# Superclass of all errors generated by Webmachine.
|
|
29
35
|
class Error < ::StandardError; end
|
|
30
36
|
|
|
@@ -33,6 +39,6 @@ module Webmachine
|
|
|
33
39
|
|
|
34
40
|
# Raised when the client has submitted an invalid request, e.g. in
|
|
35
41
|
# the case where a request header is improperly formed. Raising this
|
|
36
|
-
#
|
|
42
|
+
# error will result in a 400 response.
|
|
37
43
|
class MalformedRequest < Error; end
|
|
38
44
|
end # module Webmachine
|
data/lib/webmachine/events.rb
CHANGED
|
@@ -55,9 +55,9 @@ module Webmachine
|
|
|
55
55
|
# and publish it. Notice that events get sent even if an error occurs
|
|
56
56
|
# in the passed-in block.
|
|
57
57
|
#
|
|
58
|
-
# If an
|
|
58
|
+
# If an error happens during an instrumentation the payload will
|
|
59
59
|
# have a key `:exception` with an array of two elements as value:
|
|
60
|
-
# a string with the name of the
|
|
60
|
+
# a string with the name of the error class, and the error
|
|
61
61
|
# message. (when using the default
|
|
62
62
|
# [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications)
|
|
63
63
|
# backend)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'webmachine/constants'
|
|
2
|
+
|
|
3
|
+
module Webmachine
|
|
4
|
+
module HeaderNegotiation
|
|
5
|
+
def ensure_date_header(res)
|
|
6
|
+
if (200..499).include?(res.code)
|
|
7
|
+
res.headers[DATE] ||= Time.now.httpdate
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def ensure_content_length(res)
|
|
12
|
+
body = res.body
|
|
13
|
+
case
|
|
14
|
+
when res.headers[TRANSFER_ENCODING]
|
|
15
|
+
return
|
|
16
|
+
when [204, 205, 304].include?(res.code)
|
|
17
|
+
res.headers.delete CONTENT_LENGTH
|
|
18
|
+
when body != nil
|
|
19
|
+
res.headers[CONTENT_LENGTH] = body.respond_to?(:bytesize) ? body.bytesize.to_s : body.length.to_s
|
|
20
|
+
else
|
|
21
|
+
res.headers[CONTENT_LENGTH] = '0'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/webmachine/headers.rb
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
require 'webmachine/constants'
|
|
2
|
+
|
|
1
3
|
module Webmachine
|
|
2
4
|
# Case-insensitive Hash of Request headers
|
|
3
5
|
class Headers < ::Hash
|
|
6
|
+
CGI_HTTP_MATCH = /^HTTP_(\w+)$/.freeze
|
|
7
|
+
CONTENT_TYPE_LENGTH_MATCH = /^(CONTENT_(?:TYPE|LENGTH))$/.freeze
|
|
8
|
+
|
|
4
9
|
# Convert CGI-style Hash into Request headers
|
|
5
10
|
# @param [Hash] env a hash of CGI-style env/headers
|
|
6
11
|
# @return [Webmachine::Headers]
|
|
7
12
|
def self.from_cgi(env)
|
|
8
13
|
env.inject(new) do |h,(k,v)|
|
|
9
|
-
if k =~
|
|
10
|
-
h[$1.tr(
|
|
14
|
+
if k =~ CGI_HTTP_MATCH || k =~ CONTENT_TYPE_LENGTH_MATCH
|
|
15
|
+
h[$1.tr(UNDERSCORE, DASH)] = v
|
|
11
16
|
end
|
|
12
17
|
h
|
|
13
18
|
end
|
|
@@ -43,7 +48,7 @@ module Webmachine
|
|
|
43
48
|
|
|
44
49
|
# Returns the value for the given key. If the key can't be found,
|
|
45
50
|
# there are several options:
|
|
46
|
-
# With no other arguments, it will raise a KeyError
|
|
51
|
+
# With no other arguments, it will raise a KeyError error;
|
|
47
52
|
# if default is given, then that will be returned;
|
|
48
53
|
# if the optional code block is specified, then that will be run and its
|
|
49
54
|
# result returned.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
en:
|
|
2
2
|
webmachine:
|
|
3
3
|
errors:
|
|
4
|
-
standard_body: "<!DOCTYPE html><html
|
|
5
|
-
<head><title>%{title}</title></head
|
|
6
|
-
<body><h1>%{title}</h1
|
|
7
|
-
<
|
|
4
|
+
standard_body: "<!DOCTYPE html><html>\n
|
|
5
|
+
<head><title>%{title}</title></head>\n
|
|
6
|
+
<body><h1>%{title}</h1>\n
|
|
7
|
+
<p>%{message}</p>\n
|
|
8
|
+
<address>%{version} server</address></body></html>\n"
|
|
8
9
|
"400":
|
|
9
10
|
title: 400 Malformed Request
|
|
10
11
|
message: The request was malformed and could not be processed.
|
|
@@ -13,7 +14,7 @@ en:
|
|
|
13
14
|
message: The requested document was not found on this server.
|
|
14
15
|
"500":
|
|
15
16
|
title: 500 Internal Server Error
|
|
16
|
-
message: "The server encountered an error while processing this request: <pre
|
|
17
|
+
message: "The server encountered an error while processing this request: <pre>\n%{error}</pre>"
|
|
17
18
|
"501":
|
|
18
19
|
title: 501 Not Implemented
|
|
19
20
|
message: "The server does not support the %{method} method."
|
|
@@ -26,3 +27,4 @@ en:
|
|
|
26
27
|
invalid_media_type: "Invalid media type: %{type}"
|
|
27
28
|
not_resource_class: "%{class} is not a subclass of Webmachine::Resource"
|
|
28
29
|
process_post_invalid: "process_post returned %{result}"
|
|
30
|
+
match_all_symbol: '"*" as a path segment is deprecated and will be removed in a future release. Please use :*'
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
require 'webmachine/translation'
|
|
1
|
+
require 'webmachine/translation'
|
|
2
|
+
require 'webmachine/constants'
|
|
3
|
+
require 'webmachine/dispatcher/route'
|
|
2
4
|
|
|
3
5
|
module Webmachine
|
|
4
6
|
# Encapsulates a MIME media type, with logic for matching types.
|
|
5
7
|
class MediaType
|
|
6
8
|
extend Translation
|
|
7
9
|
# Matches valid media types
|
|
8
|
-
MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s
|
|
10
|
+
MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s*$/.freeze
|
|
9
11
|
|
|
10
12
|
# Matches sub-type parameters
|
|
11
|
-
PARAMS_REGEX = /;\s*([^=]+)(=([^;=\s]*))
|
|
13
|
+
PARAMS_REGEX = /;\s*([^=]+)(=([^;=\s]*))?/.freeze
|
|
12
14
|
|
|
13
15
|
# Creates a new MediaType by parsing an alternate representation.
|
|
14
16
|
# @param [MediaType, String, Array<String,Hash>] obj the raw type
|
|
@@ -48,7 +50,7 @@ module Webmachine
|
|
|
48
50
|
# Detects whether the {MediaType} represents an open wildcard
|
|
49
51
|
# type, that is, "*/*" without any {#params}.
|
|
50
52
|
def matches_all?
|
|
51
|
-
@type ==
|
|
53
|
+
@type == MATCHES_ALL && @params.empty?
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
# @return [true,false] Are these two types strictly equal?
|
|
@@ -97,12 +99,12 @@ module Webmachine
|
|
|
97
99
|
|
|
98
100
|
# @return [String] The major type, e.g. "application", "text", "image"
|
|
99
101
|
def major
|
|
100
|
-
type.split(
|
|
102
|
+
type.split(SLASH).first
|
|
101
103
|
end
|
|
102
104
|
|
|
103
105
|
# @return [String] the minor or sub-type, e.g. "json", "html", "jpeg"
|
|
104
106
|
def minor
|
|
105
|
-
type.split(
|
|
107
|
+
type.split(SLASH).last
|
|
106
108
|
end
|
|
107
109
|
|
|
108
110
|
# @param [MediaType] other the other type
|
|
@@ -110,10 +112,10 @@ module Webmachine
|
|
|
110
112
|
# ignoring params and taking into account wildcards
|
|
111
113
|
def type_matches?(other)
|
|
112
114
|
other = self.class.parse(other)
|
|
113
|
-
if [
|
|
115
|
+
if [Dispatcher::Route::MATCH_ALL_STR, MATCHES_ALL, type].include?(other.type)
|
|
114
116
|
true
|
|
115
117
|
else
|
|
116
|
-
other.major == major && other.minor ==
|
|
118
|
+
other.major == major && other.minor == Dispatcher::Route::MATCH_ALL_STR
|
|
117
119
|
end
|
|
118
120
|
end
|
|
119
121
|
end # class MediaType
|