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 CHANGED
@@ -1,4 +1 @@
1
1
  require "bundler/gem_tasks"
2
-
3
- require "assert/rake_tasks"
4
- Assert::RakeTasks.install
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", ["~>1.0"])
22
+ gem.add_development_dependency("assert", ["~>2.0"])
23
23
  gem.add_development_dependency("assert-mocha", ["~>1.0"])
24
24
  end
@@ -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, :responses}
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 = self.responses.find(name, params) if ENV['ANDSON_TEST_MODE']
76
- client_response ||= self.call!(name, params)
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
- AndSon::Response.parse(connection.read(timeout_value))
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module AndSon
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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
- serve(socket)
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
- pid = fork do
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
- if pid
49
- Process.kill("TERM", pid)
50
- Process.wait(pid)
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
@@ -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: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
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-02-05 00:00:00 Z
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: 15
43
+ hash: 3
44
44
  segments:
45
- - 1
45
+ - 2
46
46
  - 0
47
- version: "1.0"
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
@@ -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