and-son 0.1.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/.gitignore +17 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +104 -0
- data/Rakefile +4 -0
- data/and-son.gemspec +24 -0
- data/lib/and-son.rb +10 -0
- data/lib/and-son/client.rb +73 -0
- data/lib/and-son/connection.rb +33 -0
- data/lib/and-son/exceptions.rb +19 -0
- data/lib/and-son/response.rb +44 -0
- data/lib/and-son/version.rb +3 -0
- data/test/helper.rb +11 -0
- data/test/support/fake_server.rb +57 -0
- data/test/system/making_requests_test.rb +163 -0
- data/test/unit/and-son_test.rb +13 -0
- data/test/unit/client_test.rb +68 -0
- data/test/unit/response_test.rb +108 -0
- metadata +134 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Collin Redding
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# AndSon
|
2
|
+
|
3
|
+
AndSon is a simple Sanford client for Ruby. It provides an API for calling services and handling responses. It uses [Sanford::Protocol](https://github.com/redding/sanford-protocol) to communicate with Sanford servers.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# create a client
|
9
|
+
client = AndSon.new('127.0.0.1', 8000, 'v1')
|
10
|
+
|
11
|
+
# call a service and get its response data:
|
12
|
+
user_data = client.call('get_user', {:user_name => 'joetest'})
|
13
|
+
```
|
14
|
+
|
15
|
+
## Calling Services
|
16
|
+
|
17
|
+
To call a service, you first need a client to make the calls. You define clients by specifying the host's ip address and port plus the version of the API to make calls against.
|
18
|
+
|
19
|
+
Once you have your client defined, make service calls using the `call` method. It will return any response data and raise an exception if anything goes wrong.
|
20
|
+
|
21
|
+
### Timeouts
|
22
|
+
|
23
|
+
By default, all requests timeout after 60s. You can override this globally using the `ANDSON_TIMEOUT` env var. You can override this global timeout on a per-call basis by chaining in the `timeout` method.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# timeout this request after 10 seconds
|
27
|
+
client.timeout(10).call('get_user', {:user_name => 'joetest'})
|
28
|
+
```
|
29
|
+
|
30
|
+
When a request times out, a `Sanford::Protocol::TimeoutError` is raised:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
begin
|
34
|
+
client.timeout(10).call('get_user', {:user_name => 'joetest'})
|
35
|
+
rescue Sanford::Protocol::TimeoutError => err
|
36
|
+
puts "timeout - so sad :("
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
### Default Params
|
41
|
+
|
42
|
+
Similarly to timeouts, all requests default their params to an empty `Hash` (`{}`). This can be overriden using the `params` method.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# add an API key to all requests made by this client, to authorize our client
|
46
|
+
client.params({ 'api_key' => 12345 }).call('get_user', {:user_name => 'joetest'})
|
47
|
+
```
|
48
|
+
|
49
|
+
One thing to be aware of, AndSon has limited ability to 'merge' or 'append' params. For example:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# raises an exception, can't merge a string on to a hash
|
53
|
+
client.params({ 'api_key' => 12345 }).call('get_user', 'joetest')
|
54
|
+
```
|
55
|
+
|
56
|
+
Be aware of this when setting default params and passing additional params with the `call` method. In general, it's recommended to use ruby's `Hash` for the best results.
|
57
|
+
|
58
|
+
### Exception Handling
|
59
|
+
|
60
|
+
AndSon raises exceptions when a call responds with a `4xx` or `5xx` response code (see [Sanford Status Codes](https://github.com/redding/sanford-protocol#status-codes) for more on response codes):
|
61
|
+
|
62
|
+
* `400`: `BadRequestError < ClientError`
|
63
|
+
* `404`: `NotFoundError < ClientError`
|
64
|
+
* `4xx`: `ClientError < RequestError`
|
65
|
+
* `5xx`: `ServerError < RequestError`
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
client.call('some_unknown_service') #=> NotFoundError...
|
69
|
+
```
|
70
|
+
|
71
|
+
Each exception knows about the response that raised it:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
begin
|
75
|
+
client.call('some_unknown_service')
|
76
|
+
rescue AndSon::NotFoundError => err
|
77
|
+
err.response #=> AndSon::Response ...
|
78
|
+
err.response.code #=> 404
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### Response Handling
|
83
|
+
|
84
|
+
If you call a service and pass it a block, no exceptions will be raised and the call will yield its response to the block. The call will return the return value of the block.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
user = client.call('get_user', { :user_name => 'joetest' }) do |response|
|
88
|
+
if response.code == 200
|
89
|
+
User.new(response.data)
|
90
|
+
else
|
91
|
+
NullUser.new
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
For more details about the response object, see [sanford-protocol](https://github.com/redding/sanford-protocol#response).
|
97
|
+
|
98
|
+
## Contributing
|
99
|
+
|
100
|
+
1. Fork it
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
104
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/and-son.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'and-son/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "and-son"
|
8
|
+
gem.version = AndSon::VERSION
|
9
|
+
gem.authors = ["Collin Redding", "Kelly Redding"]
|
10
|
+
gem.email = ["collin.redding@me.com", "kelly@kellyredding.com"]
|
11
|
+
gem.description = "Simple Sanford client for Ruby."
|
12
|
+
gem.summary = "Simple Sanford client for Ruby."
|
13
|
+
gem.homepage = "https://github.com/redding/and-son"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("sanford-protocol", ["~>0.4"])
|
21
|
+
|
22
|
+
gem.add_development_dependency("assert", ["~>1.0"])
|
23
|
+
gem.add_development_dependency("assert-mocha", ["~>1.0"])
|
24
|
+
end
|
data/lib/and-son.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'sanford-protocol'
|
3
|
+
require 'and-son/connection'
|
4
|
+
require 'and-son/response'
|
5
|
+
|
6
|
+
module AndSon
|
7
|
+
|
8
|
+
module CallRunnerMethods
|
9
|
+
|
10
|
+
# define methods here to allow configuring call runner params. be sure to
|
11
|
+
# use `tap` to return whatever instance `self.call_runner` returns so you
|
12
|
+
# can method-chain. `self.call_runner` returns a new runner instance if
|
13
|
+
# called on a client, but returns the chained instance if called on a runner
|
14
|
+
|
15
|
+
def timeout(seconds)
|
16
|
+
self.call_runner.tap{|r| r.timeout_value = seconds.to_f}
|
17
|
+
end
|
18
|
+
|
19
|
+
def params(hash = nil)
|
20
|
+
if !hash.kind_of?(Hash)
|
21
|
+
raise ArgumentError, "expected params to be a Hash instead of a #{hash.class}"
|
22
|
+
end
|
23
|
+
self.call_runner.tap{|r| r.params_value.merge!(hash) }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class Client < Struct.new(:host, :port, :version)
|
29
|
+
include CallRunnerMethods
|
30
|
+
|
31
|
+
DEFAULT_TIMEOUT = 60 #seconds
|
32
|
+
|
33
|
+
# proxy the call method to the call runner
|
34
|
+
def call(*args, &block); self.call_runner.call(*args, &block); end
|
35
|
+
|
36
|
+
def call_runner
|
37
|
+
# always start with this default CallRunner
|
38
|
+
CallRunner.new({
|
39
|
+
:host => host,
|
40
|
+
:port => port,
|
41
|
+
:version => version,
|
42
|
+
:timeout_value => (ENV['ANDSON_TIMEOUT'] || DEFAULT_TIMEOUT).to_f,
|
43
|
+
:params_value => {}
|
44
|
+
})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class CallRunner < OpenStruct # {:host, :port, :version, :timeout_value, :params_value}
|
49
|
+
include CallRunnerMethods
|
50
|
+
|
51
|
+
# chain runner methods by returning itself
|
52
|
+
def call_runner; self; end
|
53
|
+
|
54
|
+
def call(name, params = {})
|
55
|
+
if !params.kind_of?(Hash)
|
56
|
+
raise ArgumentError, "expected params to be a Hash instead of a #{hash.class}"
|
57
|
+
end
|
58
|
+
call_params = self.params_value.merge(params)
|
59
|
+
AndSon::Connection.new(host, port).open do |connection|
|
60
|
+
connection.write(Sanford::Protocol::Request.new(version, name, call_params).to_hash)
|
61
|
+
client_response = AndSon::Response.parse(connection.read(timeout_value))
|
62
|
+
|
63
|
+
if block_given?
|
64
|
+
yield client_response.protocol_response
|
65
|
+
else
|
66
|
+
client_response.data
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'sanford-protocol'
|
3
|
+
|
4
|
+
module AndSon
|
5
|
+
|
6
|
+
class Connection < Struct.new(:host, :port)
|
7
|
+
module NoRequest
|
8
|
+
def self.to_s; "[?]"; end
|
9
|
+
end
|
10
|
+
|
11
|
+
def open
|
12
|
+
protocol_connection = Sanford::Protocol::Connection.new(tcp_socket)
|
13
|
+
yield protocol_connection if block_given?
|
14
|
+
ensure
|
15
|
+
protocol_connection.close rescue false
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# TCP_NODELAY is set to disable buffering. In the case of Sanford
|
21
|
+
# communication, we have all the information we need to send up front and
|
22
|
+
# are closing the connection, so it doesn't need to buffer.
|
23
|
+
# See http://linux.die.net/man/7/tcp
|
24
|
+
|
25
|
+
def tcp_socket
|
26
|
+
TCPSocket.new(host, port).tap do |socket|
|
27
|
+
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'sanford-protocol'
|
2
|
+
|
3
|
+
require 'and-son/exceptions'
|
4
|
+
|
5
|
+
module AndSon
|
6
|
+
|
7
|
+
class Response < Struct.new(:protocol_response)
|
8
|
+
|
9
|
+
CODE_MATCHERS = {
|
10
|
+
'400' => 400,
|
11
|
+
'404' => 404,
|
12
|
+
'4xx' => /4[0-9][0-9]/,
|
13
|
+
'5xx' => /5[0-9][0-9]/
|
14
|
+
}
|
15
|
+
|
16
|
+
def self.parse(hash)
|
17
|
+
self.new(Sanford::Protocol::Response.parse(hash))
|
18
|
+
end
|
19
|
+
|
20
|
+
def data
|
21
|
+
if self.code_is_5xx?
|
22
|
+
raise ServerError.new(self.protocol_response)
|
23
|
+
elsif self.code_is_404?
|
24
|
+
raise NotFoundError.new(self.protocol_response)
|
25
|
+
elsif self.code_is_400?
|
26
|
+
raise BadRequestError.new(self.protocol_response)
|
27
|
+
elsif self.code_is_4xx?
|
28
|
+
raise ClientError.new(self.protocol_response)
|
29
|
+
else
|
30
|
+
self.protocol_response.data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
CODE_MATCHERS.each do |name, matcher|
|
35
|
+
matcher = matcher.kind_of?(Regexp) ? matcher : Regexp.new(matcher.to_s)
|
36
|
+
|
37
|
+
define_method("code_is_#{name}?") do
|
38
|
+
!!(self.protocol_response.code.to_s =~ matcher)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
class FakeServer
|
2
|
+
|
3
|
+
def initialize(port)
|
4
|
+
@port = port
|
5
|
+
@handlers = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_handler(version, name, &block)
|
9
|
+
@handlers["#{version}-#{name}"] = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
server = TCPServer.new("localhost", @port)
|
14
|
+
socket = server.accept
|
15
|
+
|
16
|
+
serve(socket)
|
17
|
+
|
18
|
+
server.close rescue false
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def serve(socket)
|
24
|
+
connection = Sanford::Protocol::Connection.new(socket)
|
25
|
+
request = Sanford::Protocol::Request.parse(connection.read)
|
26
|
+
status, result = route(request)
|
27
|
+
response = Sanford::Protocol::Response.new(status, result)
|
28
|
+
connection.write(response.to_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def route(request)
|
32
|
+
handler = @handlers["#{request.version}-#{request.name}"]
|
33
|
+
returned = handler.call(request.params)
|
34
|
+
end
|
35
|
+
|
36
|
+
module Helper
|
37
|
+
|
38
|
+
def run_fake_server(server, &block)
|
39
|
+
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.
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
if pid
|
49
|
+
Process.kill("TERM", pid)
|
50
|
+
Process.wait(pid)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
class MakingRequestsTest < Assert::Context
|
4
|
+
include FakeServer::Helper
|
5
|
+
|
6
|
+
desc "making a request that"
|
7
|
+
setup do
|
8
|
+
@fake_server = FakeServer.new(12000)
|
9
|
+
end
|
10
|
+
|
11
|
+
class SuccessTest < MakingRequestsTest
|
12
|
+
desc "returns a successful response"
|
13
|
+
setup do
|
14
|
+
@fake_server.add_handler('v1', 'echo'){|params| [ 200, params['message'] ] }
|
15
|
+
end
|
16
|
+
|
17
|
+
should "get a 200 response with the parameter echoed back" do
|
18
|
+
self.run_fake_server(@fake_server) do
|
19
|
+
|
20
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
21
|
+
client.call('echo', :message => 'test') do |response|
|
22
|
+
assert_equal 200, response.status.code
|
23
|
+
assert_equal nil, response.status.message
|
24
|
+
assert_equal 'test', response.data
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class AuthorizeTest < MakingRequestsTest
|
33
|
+
setup do
|
34
|
+
@fake_server.add_handler('v1', 'authorize_it') do |params|
|
35
|
+
if params['api_key'] == 12345
|
36
|
+
[ 200, params['data'] ]
|
37
|
+
else
|
38
|
+
[ 401, params['data'] ]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
should "get a 200 response when api_key is passed with the correct value" do
|
44
|
+
self.run_fake_server(@fake_server) do
|
45
|
+
|
46
|
+
client = AndSon.new('localhost', 12000, 'v1').params({ 'api_key' => 12345 })
|
47
|
+
client.call('authorize_it', { 'data' => 'holla' }) do |response|
|
48
|
+
assert_equal 200, response.status.code
|
49
|
+
assert_equal nil, response.status.message
|
50
|
+
assert_equal 'holla', response.data
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "get a 401 response when api_key isn't passed" do
|
57
|
+
self.run_fake_server(@fake_server) do
|
58
|
+
|
59
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
60
|
+
client.call('authorize_it', { 'data' => 'holla' }) do |response|
|
61
|
+
assert_equal 401, response.status.code
|
62
|
+
assert_equal nil, response.status.message
|
63
|
+
assert_equal 'holla', response.data
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Failure400Test < MakingRequestsTest
|
71
|
+
desc "when a request fails with a 400"
|
72
|
+
setup do
|
73
|
+
@fake_server.add_handler('v1', '400'){|params| [ 400, false ] }
|
74
|
+
end
|
75
|
+
|
76
|
+
should "raise a bad request error" do
|
77
|
+
self.run_fake_server(@fake_server) do
|
78
|
+
|
79
|
+
assert_raises(AndSon::BadRequestError) do
|
80
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
81
|
+
client.call('400')
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Failure404Test < MakingRequestsTest
|
89
|
+
desc "when a request fails with a 404"
|
90
|
+
setup do
|
91
|
+
@fake_server.add_handler('v1', '404'){|params| [ 404, false ] }
|
92
|
+
end
|
93
|
+
|
94
|
+
should "raise a not found error" do
|
95
|
+
self.run_fake_server(@fake_server) do
|
96
|
+
|
97
|
+
assert_raises(AndSon::NotFoundError) do
|
98
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
99
|
+
client.call('404')
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Failure4xxTest < MakingRequestsTest
|
107
|
+
desc "when a request fails with a 4xx"
|
108
|
+
setup do
|
109
|
+
@fake_server.add_handler('v1', '4xx'){|params| [ 402, false ] }
|
110
|
+
end
|
111
|
+
|
112
|
+
should "raise a client error" do
|
113
|
+
self.run_fake_server(@fake_server) do
|
114
|
+
|
115
|
+
assert_raises(AndSon::ClientError) do
|
116
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
117
|
+
client.call('4xx')
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Failure5xxTest < MakingRequestsTest
|
125
|
+
desc "when a request fails with a 5xx"
|
126
|
+
setup do
|
127
|
+
@fake_server.add_handler('v1', '5xx'){|params| [ 500, false ] }
|
128
|
+
end
|
129
|
+
|
130
|
+
should "raise a server error" do
|
131
|
+
self.run_fake_server(@fake_server) do
|
132
|
+
|
133
|
+
assert_raises(AndSon::ServerError) do
|
134
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
135
|
+
client.call('5xx')
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class TimeoutErrorTest < MakingRequestsTest
|
143
|
+
desc "when a request takes to long to respond"
|
144
|
+
setup do
|
145
|
+
@fake_server.add_handler('v1', 'forever') do |params|
|
146
|
+
sleep 0.2
|
147
|
+
[ 200, true ]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
should "raise a timeout error" do
|
152
|
+
self.run_fake_server(@fake_server) do
|
153
|
+
|
154
|
+
assert_raises(Sanford::Protocol::TimeoutError) do
|
155
|
+
client = AndSon.new('localhost', 12000, 'v1')
|
156
|
+
client.timeout(0.1).call('forever')
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
class AndSon::Client
|
4
|
+
|
5
|
+
class BaseTest < Assert::Context
|
6
|
+
desc "AndSon::Client"
|
7
|
+
setup do
|
8
|
+
@host, @port, @version = '0.0.0.0', 8000, "v1"
|
9
|
+
@client = AndSon::Client.new(@host, @port, @version)
|
10
|
+
end
|
11
|
+
subject{ @client }
|
12
|
+
|
13
|
+
should have_imeths :host, :port, :version
|
14
|
+
should have_imeths :call_runner, :call, :timeout
|
15
|
+
|
16
|
+
should "know its default call runner" do
|
17
|
+
default_runner = subject.call_runner
|
18
|
+
|
19
|
+
assert_equal @host, default_runner.host
|
20
|
+
assert_equal @port, default_runner.port
|
21
|
+
assert_equal @version, default_runner.version
|
22
|
+
assert_equal 60.0, default_runner.timeout_value
|
23
|
+
end
|
24
|
+
|
25
|
+
should "override the default call runner timeout with an env var" do
|
26
|
+
prev = ENV['ANDSON_TIMEOUT']
|
27
|
+
ENV['ANDSON_TIMEOUT'] = '20'
|
28
|
+
|
29
|
+
assert_equal 20.0, subject.call_runner.timeout_value
|
30
|
+
|
31
|
+
ENV['ANDSON_TIMEOUT'] = prev
|
32
|
+
end
|
33
|
+
|
34
|
+
should "return a CallRunner with a timeout value set #timeout" do
|
35
|
+
runner = subject.timeout(10)
|
36
|
+
|
37
|
+
assert_kind_of AndSon::CallRunner, runner
|
38
|
+
assert_respond_to :call, runner
|
39
|
+
assert_equal 10.0, runner.timeout_value
|
40
|
+
end
|
41
|
+
|
42
|
+
should "return a CallRunner with params_value set using #params" do
|
43
|
+
runner = subject.params({ :api_key => 12345 })
|
44
|
+
|
45
|
+
assert_kind_of AndSon::CallRunner, runner
|
46
|
+
assert_respond_to :call, runner
|
47
|
+
assert_equal({ :api_key => 12345 }, runner.params_value)
|
48
|
+
end
|
49
|
+
|
50
|
+
should "raise an ArgumentError when #params is not passed a Hash" do
|
51
|
+
assert_raises(ArgumentError) do
|
52
|
+
subject.params('test')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "raise an ArgumentError when #call is not passed a Hash for params" do
|
57
|
+
runner = subject.timeout(0.1) # in case it actually tries to make the request
|
58
|
+
|
59
|
+
assert_raises(ArgumentError) do
|
60
|
+
runner.call('something', 'test')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# the `call` method is tested in the file test/system/making_requests_test.rb,
|
66
|
+
# because there is a lot of setup needed to call this method
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
class AndSon::Response
|
4
|
+
|
5
|
+
class BaseTest < Assert::Context
|
6
|
+
desc "AndSon::Response"
|
7
|
+
setup do
|
8
|
+
@protocol_response = Sanford::Protocol::Response.new([ 200, 'message' ], 'data')
|
9
|
+
@response = AndSon::Response.new(@protocol_response)
|
10
|
+
end
|
11
|
+
subject{ @response }
|
12
|
+
|
13
|
+
should have_instance_methods :data, :code_is_5xx?, :code_is_404?, :code_is_400?, :code_is_4xx?
|
14
|
+
should have_class_methods :parse
|
15
|
+
|
16
|
+
should "return the protocol response's data with #data" do
|
17
|
+
assert_equal @protocol_response.data, subject.data
|
18
|
+
end
|
19
|
+
should "return false for all the code_is methods" do
|
20
|
+
assert_equal false, subject.code_is_5xx?
|
21
|
+
assert_equal false, subject.code_is_404?
|
22
|
+
assert_equal false, subject.code_is_400?
|
23
|
+
assert_equal false, subject.code_is_4xx?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FailedResponseTest < BaseTest
|
28
|
+
desc "given a failed response"
|
29
|
+
setup do
|
30
|
+
@protocol_response = Sanford::Protocol::Response.new([ 500, 'message' ])
|
31
|
+
@response = AndSon::Response.new(@protocol_response)
|
32
|
+
end
|
33
|
+
|
34
|
+
should "raise an exception using the response's message and the exception should have the" \
|
35
|
+
"response as well" do
|
36
|
+
exception = nil
|
37
|
+
begin; subject.data; rescue Exception => exception; end
|
38
|
+
|
39
|
+
assert_instance_of AndSon::ServerError, exception
|
40
|
+
assert_equal @protocol_response.status.message, exception.message
|
41
|
+
assert_equal @protocol_response, exception.response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Response5xxTest < BaseTest
|
46
|
+
desc "given a 5xx response"
|
47
|
+
setup do
|
48
|
+
@protocol_response = Sanford::Protocol::Response.new(500)
|
49
|
+
@response = AndSon::Response.new(@protocol_response)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "return true with code_is_5xx? and false for all the other code_is methods" do
|
53
|
+
assert_equal true, subject.code_is_5xx?
|
54
|
+
assert_equal false, subject.code_is_404?
|
55
|
+
assert_equal false, subject.code_is_400?
|
56
|
+
assert_equal false, subject.code_is_4xx?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Response404Test < BaseTest
|
61
|
+
desc "given a 404 response"
|
62
|
+
setup do
|
63
|
+
@protocol_response = Sanford::Protocol::Response.new(404)
|
64
|
+
@response = AndSon::Response.new(@protocol_response)
|
65
|
+
end
|
66
|
+
|
67
|
+
should "return true with code_is_404? and code_is_4xx?; false for all the other " \
|
68
|
+
"code_is methods" do
|
69
|
+
assert_equal false, subject.code_is_5xx?
|
70
|
+
assert_equal true, subject.code_is_404?
|
71
|
+
assert_equal false, subject.code_is_400?
|
72
|
+
assert_equal true, subject.code_is_4xx?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Response400Test < BaseTest
|
77
|
+
desc "given a 400 response"
|
78
|
+
setup do
|
79
|
+
@protocol_response = Sanford::Protocol::Response.new(400)
|
80
|
+
@response = AndSon::Response.new(@protocol_response)
|
81
|
+
end
|
82
|
+
|
83
|
+
should "return true with code_is_400? and code_is_4xx?; false for all the other " \
|
84
|
+
"code_is methods" do
|
85
|
+
assert_equal false, subject.code_is_5xx?
|
86
|
+
assert_equal false, subject.code_is_404?
|
87
|
+
assert_equal true, subject.code_is_400?
|
88
|
+
assert_equal true, subject.code_is_4xx?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Response4xxTest < BaseTest
|
93
|
+
desc "given a 4xx response"
|
94
|
+
setup do
|
95
|
+
@protocol_response = Sanford::Protocol::Response.new(402)
|
96
|
+
@response = AndSon::Response.new(@protocol_response)
|
97
|
+
end
|
98
|
+
|
99
|
+
should "return true with code_is_4xx? and false for all the other code_is methods" do
|
100
|
+
assert_equal false, subject.code_is_5xx?
|
101
|
+
assert_equal false, subject.code_is_404?
|
102
|
+
assert_equal false, subject.code_is_400?
|
103
|
+
assert_equal true, subject.code_is_4xx?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: and-son
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Collin Redding
|
14
|
+
- Kelly Redding
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2012-11-28 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 4
|
32
|
+
version: "0.4"
|
33
|
+
requirement: *id001
|
34
|
+
name: sanford-protocol
|
35
|
+
type: :runtime
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 15
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 0
|
47
|
+
version: "1.0"
|
48
|
+
requirement: *id002
|
49
|
+
name: assert
|
50
|
+
type: :development
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 15
|
59
|
+
segments:
|
60
|
+
- 1
|
61
|
+
- 0
|
62
|
+
version: "1.0"
|
63
|
+
requirement: *id003
|
64
|
+
name: assert-mocha
|
65
|
+
type: :development
|
66
|
+
description: Simple Sanford client for Ruby.
|
67
|
+
email:
|
68
|
+
- collin.redding@me.com
|
69
|
+
- kelly@kellyredding.com
|
70
|
+
executables: []
|
71
|
+
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files: []
|
75
|
+
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- and-son.gemspec
|
83
|
+
- lib/and-son.rb
|
84
|
+
- lib/and-son/client.rb
|
85
|
+
- lib/and-son/connection.rb
|
86
|
+
- lib/and-son/exceptions.rb
|
87
|
+
- lib/and-son/response.rb
|
88
|
+
- lib/and-son/version.rb
|
89
|
+
- test/helper.rb
|
90
|
+
- test/support/fake_server.rb
|
91
|
+
- test/system/making_requests_test.rb
|
92
|
+
- test/unit/and-son_test.rb
|
93
|
+
- test/unit/client_test.rb
|
94
|
+
- test/unit/response_test.rb
|
95
|
+
homepage: https://github.com/redding/and-son
|
96
|
+
licenses: []
|
97
|
+
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.8.15
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Simple Sanford client for Ruby.
|
128
|
+
test_files:
|
129
|
+
- test/helper.rb
|
130
|
+
- test/support/fake_server.rb
|
131
|
+
- test/system/making_requests_test.rb
|
132
|
+
- test/unit/and-son_test.rb
|
133
|
+
- test/unit/client_test.rb
|
134
|
+
- test/unit/response_test.rb
|