and-son 0.2.1 → 0.3.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.
- data/Rakefile +0 -3
- data/and-son.gemspec +1 -1
- data/lib/and-son/client.rb +71 -5
- data/lib/and-son/response.rb +15 -2
- data/lib/and-son/version.rb +1 -1
- data/test/support/fake_server.rb +21 -11
- data/test/unit/client_test.rb +29 -2
- metadata +8 -9
- data/lib/and-son/exceptions.rb +0 -19
data/Rakefile
CHANGED
data/and-son.gemspec
CHANGED
@@ -19,6 +19,6 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency("sanford-protocol", ["~>0.5"])
|
21
21
|
|
22
|
-
gem.add_development_dependency("assert", ["~>
|
22
|
+
gem.add_development_dependency("assert", ["~>2.0"])
|
23
23
|
gem.add_development_dependency("assert-mocha", ["~>1.0"])
|
24
24
|
end
|
data/lib/and-son/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'logger'
|
1
3
|
require 'ostruct'
|
2
4
|
require 'sanford-protocol'
|
3
5
|
require 'and-son/connection'
|
@@ -14,7 +16,7 @@ module AndSon
|
|
14
16
|
# called on a client, but returns the chained instance if called on a runner
|
15
17
|
|
16
18
|
def timeout(seconds)
|
17
|
-
self.call_runner.tap{|r| r.timeout_value = seconds.to_f}
|
19
|
+
self.call_runner.tap{|r| r.timeout_value = seconds.to_f }
|
18
20
|
end
|
19
21
|
|
20
22
|
def params(hash = nil)
|
@@ -24,6 +26,10 @@ module AndSon
|
|
24
26
|
self.call_runner.tap{|r| r.params_value.merge!(self.stringify_keys(hash)) }
|
25
27
|
end
|
26
28
|
|
29
|
+
def logger(passed_logger)
|
30
|
+
self.call_runner.tap{|r| r.logger_value = passed_logger }
|
31
|
+
end
|
32
|
+
|
27
33
|
protected
|
28
34
|
|
29
35
|
def stringify_keys(hash)
|
@@ -55,13 +61,15 @@ module AndSon
|
|
55
61
|
:version => version,
|
56
62
|
:timeout_value => (ENV['ANDSON_TIMEOUT'] || DEFAULT_TIMEOUT).to_f,
|
57
63
|
:params_value => {},
|
64
|
+
:logger_value => NullLogger.new,
|
58
65
|
:responses => @responses,
|
59
66
|
})
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
63
70
|
class CallRunner < OpenStruct
|
64
|
-
# {:host, :port, :version, :timeout_value, :params_value, :
|
71
|
+
# { :host, :port, :version, :timeout_value, :params_value, :logger_value,
|
72
|
+
# :responses }
|
65
73
|
include CallRunnerMethods
|
66
74
|
|
67
75
|
# chain runner methods by returning itself
|
@@ -72,9 +80,13 @@ module AndSon
|
|
72
80
|
if !params.kind_of?(Hash)
|
73
81
|
raise ArgumentError, "expected params to be a Hash instead of a #{params.class}"
|
74
82
|
end
|
75
|
-
client_response =
|
76
|
-
|
83
|
+
client_response = nil
|
84
|
+
benchmark = Benchmark.measure do
|
85
|
+
client_response = self.responses.find(name, params) if ENV['ANDSON_TEST_MODE']
|
86
|
+
client_response ||= self.call!(name, params)
|
87
|
+
end
|
77
88
|
|
89
|
+
self.logger_value.info("[AndSon] #{summary_line(name, params, benchmark, client_response)}")
|
78
90
|
if block_given?
|
79
91
|
yield client_response.protocol_response
|
80
92
|
else
|
@@ -86,10 +98,64 @@ module AndSon
|
|
86
98
|
call_params = self.params_value.merge(params)
|
87
99
|
AndSon::Connection.new(host, port).open do |connection|
|
88
100
|
connection.write(Sanford::Protocol::Request.new(version, name, call_params).to_hash)
|
89
|
-
|
101
|
+
if !connection.peek(timeout_value).empty?
|
102
|
+
AndSon::Response.parse(connection.read(timeout_value))
|
103
|
+
else
|
104
|
+
raise AndSon::ConnectionClosedError.new
|
105
|
+
end
|
90
106
|
end
|
91
107
|
end
|
92
108
|
|
109
|
+
protected
|
110
|
+
|
111
|
+
def summary_line(name, params, benchmark, client_response)
|
112
|
+
response = client_response.protocol_response
|
113
|
+
SummaryLine.new.tap do |line|
|
114
|
+
line.add 'host', "#{self.host}:#{self.port}"
|
115
|
+
line.add 'version', self.version
|
116
|
+
line.add 'service', name
|
117
|
+
line.add 'params', params
|
118
|
+
line.add 'status', response.code
|
119
|
+
line.add 'duration', self.round_time(benchmark.real)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
ROUND_PRECISION = 2
|
124
|
+
ROUND_MODIFIER = 10 ** ROUND_PRECISION
|
125
|
+
def round_time(time_in_seconds)
|
126
|
+
(time_in_seconds * 1000 * ROUND_MODIFIER).to_i / ROUND_MODIFIER.to_f
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
class SummaryLine
|
132
|
+
|
133
|
+
def initialize
|
134
|
+
@hash = {}
|
135
|
+
end
|
136
|
+
|
137
|
+
def add(key, value)
|
138
|
+
@hash[key] = value.inspect if value
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
[ 'host', 'version', 'service', 'status', 'duration', 'params' ].map do |key|
|
143
|
+
"#{key}=#{@hash[key]}" if @hash[key]
|
144
|
+
end.compact.join(" ")
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
class ConnectionClosedError < RuntimeError
|
150
|
+
def initialize
|
151
|
+
super "The server closed the connection, no response was written."
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class NullLogger
|
156
|
+
::Logger::Severity.constants.each do |name|
|
157
|
+
define_method(name.downcase){|*args| } # no-op
|
158
|
+
end
|
93
159
|
end
|
94
160
|
|
95
161
|
end
|
data/lib/and-son/response.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'sanford-protocol'
|
2
2
|
|
3
|
-
require 'and-son/exceptions'
|
4
|
-
|
5
3
|
module AndSon
|
6
4
|
|
7
5
|
class Response < Struct.new(:protocol_response)
|
@@ -41,4 +39,19 @@ module AndSon
|
|
41
39
|
|
42
40
|
end
|
43
41
|
|
42
|
+
class RequestError < RuntimeError
|
43
|
+
attr_reader :response
|
44
|
+
|
45
|
+
def initialize(protocol_response)
|
46
|
+
super(protocol_response.status.message)
|
47
|
+
@response = protocol_response
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ClientError = Class.new(RequestError)
|
52
|
+
BadRequestError = Class.new(ClientError)
|
53
|
+
NotFoundError = Class.new(ClientError)
|
54
|
+
|
55
|
+
ServerError = Class.new(RequestError)
|
56
|
+
|
44
57
|
end
|
data/lib/and-son/version.rb
CHANGED
data/test/support/fake_server.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
class FakeServer
|
2
2
|
|
3
|
-
def initialize(port)
|
3
|
+
def initialize(port, options = nil)
|
4
|
+
options ||= {}
|
4
5
|
@port = port
|
5
6
|
@handlers = {}
|
7
|
+
|
8
|
+
@closing_server = !!options[:closing_server]
|
6
9
|
end
|
7
10
|
|
8
11
|
def add_handler(version, name, &block)
|
@@ -13,7 +16,13 @@ class FakeServer
|
|
13
16
|
server = TCPServer.new("localhost", @port)
|
14
17
|
socket = server.accept
|
15
18
|
|
16
|
-
|
19
|
+
if @closing_server
|
20
|
+
sleep 0.1 # ensure the connection isn't closed before a client can run
|
21
|
+
# IO.select
|
22
|
+
socket.close
|
23
|
+
else
|
24
|
+
serve(socket)
|
25
|
+
end
|
17
26
|
|
18
27
|
server.close rescue false
|
19
28
|
end
|
@@ -37,21 +46,22 @@ class FakeServer
|
|
37
46
|
|
38
47
|
def run_fake_server(server, &block)
|
39
48
|
begin
|
40
|
-
|
41
|
-
trap("TERM"){ exit }
|
42
|
-
server.run
|
43
|
-
end
|
44
|
-
|
45
|
-
sleep 0.3 # Give time for the socket to start listening.
|
49
|
+
thread = Thread.new{ server.run }
|
46
50
|
yield
|
47
51
|
ensure
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
begin
|
53
|
+
TCPSocket.open("localhost", server.instance_variable_get("@port"))
|
54
|
+
rescue Exception
|
51
55
|
end
|
56
|
+
thread.join
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
60
|
+
def start_closing_server(port, &block)
|
61
|
+
server = FakeServer.new(port, :closing_server => true)
|
62
|
+
run_fake_server(server, &block)
|
63
|
+
end
|
64
|
+
|
55
65
|
end
|
56
66
|
|
57
67
|
end
|
data/test/unit/client_test.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'assert'
|
2
|
+
require 'and-son/stored_responses'
|
2
3
|
|
3
4
|
class AndSon::Client
|
4
5
|
|
5
6
|
class BaseTest < Assert::Context
|
7
|
+
include FakeServer::Helper
|
8
|
+
|
6
9
|
desc "AndSon::Client"
|
7
10
|
setup do
|
8
11
|
@host, @port, @version = '0.0.0.0', 8000, "v1"
|
@@ -10,8 +13,8 @@ class AndSon::Client
|
|
10
13
|
end
|
11
14
|
subject{ @client }
|
12
15
|
|
13
|
-
should have_imeths :host, :port, :version
|
14
|
-
should have_imeths :call_runner, :call, :timeout
|
16
|
+
should have_imeths :host, :port, :version, :responses
|
17
|
+
should have_imeths :call_runner, :call, :timeout, :logger, :params
|
15
18
|
|
16
19
|
should "know its default call runner" do
|
17
20
|
default_runner = subject.call_runner
|
@@ -20,6 +23,7 @@ class AndSon::Client
|
|
20
23
|
assert_equal @port, default_runner.port
|
21
24
|
assert_equal @version, default_runner.version
|
22
25
|
assert_equal 60.0, default_runner.timeout_value
|
26
|
+
assert_instance_of AndSon::NullLogger, default_runner.logger_value
|
23
27
|
end
|
24
28
|
|
25
29
|
should "override the default call runner timeout with an env var" do
|
@@ -48,6 +52,14 @@ class AndSon::Client
|
|
48
52
|
assert_equal({ "api_key" => 12345 }, runner.params_value)
|
49
53
|
end
|
50
54
|
|
55
|
+
should "return a CallRunner with a logger value set #logger" do
|
56
|
+
runner = subject.logger(logger = Logger.new(STDOUT))
|
57
|
+
|
58
|
+
assert_kind_of AndSon::CallRunner, runner
|
59
|
+
assert_respond_to :call, runner
|
60
|
+
assert_equal logger, runner.logger_value
|
61
|
+
end
|
62
|
+
|
51
63
|
should "raise an ArgumentError when #params is not passed a Hash" do
|
52
64
|
assert_raises(ArgumentError) do
|
53
65
|
subject.params('test')
|
@@ -61,6 +73,21 @@ class AndSon::Client
|
|
61
73
|
runner.call('something', 'test')
|
62
74
|
end
|
63
75
|
end
|
76
|
+
|
77
|
+
should "track its stored responses" do
|
78
|
+
assert_kind_of AndSon::StoredResponses, subject.responses
|
79
|
+
end
|
80
|
+
|
81
|
+
should "raise a ConnectionClosedError when the server closes the connection" do
|
82
|
+
self.start_closing_server(12001) do
|
83
|
+
client = AndSon::Client.new('localhost', 12001, 'v1')
|
84
|
+
|
85
|
+
assert_raises(AndSon::ConnectionClosedError) do
|
86
|
+
client.call('anything')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
64
91
|
end
|
65
92
|
|
66
93
|
# the `call` method is tested in the file test/system/making_requests_test.rb,
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: and-son
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Collin Redding
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2013-
|
19
|
+
date: 2013-03-07 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
prerelease: false
|
@@ -40,11 +40,11 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
hash:
|
43
|
+
hash: 3
|
44
44
|
segments:
|
45
|
-
-
|
45
|
+
- 2
|
46
46
|
- 0
|
47
|
-
version: "
|
47
|
+
version: "2.0"
|
48
48
|
requirement: *id002
|
49
49
|
name: assert
|
50
50
|
type: :development
|
@@ -83,7 +83,6 @@ files:
|
|
83
83
|
- lib/and-son.rb
|
84
84
|
- lib/and-son/client.rb
|
85
85
|
- lib/and-son/connection.rb
|
86
|
-
- lib/and-son/exceptions.rb
|
87
86
|
- lib/and-son/response.rb
|
88
87
|
- lib/and-son/stored_responses.rb
|
89
88
|
- lib/and-son/version.rb
|
data/lib/and-son/exceptions.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module AndSon
|
2
|
-
|
3
|
-
class RequestError < RuntimeError
|
4
|
-
attr_reader :response
|
5
|
-
|
6
|
-
def initialize(protocol_response)
|
7
|
-
super(protocol_response.status.message)
|
8
|
-
@response = protocol_response
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
ClientError = Class.new(RequestError)
|
13
|
-
|
14
|
-
BadRequestError = Class.new(ClientError)
|
15
|
-
NotFoundError = Class.new(ClientError)
|
16
|
-
|
17
|
-
ServerError = Class.new(RequestError)
|
18
|
-
|
19
|
-
end
|