ur 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.simplecov +1 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile.rb +10 -0
- data/lib/ur.rb +150 -0
- data/lib/ur/middleware.rb +42 -0
- data/lib/ur/processing.rb +29 -0
- data/lib/ur/request.rb +14 -0
- data/lib/ur/request_and_response.rb +23 -0
- data/lib/ur/response.rb +7 -0
- data/lib/ur/version.rb +1 -0
- data/test/test_helper.rb +22 -0
- data/test/ur_faraday_test.rb +79 -0
- data/test/ur_processing_test.rb +11 -0
- data/test/ur_rack_test.rb +29 -0
- data/test/ur_test.rb +105 -0
- data/ur.gemspec +43 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '09cd35887d4f337c01a55526c85dec4d0473ed24532d920960890cccc44f3f52'
|
4
|
+
data.tar.gz: e6cb70fe2d62dd42864c6fd6b2b6cf7c9ebf3f0c73744e0fa8a952618e1d8b1e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e1b5884244f923e22e6570d3e87ce7b3d70771987845bff06da886bb86ed9d29623cbdbf6c55ed943b53529e1506d53f5b995704de177a279c566e8770986f0
|
7
|
+
data.tar.gz: 040554fff56b914de9343fb4abd7e5175557224c34632385efaa5794125587f1ea4ae31f5cf8c9b761d20d8b881a320abcee4ebd49cd2614f25680dcce534d09
|
data/.simplecov
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
SimpleCov.start
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--main README.md --markup=markdown {lib}/**/*.rb
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Ethan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Ur ᚒ
|
2
|
+
|
3
|
+
Ur: Unified Request/Response Representation in Ruby
|
4
|
+
|
5
|
+
## Usage with middleware
|
6
|
+
|
7
|
+
Rack middleware:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class MyRackMiddleware
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
# do things before the request
|
17
|
+
|
18
|
+
ur = Ur.from_rack_request(env)
|
19
|
+
|
20
|
+
# set additional properties of the ur, for example:
|
21
|
+
ur.logger = my_logger
|
22
|
+
|
23
|
+
rack_response = ur.with_rack_response(@app, env) do
|
24
|
+
# do things after the response
|
25
|
+
end
|
26
|
+
rack_response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Faraday middleware:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class MyFaradayMiddleware < ::Faraday::Middleware
|
35
|
+
def call(request_env)
|
36
|
+
# do things before the request
|
37
|
+
|
38
|
+
ur = Ur.from_faraday_request(request_env)
|
39
|
+
|
40
|
+
# set additional properties of the ur, for example:
|
41
|
+
ur.logger = my_logger
|
42
|
+
|
43
|
+
ur.faraday_on_complete(@app, request_env) do |response_env|
|
44
|
+
# do things after the response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile.rb
ADDED
data/lib/ur.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require "ur/version"
|
2
|
+
|
3
|
+
require 'jsi'
|
4
|
+
require 'time'
|
5
|
+
require 'addressable/uri'
|
6
|
+
|
7
|
+
Ur = JSI.class_for_schema({
|
8
|
+
id: 'https://schemas.ur.unth.net/ur',
|
9
|
+
type: 'object',
|
10
|
+
properties: {
|
11
|
+
bound: {
|
12
|
+
type: 'string',
|
13
|
+
description: %q([rfc2616] Inbound and outbound refer to the request and response paths for messages: "inbound" means "traveling toward the origin server", and "outbound" means "traveling toward the user agent"),
|
14
|
+
enum: ['inbound', 'outbound'],
|
15
|
+
},
|
16
|
+
request: {
|
17
|
+
type: 'object',
|
18
|
+
properties: {
|
19
|
+
method: {type: 'string', description: 'HTTP ', example: 'POST'},
|
20
|
+
uri: {type: 'string', example: 'https://example.com/foo?bar=baz'},
|
21
|
+
headers: {type: 'object'},
|
22
|
+
body: {type: 'string'},
|
23
|
+
},
|
24
|
+
},
|
25
|
+
response: {
|
26
|
+
type: 'object',
|
27
|
+
properties: {
|
28
|
+
status: {type: 'integer', example: 200},
|
29
|
+
headers: {type: 'object'},
|
30
|
+
body: {type: 'string'},
|
31
|
+
},
|
32
|
+
},
|
33
|
+
processing: {
|
34
|
+
type: 'object',
|
35
|
+
properties: {
|
36
|
+
began_at_s: {type: 'string'},
|
37
|
+
duration: {type: 'number'},
|
38
|
+
tags: {type: 'array', items: {type: 'string'}}
|
39
|
+
},
|
40
|
+
},
|
41
|
+
},
|
42
|
+
})
|
43
|
+
class Ur
|
44
|
+
VERSION = UR_VERSION
|
45
|
+
|
46
|
+
autoload :RequestAndResponse, 'ur/request_and_response'
|
47
|
+
autoload :Middleware, 'ur/middleware'
|
48
|
+
autoload :FaradayMiddleware, 'ur/middleware'
|
49
|
+
autoload :RackMiddleware, 'ur/middleware'
|
50
|
+
|
51
|
+
Request = JSI.class_for_schema(self.schema['properties']['request'])
|
52
|
+
Response = JSI.class_for_schema(self.schema['properties']['response'])
|
53
|
+
Processing = JSI.class_for_schema(self.schema['properties']['processing'])
|
54
|
+
require 'ur/request'
|
55
|
+
require 'ur/response'
|
56
|
+
require 'ur/processing'
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def from_rack_request(request_env)
|
60
|
+
if request_env.is_a?(Rack::Request)
|
61
|
+
rack_request = request_env
|
62
|
+
env = request_env.env
|
63
|
+
else
|
64
|
+
rack_request = Rack::Request.new(request_env)
|
65
|
+
env = request_env
|
66
|
+
end
|
67
|
+
|
68
|
+
Ur.new({}).tap do |ur|
|
69
|
+
ur.processing.begin!
|
70
|
+
ur.bound = 'inbound'
|
71
|
+
ur.request['method'] = rack_request.request_method
|
72
|
+
ur.request.headers = env.map do |(key, value)|
|
73
|
+
http_match = key.match(/\AHTTP_/)
|
74
|
+
if http_match
|
75
|
+
name = http_match.post_match.downcase
|
76
|
+
{name => value}
|
77
|
+
else
|
78
|
+
name = %w(content_type content_length).detect { |sname| sname.downcase == key.downcase }
|
79
|
+
if name
|
80
|
+
{name => value}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end.compact.inject({}, &:update)
|
84
|
+
ur.request.addressable_uri = Addressable::URI.new(
|
85
|
+
:scheme => rack_request.scheme,
|
86
|
+
:host => rack_request.host,
|
87
|
+
:port => rack_request.port,
|
88
|
+
:path => rack_request.path,
|
89
|
+
:query => (rack_request.query_string unless rack_request.query_string.empty?)
|
90
|
+
)
|
91
|
+
env["rack.input"].rewind
|
92
|
+
ur.request.body = env["rack.input"].read
|
93
|
+
env["rack.input"].rewind
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def from_faraday_request(request_env, logger: nil)
|
98
|
+
Ur.new({}).tap do |ur|
|
99
|
+
ur.processing.begin!
|
100
|
+
ur.bound = 'outbound'
|
101
|
+
ur.request['method'] = request_env[:method].to_s
|
102
|
+
ur.request.headers = request_env[:request_headers]
|
103
|
+
ur.request.uri = request_env[:url].normalize.to_s
|
104
|
+
ur.request.set_body_from_faraday(request_env)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(ur = {}, *a, &b)
|
110
|
+
super(ur, *a, &b)
|
111
|
+
unless instance.respond_to?(:to_hash)
|
112
|
+
raise(TypeError, "expected hash argument. got: #{ur.pretty_inspect.chomp}")
|
113
|
+
end
|
114
|
+
self.request = {} if self.request.nil?
|
115
|
+
self.response = {} if self.response.nil?
|
116
|
+
self.processing = {} if self.processing.nil?
|
117
|
+
end
|
118
|
+
|
119
|
+
def logger=(logger)
|
120
|
+
if logger && logger.formatter.respond_to?(:current_tags)
|
121
|
+
processing.tags = logger.formatter.current_tags.dup
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def with_rack_response(app, env)
|
126
|
+
status, response_headers, response_body = app.call(env)
|
127
|
+
|
128
|
+
response.status = status
|
129
|
+
response.headers = response_headers
|
130
|
+
response.body = response_body.to_enum.to_a.join('')
|
131
|
+
|
132
|
+
response_body_proxy = ::Rack::BodyProxy.new(response_body) do
|
133
|
+
processing.finish!
|
134
|
+
|
135
|
+
yield
|
136
|
+
end
|
137
|
+
[status, response_headers, response_body_proxy]
|
138
|
+
end
|
139
|
+
|
140
|
+
def faraday_on_complete(app, request_env, &block)
|
141
|
+
app.call(request_env).on_complete do |response_env|
|
142
|
+
response.status = response_env[:status]
|
143
|
+
response.headers = response_env[:response_headers]
|
144
|
+
response.set_body_from_faraday(response_env)
|
145
|
+
processing.finish!
|
146
|
+
|
147
|
+
yield(response_env)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'ur' unless Object.const_defined?(:Ur)
|
2
|
+
|
3
|
+
class Ur
|
4
|
+
module Middleware
|
5
|
+
def initialize(app, options = {})
|
6
|
+
@app = app
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
attr_reader :app
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def invoke_callback(name, *a, &b)
|
13
|
+
if @options[name]
|
14
|
+
@options[name].call(*a, &b)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class FaradayMiddleware < ::Faraday::Middleware
|
20
|
+
include Ur::Middleware
|
21
|
+
def call(request_env)
|
22
|
+
ur = Ur.from_faraday_request(request_env)
|
23
|
+
invoke_callback(:before_request, ur)
|
24
|
+
ur.logger = options[:logger] if options[:logger]
|
25
|
+
ur.faraday_on_complete(@app, request_env) do |response_env|
|
26
|
+
invoke_callback(:after_response, ur)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class RackMiddleware
|
32
|
+
include Ur::Middleware
|
33
|
+
def call(env)
|
34
|
+
ur = Ur.from_rack_request(env)
|
35
|
+
invoke_callback(:before_request, ur)
|
36
|
+
ur.logger = options[:logger] if options[:logger]
|
37
|
+
ur.with_rack_response(@app, env) do
|
38
|
+
invoke_callback(:after_response, ur)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ur' unless Object.const_defined?(:Ur)
|
2
|
+
|
3
|
+
class Ur
|
4
|
+
class Processing
|
5
|
+
def began_at
|
6
|
+
began_at_s ? Time.parse(began_at_s) : nil
|
7
|
+
end
|
8
|
+
def began_at=(time)
|
9
|
+
self.began_at_s = time ? time.utc.iso8601(6) : nil
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :began_at_ns
|
13
|
+
|
14
|
+
def begin!
|
15
|
+
self.began_at ||= Time.now
|
16
|
+
self.began_at_ns ||= Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
17
|
+
end
|
18
|
+
def finish!
|
19
|
+
return if duration
|
20
|
+
if began_at_ns
|
21
|
+
now_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
22
|
+
self.duration = (now_ns - began_at_ns) * 1e-9
|
23
|
+
elsif began_at
|
24
|
+
now = Time.now
|
25
|
+
self.duration = (now.to_f - began_at.to_f)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ur/request.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'ur' unless Object.const_defined?(:Ur)
|
2
|
+
|
3
|
+
class Ur
|
4
|
+
class Request
|
5
|
+
include RequestAndResponse
|
6
|
+
|
7
|
+
def addressable_uri
|
8
|
+
uri ? Addressable::URI.parse(uri) : nil
|
9
|
+
end
|
10
|
+
def addressable_uri=(auri)
|
11
|
+
self.uri = auri ? auri.normalize.to_s : nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ur' unless Object.const_defined?(:Ur)
|
2
|
+
|
3
|
+
class Ur
|
4
|
+
module RequestAndResponse
|
5
|
+
module FaradayEntity
|
6
|
+
def set_body_from_faraday(env)
|
7
|
+
if env[:raw_body].respond_to?(:to_str)
|
8
|
+
self.body = env[:raw_body].to_str.dup
|
9
|
+
elsif env[:body].respond_to?(:to_str)
|
10
|
+
self.body = env[:body].to_str.dup
|
11
|
+
elsif env[:body].respond_to?(:read) && env[:body].respond_to?(:rewind)
|
12
|
+
env[:body].rewind
|
13
|
+
self.body = env[:body].read
|
14
|
+
env[:body].rewind
|
15
|
+
elsif env[:body]
|
16
|
+
# TODO not good
|
17
|
+
self['body_parsed'] = env[:body]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
include FaradayEntity
|
22
|
+
end
|
23
|
+
end
|
data/lib/ur/response.rb
ADDED
data/lib/ur/version.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
UR_VERSION = "0.0.1"
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('../lib', File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
# NO EXPECTATIONS
|
7
|
+
ENV["MT_NO_EXPECTATIONS"] = ''
|
8
|
+
|
9
|
+
require 'minitest/autorun'
|
10
|
+
require 'minitest/reporters'
|
11
|
+
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
12
|
+
|
13
|
+
class UrSpec < Minitest::Spec
|
14
|
+
def assert_json_equal(exp, act, *a)
|
15
|
+
assert_equal(JSI::Typelike.as_json(exp), JSI::Typelike.as_json(act), *a)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# register this to be the base class for specs instead of Minitest::Spec
|
20
|
+
Minitest::Spec.register_spec_type(//, UrSpec)
|
21
|
+
|
22
|
+
require 'ur'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday_middleware'
|
4
|
+
|
5
|
+
describe 'Ur faraday integration' do
|
6
|
+
it 'integrates, basic usage' do
|
7
|
+
ur = nil
|
8
|
+
faraday_conn = ::Faraday.new('https://ur.unth.net/') do |builder|
|
9
|
+
builder.use(Ur::FaradayMiddleware,
|
10
|
+
after_response: -> (ur_) { ur = ur_ },
|
11
|
+
)
|
12
|
+
builder.use(Faraday::Adapter::Rack, -> (env) { [200, {'Content-Type' => 'text/plain'}, ['ᚒ']] })
|
13
|
+
end
|
14
|
+
res = faraday_conn.get('/')
|
15
|
+
assert_equal('ᚒ', res.body)
|
16
|
+
assert_instance_of(Ur, ur)
|
17
|
+
assert_equal('get', ur.request['method'])
|
18
|
+
assert_equal('text/plain', ur.response.headers['Content-Type'])
|
19
|
+
assert_equal('ᚒ', ur.response.body)
|
20
|
+
assert(ur.validate)
|
21
|
+
end
|
22
|
+
it 'integrates, IO body' do
|
23
|
+
ur = nil
|
24
|
+
faraday_conn = ::Faraday.new('https://ur.unth.net/') do |builder|
|
25
|
+
builder.use(Ur::FaradayMiddleware,
|
26
|
+
after_response: -> (ur_) { ur = ur_ },
|
27
|
+
)
|
28
|
+
builder.use(Faraday::Adapter::Rack, -> (env) { [200, {'Content-Type' => 'text/plain'}, ['☺']] })
|
29
|
+
end
|
30
|
+
res = faraday_conn.post('/', StringIO.new('hello!'))
|
31
|
+
assert_equal('☺', res.body)
|
32
|
+
assert_instance_of(Ur, ur)
|
33
|
+
assert_equal('post', ur.request['method'])
|
34
|
+
assert_equal('hello!', ur.request.body)
|
35
|
+
assert_equal('text/plain', ur.response.headers['Content-Type'])
|
36
|
+
assert_equal('☺', ur.response.body)
|
37
|
+
assert(ur.validate)
|
38
|
+
end
|
39
|
+
it 'integrates, faraday middleware munges the json bodies but uses preserve_raw' do
|
40
|
+
ur = nil
|
41
|
+
faraday_conn = ::Faraday.new('https://ur.unth.net/') do |builder|
|
42
|
+
builder.request :json
|
43
|
+
builder.use(Ur::FaradayMiddleware,
|
44
|
+
after_response: -> (ur_) { ur = ur_ },
|
45
|
+
)
|
46
|
+
builder.response :json, preserve_raw: true
|
47
|
+
builder.use(Faraday::Adapter::Rack, -> (env) { [200, {'Content-Type' => 'application/json'}, ['{}']] })
|
48
|
+
end
|
49
|
+
res = faraday_conn.post('/', {'a' => 'b'})
|
50
|
+
assert_equal({}, res.body)
|
51
|
+
assert_instance_of(Ur, ur)
|
52
|
+
assert_equal('post', ur.request['method'])
|
53
|
+
assert_equal('{"a":"b"}', ur.request.body)
|
54
|
+
assert_equal('application/json', ur.response.headers['Content-Type'])
|
55
|
+
assert_equal('{}', ur.response.body)
|
56
|
+
assert(ur.validate)
|
57
|
+
end
|
58
|
+
it 'integrates, faraday middleware munges the json bodies and does not preserve_raw' do
|
59
|
+
ur = nil
|
60
|
+
faraday_conn = ::Faraday.new('https://ur.unth.net/') do |builder|
|
61
|
+
builder.use(Ur::FaradayMiddleware,
|
62
|
+
after_response: -> (ur_) { ur = ur_ },
|
63
|
+
)
|
64
|
+
builder.request :json
|
65
|
+
builder.response :json
|
66
|
+
builder.use(Faraday::Adapter::Rack, -> (env) { [200, {'Content-Type' => 'application/json'}, ['{}']] })
|
67
|
+
end
|
68
|
+
res = faraday_conn.post('/', {'a' => 'b'})
|
69
|
+
assert_equal({}, res.body)
|
70
|
+
assert_instance_of(Ur, ur)
|
71
|
+
assert_equal('post', ur.request['method'])
|
72
|
+
assert_nil(ur.request.body) # no good
|
73
|
+
assert_json_equal({"a" => "b"}, ur.request['body_parsed']) # best we get here
|
74
|
+
assert_equal('application/json', ur.response.headers['Content-Type'])
|
75
|
+
assert_nil(ur.response.body) # no good
|
76
|
+
assert_json_equal({}, ur.response['body_parsed']) # best we get here
|
77
|
+
assert(ur.validate)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe 'Ur processing' do
|
4
|
+
it 'sets duration from began_at' do
|
5
|
+
ur = Ur.new
|
6
|
+
ur.processing.began_at = Time.now
|
7
|
+
ur.processing.finish!
|
8
|
+
assert_instance_of(Float, ur.processing.duration)
|
9
|
+
assert_operator(ur.processing.duration, :>, 0)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
describe 'Ur rack integration' do
|
5
|
+
it 'builds from a rack env' do
|
6
|
+
env = Rack::MockRequest.env_for('https://ur.unth.net/', {'HTTP_FOO' => 'bar'})
|
7
|
+
ur = Ur.from_rack_request(env)
|
8
|
+
assert_equal('inbound', ur.bound)
|
9
|
+
assert_equal('GET', ur.request['method'])
|
10
|
+
assert_equal('bar', ur.request.headers['foo'])
|
11
|
+
assert_equal('https://ur.unth.net/', ur.request.uri)
|
12
|
+
assert(ur.response.empty?)
|
13
|
+
assert_instance_of(Time, ur.processing.began_at)
|
14
|
+
assert_nil(ur.processing.duration)
|
15
|
+
assert(ur.validate)
|
16
|
+
end
|
17
|
+
it 'builds from a rack request' do
|
18
|
+
env = Rack::Request.new(Rack::MockRequest.env_for('https://ur.unth.net/', {'HTTP_FOO' => 'bar'}))
|
19
|
+
ur = Ur.from_rack_request(env)
|
20
|
+
assert_equal('inbound', ur.bound)
|
21
|
+
assert_equal('GET', ur.request['method'])
|
22
|
+
assert_equal('bar', ur.request.headers['foo'])
|
23
|
+
assert_equal('https://ur.unth.net/', ur.request.uri)
|
24
|
+
assert(ur.response.empty?)
|
25
|
+
assert_instance_of(Time, ur.processing.began_at)
|
26
|
+
assert_nil(ur.processing.duration)
|
27
|
+
assert(ur.validate)
|
28
|
+
end
|
29
|
+
end
|
data/test/ur_test.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'faraday'
|
3
|
+
require 'active_support/tagged_logging'
|
4
|
+
|
5
|
+
describe 'Ur' do
|
6
|
+
it 'has a valid schema' do
|
7
|
+
Ur.schema.validate_schema!
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'initializes' do
|
11
|
+
Ur.new({})
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'would prefer not to initialize' do
|
15
|
+
assert_raises(TypeError) { Ur.new("hello!") }
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'integrates with rack and faraday middlewares' do
|
19
|
+
rack_app = proc do |env|
|
20
|
+
[200, {'Content-Type' => 'text/plain'}, ['ᚒ']]
|
21
|
+
end
|
22
|
+
client_logger = ActiveSupport::TaggedLogging.new(Logger.new(StringIO.new))
|
23
|
+
server_logger = ActiveSupport::TaggedLogging.new(Logger.new(StringIO.new))
|
24
|
+
called_rack_before_request = false
|
25
|
+
called_rack_after_response = false
|
26
|
+
called_faraday_before_request = false
|
27
|
+
called_faraday_after_response = false
|
28
|
+
rack_app = Ur::RackMiddleware.new(rack_app, logger: server_logger,
|
29
|
+
before_request: -> (ur) do
|
30
|
+
called_rack_before_request = true
|
31
|
+
|
32
|
+
server_logger.push_tags 'ur_test_rack'
|
33
|
+
|
34
|
+
assert_equal('inbound', ur.bound)
|
35
|
+
assert_equal('GET', ur.request['method'])
|
36
|
+
assert_equal('ur.unth.net', ur.request.headers['host'])
|
37
|
+
assert_equal('bar', ur.request.headers['foo'])
|
38
|
+
assert_equal('https://ur.unth.net/', ur.request.uri)
|
39
|
+
assert(ur.response.empty?)
|
40
|
+
assert_instance_of(Time, ur.processing.began_at)
|
41
|
+
assert_nil(ur.processing.duration)
|
42
|
+
assert(ur.validate)
|
43
|
+
end,
|
44
|
+
after_response: -> (ur) do
|
45
|
+
called_rack_after_response = true
|
46
|
+
|
47
|
+
server_logger.pop_tags
|
48
|
+
|
49
|
+
assert_equal('inbound', ur.bound)
|
50
|
+
assert_equal('GET', ur.request['method'])
|
51
|
+
assert_equal(200, ur.response.status)
|
52
|
+
assert_equal('text/plain', ur.response.headers['Content-Type'])
|
53
|
+
assert_equal('ᚒ', ur.response.body)
|
54
|
+
assert_instance_of(Time, ur.processing.began_at)
|
55
|
+
assert_instance_of(Float, ur.processing.duration)
|
56
|
+
assert_operator(ur.processing.duration, :>, 0)
|
57
|
+
assert_equal(['ur_test_rack'], ur.processing.tags.to_a)
|
58
|
+
assert(ur.validate)
|
59
|
+
end,
|
60
|
+
)
|
61
|
+
faraday_conn = ::Faraday.new('https://ur.unth.net/') do |builder|
|
62
|
+
builder.use(Ur::FaradayMiddleware, logger: client_logger,
|
63
|
+
before_request: -> (ur) do
|
64
|
+
called_faraday_before_request = true
|
65
|
+
|
66
|
+
client_logger.push_tags 'ur_test_faraday'
|
67
|
+
|
68
|
+
assert_equal('outbound', ur.bound)
|
69
|
+
assert_equal('get', ur.request['method'])
|
70
|
+
assert_equal('bar', ur.request.headers['foo'])
|
71
|
+
assert_equal('https://ur.unth.net/', ur.request.uri)
|
72
|
+
assert_equal(Addressable::URI.parse('https://ur.unth.net/'), ur.request.addressable_uri)
|
73
|
+
assert(ur.response.empty?)
|
74
|
+
assert_instance_of(Time, ur.processing.began_at)
|
75
|
+
assert_nil(ur.processing.duration)
|
76
|
+
assert(ur.validate)
|
77
|
+
end,
|
78
|
+
after_response: -> (ur) do
|
79
|
+
called_faraday_after_response = true
|
80
|
+
|
81
|
+
client_logger.pop_tags
|
82
|
+
|
83
|
+
assert_equal('outbound', ur.bound)
|
84
|
+
assert_equal('get', ur.request['method'])
|
85
|
+
assert_equal(200, ur.response.status)
|
86
|
+
assert_equal('text/plain', ur.response.headers['Content-Type'])
|
87
|
+
assert_equal('ᚒ', ur.response.body)
|
88
|
+
assert_instance_of(Time, ur.processing.began_at)
|
89
|
+
assert_instance_of(Float, ur.processing.duration)
|
90
|
+
assert_operator(ur.processing.duration, :>, 0)
|
91
|
+
assert_equal(['ur_test_faraday'], ur.processing.tags.to_a)
|
92
|
+
assert(ur.validate)
|
93
|
+
end,
|
94
|
+
)
|
95
|
+
builder.use(Faraday::Adapter::Rack, rack_app)
|
96
|
+
end
|
97
|
+
res = faraday_conn.get('/', nil, {'Foo' => 'bar'})
|
98
|
+
assert(called_rack_before_request)
|
99
|
+
assert(called_rack_after_response)
|
100
|
+
assert(called_faraday_before_request)
|
101
|
+
assert(called_faraday_after_response)
|
102
|
+
assert_equal(200, res.status)
|
103
|
+
assert_equal('ᚒ', res.body)
|
104
|
+
end
|
105
|
+
end
|
data/ur.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ur/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ur"
|
8
|
+
spec.version = UR_VERSION
|
9
|
+
spec.authors = ["Ethan"]
|
10
|
+
spec.email = ["ethan@unth"]
|
11
|
+
|
12
|
+
spec.summary = 'ur: unified request representation'
|
13
|
+
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.'
|
14
|
+
spec.homepage = "https://github.com/notEthan/ur"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
ignore_files = %w(.gitignore .travis.yml Gemfile test)
|
18
|
+
ignore_files_re = %r{\A(#{ignore_files.map { |f| Regexp.escape(f) }.join('|')})(/|\z)}
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(ignore_files_re) }
|
23
|
+
spec.test_files = `git ls-files -z test`.split("\x0") + [
|
24
|
+
'.simplecov',
|
25
|
+
]
|
26
|
+
end
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_dependency "jsi", "~> 0.0.2"
|
32
|
+
spec.add_dependency "addressable", "~> 2.0"
|
33
|
+
spec.add_development_dependency "rack"
|
34
|
+
spec.add_development_dependency "rack-test"
|
35
|
+
spec.add_development_dependency "faraday"
|
36
|
+
spec.add_development_dependency "faraday_middleware"
|
37
|
+
spec.add_development_dependency "activesupport"
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
39
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
41
|
+
spec.add_development_dependency "minitest-reporters"
|
42
|
+
spec.add_development_dependency "simplecov"
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ethan
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jsi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: addressable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: faraday_middleware
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activesupport
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: bundler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.16'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.16'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rake
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '10.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '10.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: minitest
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '5.0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '5.0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: minitest-reporters
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: ur provides a unified representation of a request and response. it can
|
182
|
+
be interpreted from rack, faraday, or potentially other sources, and provides a
|
183
|
+
consistent interface to access the attributes inherent to the request and additional
|
184
|
+
useful parsers and computation from the request.
|
185
|
+
email:
|
186
|
+
- ethan@unth
|
187
|
+
executables: []
|
188
|
+
extensions: []
|
189
|
+
extra_rdoc_files: []
|
190
|
+
files:
|
191
|
+
- ".simplecov"
|
192
|
+
- ".yardopts"
|
193
|
+
- CHANGELOG.md
|
194
|
+
- LICENSE.txt
|
195
|
+
- README.md
|
196
|
+
- Rakefile.rb
|
197
|
+
- lib/ur.rb
|
198
|
+
- lib/ur/middleware.rb
|
199
|
+
- lib/ur/processing.rb
|
200
|
+
- lib/ur/request.rb
|
201
|
+
- lib/ur/request_and_response.rb
|
202
|
+
- lib/ur/response.rb
|
203
|
+
- lib/ur/version.rb
|
204
|
+
- test/test_helper.rb
|
205
|
+
- test/ur_faraday_test.rb
|
206
|
+
- test/ur_processing_test.rb
|
207
|
+
- test/ur_rack_test.rb
|
208
|
+
- test/ur_test.rb
|
209
|
+
- ur.gemspec
|
210
|
+
homepage: https://github.com/notEthan/ur
|
211
|
+
licenses:
|
212
|
+
- MIT
|
213
|
+
metadata: {}
|
214
|
+
post_install_message:
|
215
|
+
rdoc_options: []
|
216
|
+
require_paths:
|
217
|
+
- lib
|
218
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '0'
|
228
|
+
requirements: []
|
229
|
+
rubyforge_project:
|
230
|
+
rubygems_version: 2.7.7
|
231
|
+
signing_key:
|
232
|
+
specification_version: 4
|
233
|
+
summary: 'ur: unified request representation'
|
234
|
+
test_files:
|
235
|
+
- test/test_helper.rb
|
236
|
+
- test/ur_faraday_test.rb
|
237
|
+
- test/ur_processing_test.rb
|
238
|
+
- test/ur_rack_test.rb
|
239
|
+
- test/ur_test.rb
|
240
|
+
- ".simplecov"
|