and-son 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|