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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1161e5f2e2219053fc4c54e541feb0d4c6efa1c1af930365e526a88bc0973e1b
4
- data.tar.gz: b42e166ae62f0a651d5425147e64b1d200c09862869188b8f95e52454f7b8e97
3
+ metadata.gz: 834735162f5c696d940e546b1908bca87a2b0c8e93d3f20247b1468404071b56
4
+ data.tar.gz: 6083f1cfaf96c4857c43e580398c512e00589f8e0ef9b799f365d2719d8c1848
5
5
  SHA512:
6
- metadata.gz: 17187f65ff16035e72fc7d7e0b9468bd970d337b1f8173fbc2dde15f420ad406aa33ecb440b4b65e0dffb921279660fdedb48b4bb320ce3e4e9a411eec3a21da
7
- data.tar.gz: 8c8d0f0c86af8a97c18dc794e69b84634421607a7e6715120b90fd3d9f74bdd1848a5c422598dbc06fdb0ea7ce4ac474e76b761ec7e2243fdb87b2aa812af292
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
@@ -1,3 +1,12 @@
1
+ # v0.2.3
2
+
3
+ - JSI v0.7.0
4
+
5
+ # v0.2.2
6
+
7
+ - Ur::Weblink
8
+ - Ruby 3
9
+
1
10
  # v0.2.1
2
11
 
3
12
  - JSI v0.6.0
data/README.md CHANGED
@@ -61,6 +61,6 @@ end
61
61
 
62
62
  ## License
63
63
 
64
- [<img align="right" src="https://github.com/notEthan/ur/raw/master/resources/icons/LGPL-3.0.png">](https://www.gnu.org/licenses/lgpl-3.0.html)
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 Open Source Software licensed under the terms of the [GNU Lesser General Public License version 3](https://www.gnu.org/licenses/lgpl-3.0.html).
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).
@@ -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 && type.casecmp?(other_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 && subtype.casecmp?(other_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 && suffix.casecmp?(other_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 && type.casecmp?('text')
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 && type.casecmp?('image')
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 && type.casecmp?('audio')
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 && type.casecmp?('video')
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 && type.casecmp?('application')
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 && type.casecmp?('message')
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 && type.casecmp?('multipart')
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, @options.select { |k, _| [:schemas].include?(k) })
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, @options.select { |k, _| [:schemas].include?(k) })
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".freeze
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, &block)
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.1
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: 2022-01-25 00:00:00.000000000 Z
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.6.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.6.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.1.2
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: []