ur 0.2.1 → 0.2.2
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/.yardopts +1 -0
- data/CHANGELOG.md +6 -0
- data/README.md +2 -2
- data/lib/ur/content_type.rb +12 -12
- data/lib/ur/middleware.rb +2 -2
- data/lib/ur/version.rb +1 -1
- data/lib/ur/weblink.rb +125 -0
- data/lib/ur.rb +4 -3
- data/ur.gemspec +30 -0
- metadata +11 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a39ab1a17905dc8a0fb40eb22896580951abec8cfe78a11c4a1fd558ca6c6917
|
|
4
|
+
data.tar.gz: 5915b4da516b30c6ee291eb7f5a5bb5a8f974d30da327d5660a9b1a0549068a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e59ca34e85e21e8c659a10e57cffad39d3fa718389bed6ccb6e4327cb33b237569517bb8d5c2eb93272a0c20634966697823a7142f1d861ba58c933dad5342c6
|
|
7
|
+
data.tar.gz: 3ab8ad92c6e70fc3dd87928a200a500e8bd5d27440fd9c2b3e35d46d3778aed80122cefda50875cb0bb6f84dcba75fbc24bdc194d88d4f345439b97f98f7b9b6
|
data/.yardopts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--main README.md --markup=markdown {lib}/**/*.rb
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -61,6 +61,6 @@ end
|
|
|
61
61
|
|
|
62
62
|
## License
|
|
63
63
|
|
|
64
|
-
[<img align="right" src="https://
|
|
64
|
+
[<img align="right" src="https://www.gnu.org/graphics/lgplv3-147x51.png">](https://www.gnu.org/licenses/lgpl-3.0.html)
|
|
65
65
|
|
|
66
|
-
Ur is
|
|
66
|
+
Ur is licensed under the terms of the [GNU Lesser General Public License version 3](https://www.gnu.org/licenses/lgpl-3.0.html).
|
data/lib/ur/content_type.rb
CHANGED
|
@@ -154,21 +154,21 @@ module Ur
|
|
|
154
154
|
# @param other_type
|
|
155
155
|
# @return [Boolean]
|
|
156
156
|
def type?(other_type)
|
|
157
|
-
type
|
|
157
|
+
type ? type.casecmp?(other_type) : false
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
# is the 'subtype' portion of our media type equal (case-insensitive) to the given other_subtype
|
|
161
161
|
# @param other_subtype
|
|
162
162
|
# @return [Boolean]
|
|
163
163
|
def subtype?(other_subtype)
|
|
164
|
-
subtype
|
|
164
|
+
subtype ? subtype.casecmp?(other_subtype) : false
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
# is the 'suffix' portion of our media type equal (case-insensitive) to the given other_suffix
|
|
168
168
|
# @param other_suffix
|
|
169
169
|
# @return [Boolean]
|
|
170
170
|
def suffix?(other_suffix)
|
|
171
|
-
suffix
|
|
171
|
+
suffix ? suffix.casecmp?(other_suffix) : false
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
SOME_TEXT_SUBTYPES = %w(
|
|
@@ -215,55 +215,55 @@ module Ur
|
|
|
215
215
|
# is this an XML content type?
|
|
216
216
|
# @return [Boolean]
|
|
217
217
|
def xml?
|
|
218
|
-
suffix ? suffix.casecmp?('xml'): subtype ? subtype.casecmp?('xml') : false
|
|
218
|
+
suffix ? suffix.casecmp?('xml') : subtype ? subtype.casecmp?('xml') : false
|
|
219
219
|
end
|
|
220
220
|
|
|
221
221
|
# is this a `x-www-form-urlencoded` content type?
|
|
222
222
|
# @return [Boolean]
|
|
223
223
|
def form_urlencoded?
|
|
224
|
-
suffix ? suffix.casecmp?('x-www-form-urlencoded'): subtype ? subtype.casecmp?('x-www-form-urlencoded') : false
|
|
224
|
+
suffix ? suffix.casecmp?('x-www-form-urlencoded') : subtype ? subtype.casecmp?('x-www-form-urlencoded') : false
|
|
225
225
|
end
|
|
226
226
|
|
|
227
227
|
# is the 'type' portion of our media type 'text'
|
|
228
228
|
# @return [Boolean]
|
|
229
229
|
def type_text?
|
|
230
|
-
type
|
|
230
|
+
type ? type.casecmp?('text') : false
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
# is the 'type' portion of our media type 'image'
|
|
234
234
|
# @return [Boolean]
|
|
235
235
|
def type_image?
|
|
236
|
-
type
|
|
236
|
+
type ? type.casecmp?('image') : false
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
# is the 'type' portion of our media type 'audio'
|
|
240
240
|
# @return [Boolean]
|
|
241
241
|
def type_audio?
|
|
242
|
-
type
|
|
242
|
+
type ? type.casecmp?('audio') : false
|
|
243
243
|
end
|
|
244
244
|
|
|
245
245
|
# is the 'type' portion of our media type 'video'
|
|
246
246
|
# @return [Boolean]
|
|
247
247
|
def type_video?
|
|
248
|
-
type
|
|
248
|
+
type ? type.casecmp?('video') : false
|
|
249
249
|
end
|
|
250
250
|
|
|
251
251
|
# is the 'type' portion of our media type 'application'
|
|
252
252
|
# @return [Boolean]
|
|
253
253
|
def type_application?
|
|
254
|
-
type
|
|
254
|
+
type ? type.casecmp?('application') : false
|
|
255
255
|
end
|
|
256
256
|
|
|
257
257
|
# is the 'type' portion of our media type 'message'
|
|
258
258
|
# @return [Boolean]
|
|
259
259
|
def type_message?
|
|
260
|
-
type
|
|
260
|
+
type ? type.casecmp?('message') : false
|
|
261
261
|
end
|
|
262
262
|
|
|
263
263
|
# is the 'type' portion of our media type 'multipart'
|
|
264
264
|
# @return [Boolean]
|
|
265
265
|
def type_multipart?
|
|
266
|
-
type
|
|
266
|
+
type ? type.casecmp?('multipart') : false
|
|
267
267
|
end
|
|
268
268
|
end
|
|
269
269
|
end
|
data/lib/ur/middleware.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Ur
|
|
|
30
30
|
class FaradayMiddleware < ::Faraday::Middleware
|
|
31
31
|
include Ur::Middleware
|
|
32
32
|
def call(request_env)
|
|
33
|
-
ur = Ur.from_faraday_request(request_env,
|
|
33
|
+
ur = Ur.from_faraday_request(request_env, **@options.select { |k, _| [:schemas].include?(k) })
|
|
34
34
|
invoke_callback(:before_request, ur)
|
|
35
35
|
begin_request(ur)
|
|
36
36
|
ur.faraday_on_complete(@app, request_env) do |response_env|
|
|
@@ -43,7 +43,7 @@ module Ur
|
|
|
43
43
|
class RackMiddleware
|
|
44
44
|
include Ur::Middleware
|
|
45
45
|
def call(env)
|
|
46
|
-
ur = Ur.from_rack_request(env,
|
|
46
|
+
ur = Ur.from_rack_request(env, **@options.select { |k, _| [:schemas].include?(k) })
|
|
47
47
|
invoke_callback(:before_request, ur)
|
|
48
48
|
begin_request(ur)
|
|
49
49
|
ur.with_rack_response(@app, env) do
|
data/lib/ur/version.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
UR_VERSION = "0.2.
|
|
1
|
+
UR_VERSION = "0.2.2".freeze
|
data/lib/ur/weblink.rb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ur' unless Object.const_defined?(:Ur)
|
|
4
|
+
|
|
5
|
+
module Ur
|
|
6
|
+
# a RFC5988 Web Link
|
|
7
|
+
#
|
|
8
|
+
# https://tools.ietf.org/html/rfc5988
|
|
9
|
+
class Weblink
|
|
10
|
+
# Weblink::Error, base class for all errors of the Weblink class
|
|
11
|
+
class Error < StandardError
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# error parsing a Weblink
|
|
15
|
+
class ParseError < Error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# error when attempting an operation that requires a context URI which was not provided
|
|
19
|
+
class NoContextError < Error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# parses an array of Web Links from the value an HTTP Link header, as described in
|
|
23
|
+
# https://tools.ietf.org/html/rfc5988#section-5
|
|
24
|
+
#
|
|
25
|
+
# returns an Array of Weblink objects
|
|
26
|
+
def self.parse_link_value(link_value, context_uri=nil)
|
|
27
|
+
links = []
|
|
28
|
+
|
|
29
|
+
return links unless link_value
|
|
30
|
+
|
|
31
|
+
attr_char = /[a-zA-Z0-9!#\$&+\-.^_`|~]/ # defined in https://tools.ietf.org/html/rfc5987#section-3.2.1
|
|
32
|
+
ptoken = %r([a-zA-Z0-9!#\$%&'()*+\-./:<=>?@\[\]^_`{|}~])
|
|
33
|
+
quoted_string = /"([^"]*)"/
|
|
34
|
+
|
|
35
|
+
require 'strscan'
|
|
36
|
+
ss = StringScanner.new(link_value)
|
|
37
|
+
parse_fail = proc do
|
|
38
|
+
raise ParseError, "Unable to parse link value: #{link_value} " +
|
|
39
|
+
"around character #{ss.pos}: #{ss.peek(link_value.length - ss.pos)}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
while !ss.eos?
|
|
43
|
+
# get the target_uri, within some angle brackets
|
|
44
|
+
ss.scan(/\s*<([^>]+)>/) || parse_fail.call
|
|
45
|
+
target_uri = ss[1]
|
|
46
|
+
attributes = {}
|
|
47
|
+
# get the attributes: semicolon, some attr_chars, an optional asterisk, equals, and a quoted
|
|
48
|
+
# string or series of unquoted ptokens
|
|
49
|
+
while ss.scan(/\s*;\s*(#{attr_char.source}+\*?)\s*=\s*(?:#{quoted_string.source}|(#{ptoken.source}+))\s*/)
|
|
50
|
+
attributes[ss[1]] = ss[2] || ss[3]
|
|
51
|
+
end
|
|
52
|
+
links << new(target_uri, attributes, context_uri)
|
|
53
|
+
unless ss.eos?
|
|
54
|
+
# either the string ends or has a comma followed by another link
|
|
55
|
+
ss.scan(/\s*,\s*/) || parse_fail.call
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
links
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize(target_uri, attributes, context_uri=nil)
|
|
62
|
+
@target_uri = to_addressable_uri(target_uri)
|
|
63
|
+
@attributes = attributes
|
|
64
|
+
@context_uri = to_addressable_uri(context_uri)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# the context uri of the link, as an Addressable URI. this URI must be absolute, and the target_uri
|
|
68
|
+
# may be resolved against it. this is most typically the request URI of a request to a service
|
|
69
|
+
attr_reader :context_uri
|
|
70
|
+
|
|
71
|
+
# RFC 5988 calls it IRI, but nobody else does. we'll throw in an alias.
|
|
72
|
+
alias_method :context_iri, :context_uri
|
|
73
|
+
|
|
74
|
+
# returns the target URI as an Addressable::URI
|
|
75
|
+
attr_reader :target_uri
|
|
76
|
+
|
|
77
|
+
# RFC 5988 calls it IRI, but nobody else does. we'll throw in an alias.
|
|
78
|
+
alias_method :target_iri, :target_uri
|
|
79
|
+
|
|
80
|
+
# attempts to make target_uri absolute, using context_uri if available. raises if
|
|
81
|
+
# there is not information available to make an absolute target URI
|
|
82
|
+
def absolute_target_uri
|
|
83
|
+
if target_uri.absolute?
|
|
84
|
+
target_uri
|
|
85
|
+
elsif context_uri
|
|
86
|
+
context_uri + target_uri
|
|
87
|
+
else
|
|
88
|
+
raise NoContextError, "Target URI is relative but no Context URI given - cannot determine absolute target URI"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# link attributes
|
|
93
|
+
attr_reader :attributes
|
|
94
|
+
|
|
95
|
+
# subscript returns an attribute of this Link, if defined, otherwise nil
|
|
96
|
+
def [](attribute_key)
|
|
97
|
+
@attributes[attribute_key]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# link rel attribute
|
|
101
|
+
def rel
|
|
102
|
+
self['rel']
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
alias_method :relation_type, :rel
|
|
106
|
+
|
|
107
|
+
# compares relation types in a case-insensitive manner as mandated in
|
|
108
|
+
# https://tools.ietf.org/html/rfc5988#section-4.1
|
|
109
|
+
def rel?(other_rel)
|
|
110
|
+
rel && other_rel && rel.downcase == other_rel.downcase
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# a string of this weblink, appropriate for adding to a Link header
|
|
114
|
+
def to_s
|
|
115
|
+
"<#{target_uri}>" + attributes.map { |k,v| %Q(; #{k}="#{v}") }.join('')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
# if uri is nil, returns nil; otherwise, tries to return a Addressable::URI
|
|
121
|
+
def to_addressable_uri(uri)
|
|
122
|
+
uri.nil? || uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/ur.rb
CHANGED
|
@@ -19,6 +19,7 @@ module Ur
|
|
|
19
19
|
autoload :FaradayMiddleware, 'ur/middleware'
|
|
20
20
|
autoload :RackMiddleware, 'ur/middleware'
|
|
21
21
|
autoload :Faraday, 'ur/faraday'
|
|
22
|
+
autoload :Weblink, 'ur/weblink'
|
|
22
23
|
|
|
23
24
|
Request = self.properties['request']
|
|
24
25
|
Response = self.properties['response']
|
|
@@ -51,7 +52,7 @@ module Ur
|
|
|
51
52
|
env = request_env
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
new({'bound' => 'inbound'}, options).tap do |ur|
|
|
55
|
+
new({'bound' => 'inbound'}, **options).tap do |ur|
|
|
55
56
|
ur.request['method'] = rack_request.request_method
|
|
56
57
|
|
|
57
58
|
ur.request.addressable_uri = Addressable::URI.new(
|
|
@@ -82,7 +83,7 @@ module Ur
|
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
def from_faraday_request(request_env, **options)
|
|
85
|
-
new({'bound' => 'outbound'}, options).tap do |ur|
|
|
86
|
+
new({'bound' => 'outbound'}, **options).tap do |ur|
|
|
86
87
|
ur.request['method'] = request_env[:method].to_s
|
|
87
88
|
ur.request.uri = request_env[:url].normalize.to_s
|
|
88
89
|
ur.request.headers = request_env[:request_headers]
|
|
@@ -114,7 +115,7 @@ module Ur
|
|
|
114
115
|
[status, response_headers, response_body_proxy]
|
|
115
116
|
end
|
|
116
117
|
|
|
117
|
-
def faraday_on_complete(app, request_env
|
|
118
|
+
def faraday_on_complete(app, request_env)
|
|
118
119
|
app.call(request_env).on_complete do |response_env|
|
|
119
120
|
response.status = response_env[:status]
|
|
120
121
|
response.headers = response_env[:response_headers]
|
data/ur.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "ur/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "ur"
|
|
7
|
+
spec.version = UR_VERSION
|
|
8
|
+
spec.authors = ["Ethan"]
|
|
9
|
+
spec.email = ["ethan.ur@unth.net"]
|
|
10
|
+
|
|
11
|
+
spec.summary = 'ur: unified request representation'
|
|
12
|
+
spec.description = 'ur provides a unified representation of a request and response. it can be interpreted from rack, faraday, or potentially other sources, and provides a consistent interface to access the attributes inherent to the request and additional useful parsers and computation from the request.'
|
|
13
|
+
spec.homepage = "https://github.com/notEthan/ur"
|
|
14
|
+
spec.license = "LGPL-3.0"
|
|
15
|
+
|
|
16
|
+
spec.files = [
|
|
17
|
+
'LICENSE.md',
|
|
18
|
+
'CHANGELOG.md',
|
|
19
|
+
'README.md',
|
|
20
|
+
'.yardopts',
|
|
21
|
+
'resources/ur.schema.yml',
|
|
22
|
+
'ur.gemspec',
|
|
23
|
+
*Dir['lib/**/*'],
|
|
24
|
+
].reject { |f| File.lstat(f).ftype == 'directory' }
|
|
25
|
+
|
|
26
|
+
spec.require_paths = ["lib"]
|
|
27
|
+
|
|
28
|
+
spec.add_dependency "jsi", "~> 0.6"
|
|
29
|
+
spec.add_dependency "addressable", "~> 2.0"
|
|
30
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ur
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ethan
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-01-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jsi
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.6
|
|
19
|
+
version: '0.6'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.6
|
|
26
|
+
version: '0.6'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: addressable
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -48,6 +48,7 @@ executables: []
|
|
|
48
48
|
extensions: []
|
|
49
49
|
extra_rdoc_files: []
|
|
50
50
|
files:
|
|
51
|
+
- ".yardopts"
|
|
51
52
|
- CHANGELOG.md
|
|
52
53
|
- LICENSE.md
|
|
53
54
|
- README.md
|
|
@@ -62,12 +63,14 @@ files:
|
|
|
62
63
|
- lib/ur/response.rb
|
|
63
64
|
- lib/ur/sub_ur.rb
|
|
64
65
|
- lib/ur/version.rb
|
|
66
|
+
- lib/ur/weblink.rb
|
|
65
67
|
- resources/ur.schema.yml
|
|
68
|
+
- ur.gemspec
|
|
66
69
|
homepage: https://github.com/notEthan/ur
|
|
67
70
|
licenses:
|
|
68
71
|
- LGPL-3.0
|
|
69
72
|
metadata: {}
|
|
70
|
-
post_install_message:
|
|
73
|
+
post_install_message:
|
|
71
74
|
rdoc_options: []
|
|
72
75
|
require_paths:
|
|
73
76
|
- lib
|
|
@@ -82,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
82
85
|
- !ruby/object:Gem::Version
|
|
83
86
|
version: '0'
|
|
84
87
|
requirements: []
|
|
85
|
-
rubygems_version: 3.1.
|
|
86
|
-
signing_key:
|
|
88
|
+
rubygems_version: 3.1.6
|
|
89
|
+
signing_key:
|
|
87
90
|
specification_version: 4
|
|
88
91
|
summary: 'ur: unified request representation'
|
|
89
92
|
test_files: []
|