ur 0.2.1 → 0.2.3
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 +9 -0
- data/README.md +2 -2
- data/lib/ur/content_type.rb +13 -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 +28 -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: 834735162f5c696d940e546b1908bca87a2b0c8e93d3f20247b1468404071b56
|
4
|
+
data.tar.gz: 6083f1cfaf96c4857c43e580398c512e00589f8e0ef9b799f365d2719d8c1848
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39f8e38f80a35a4d019bed707745d7835fcdc92e8e162dd90a492ce46c711016cb73789ff57d384ac763f8514a5f5749ccc4f1db75a9d4cad4e96a7512ce8be4
|
7
|
+
data.tar.gz: 4c92cbd6cf2dcb597bd6a265cd7c5a892af3ea504a220e82a7cbcc5e06ef421703ead862e0ba5b6ab88b7633050fe93966befc78c50a76c4f5e23155d65c8a7c
|
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
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'ur' unless Object.const_defined?(:Ur)
|
4
|
+
require 'strscan'
|
4
5
|
|
5
6
|
module Ur
|
6
7
|
# Ur::ContentType represents a Content-Type header field.
|
@@ -154,21 +155,21 @@ module Ur
|
|
154
155
|
# @param other_type
|
155
156
|
# @return [Boolean]
|
156
157
|
def type?(other_type)
|
157
|
-
type
|
158
|
+
type ? type.casecmp?(other_type) : false
|
158
159
|
end
|
159
160
|
|
160
161
|
# is the 'subtype' portion of our media type equal (case-insensitive) to the given other_subtype
|
161
162
|
# @param other_subtype
|
162
163
|
# @return [Boolean]
|
163
164
|
def subtype?(other_subtype)
|
164
|
-
subtype
|
165
|
+
subtype ? subtype.casecmp?(other_subtype) : false
|
165
166
|
end
|
166
167
|
|
167
168
|
# is the 'suffix' portion of our media type equal (case-insensitive) to the given other_suffix
|
168
169
|
# @param other_suffix
|
169
170
|
# @return [Boolean]
|
170
171
|
def suffix?(other_suffix)
|
171
|
-
suffix
|
172
|
+
suffix ? suffix.casecmp?(other_suffix) : false
|
172
173
|
end
|
173
174
|
|
174
175
|
SOME_TEXT_SUBTYPES = %w(
|
@@ -215,55 +216,55 @@ module Ur
|
|
215
216
|
# is this an XML content type?
|
216
217
|
# @return [Boolean]
|
217
218
|
def xml?
|
218
|
-
suffix ? suffix.casecmp?('xml'): subtype ? subtype.casecmp?('xml') : false
|
219
|
+
suffix ? suffix.casecmp?('xml') : subtype ? subtype.casecmp?('xml') : false
|
219
220
|
end
|
220
221
|
|
221
222
|
# is this a `x-www-form-urlencoded` content type?
|
222
223
|
# @return [Boolean]
|
223
224
|
def form_urlencoded?
|
224
|
-
suffix ? suffix.casecmp?('x-www-form-urlencoded'): subtype ? subtype.casecmp?('x-www-form-urlencoded') : false
|
225
|
+
suffix ? suffix.casecmp?('x-www-form-urlencoded') : subtype ? subtype.casecmp?('x-www-form-urlencoded') : false
|
225
226
|
end
|
226
227
|
|
227
228
|
# is the 'type' portion of our media type 'text'
|
228
229
|
# @return [Boolean]
|
229
230
|
def type_text?
|
230
|
-
type
|
231
|
+
type ? type.casecmp?('text') : false
|
231
232
|
end
|
232
233
|
|
233
234
|
# is the 'type' portion of our media type 'image'
|
234
235
|
# @return [Boolean]
|
235
236
|
def type_image?
|
236
|
-
type
|
237
|
+
type ? type.casecmp?('image') : false
|
237
238
|
end
|
238
239
|
|
239
240
|
# is the 'type' portion of our media type 'audio'
|
240
241
|
# @return [Boolean]
|
241
242
|
def type_audio?
|
242
|
-
type
|
243
|
+
type ? type.casecmp?('audio') : false
|
243
244
|
end
|
244
245
|
|
245
246
|
# is the 'type' portion of our media type 'video'
|
246
247
|
# @return [Boolean]
|
247
248
|
def type_video?
|
248
|
-
type
|
249
|
+
type ? type.casecmp?('video') : false
|
249
250
|
end
|
250
251
|
|
251
252
|
# is the 'type' portion of our media type 'application'
|
252
253
|
# @return [Boolean]
|
253
254
|
def type_application?
|
254
|
-
type
|
255
|
+
type ? type.casecmp?('application') : false
|
255
256
|
end
|
256
257
|
|
257
258
|
# is the 'type' portion of our media type 'message'
|
258
259
|
# @return [Boolean]
|
259
260
|
def type_message?
|
260
|
-
type
|
261
|
+
type ? type.casecmp?('message') : false
|
261
262
|
end
|
262
263
|
|
263
264
|
# is the 'type' portion of our media type 'multipart'
|
264
265
|
# @return [Boolean]
|
265
266
|
def type_multipart?
|
266
|
-
type
|
267
|
+
type ? type.casecmp?('multipart') : false
|
267
268
|
end
|
268
269
|
end
|
269
270
|
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.3".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
|
+
require 'strscan'
|
5
|
+
|
6
|
+
module Ur
|
7
|
+
# a RFC5988 Web Link
|
8
|
+
#
|
9
|
+
# https://tools.ietf.org/html/rfc5988
|
10
|
+
class Weblink
|
11
|
+
# Weblink::Error, base class for all errors of the Weblink class
|
12
|
+
class Error < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
# error parsing a Weblink
|
16
|
+
class ParseError < Error
|
17
|
+
end
|
18
|
+
|
19
|
+
# error when attempting an operation that requires a context URI which was not provided
|
20
|
+
class NoContextError < Error
|
21
|
+
end
|
22
|
+
|
23
|
+
# parses an array of Web Links from the value an HTTP Link header, as described in
|
24
|
+
# https://tools.ietf.org/html/rfc5988#section-5
|
25
|
+
#
|
26
|
+
# returns an Array of Weblink objects
|
27
|
+
def self.parse_link_value(link_value, context_uri=nil)
|
28
|
+
links = []
|
29
|
+
|
30
|
+
return links unless link_value
|
31
|
+
|
32
|
+
attr_char = /[a-zA-Z0-9!#\$&+\-.^_`|~]/ # defined in https://tools.ietf.org/html/rfc5987#section-3.2.1
|
33
|
+
ptoken = %r([a-zA-Z0-9!#\$%&'()*+\-./:<=>?@\[\]^_`{|}~])
|
34
|
+
quoted_string = /"([^"]*)"/
|
35
|
+
|
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,28 @@
|
|
1
|
+
require_relative "lib/ur/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "ur"
|
5
|
+
spec.version = UR_VERSION
|
6
|
+
spec.authors = ["Ethan"]
|
7
|
+
spec.email = ["ethan.ur@unth.net"]
|
8
|
+
|
9
|
+
spec.summary = 'ur: unified request representation'
|
10
|
+
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.'
|
11
|
+
spec.homepage = "https://github.com/notEthan/ur"
|
12
|
+
spec.license = "LGPL-3.0"
|
13
|
+
|
14
|
+
spec.files = [
|
15
|
+
'LICENSE.md',
|
16
|
+
'CHANGELOG.md',
|
17
|
+
'README.md',
|
18
|
+
'.yardopts',
|
19
|
+
'resources/ur.schema.yml',
|
20
|
+
'ur.gemspec',
|
21
|
+
*Dir['lib/**/*'],
|
22
|
+
].reject { |f| File.lstat(f).ftype == 'directory' }
|
23
|
+
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "jsi", "~> 0.7"
|
27
|
+
spec.add_dependency "addressable", "~> 2.0"
|
28
|
+
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.3
|
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-07-29 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.
|
19
|
+
version: '0.7'
|
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.
|
26
|
+
version: '0.7'
|
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.
|
86
|
-
signing_key:
|
88
|
+
rubygems_version: 3.4.10
|
89
|
+
signing_key:
|
87
90
|
specification_version: 4
|
88
91
|
summary: 'ur: unified request representation'
|
89
92
|
test_files: []
|