Skip to content

Commit 9c41e84

Browse files
author
Will Myers
authored
Add client config options (#3)
* Add client config options * Bump version to 1.1.0 for new features
1 parent bd1b0de commit 9c41e84

File tree

7 files changed

+128
-23
lines changed

7 files changed

+128
-23
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ puts response.to_hash()
5353

5454
n.b. the keys of the response hash will always be symbols.
5555

56+
## Configuration
57+
58+
You may optionally supply a config argument with your API key:
59+
60+
```ruby
61+
require 'button'
62+
63+
client = Button::Client.new('sk-XXX', {
64+
hostname: 'api.testsite.com',
65+
port: 3000,
66+
secure: false,
67+
timeout: 5 # seconds
68+
})
69+
```
70+
71+
The supported options are as follows:
72+
73+
* `hostname`: Defaults to `api.usebutton.com`.
74+
* `port`: Defaults to `443` if `config.secure`, else defaults to `80`.
75+
* `secure`: Whether or not to use HTTPS. Defaults to True. **N.B: Button's API is only exposed through HTTPS. This option is provided purely as a convenience for testing and development.**
76+
* `timeout`: The time in seconds that may elapse before network requests abort. Defaults to `nil`.
77+
5678
## Resources
5779

5880
We currently expose only one resource to manage, `Orders`.

lib/button/client.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,28 @@ module Button
1616
# puts client.orders.get("btnorder-XXX")
1717
#
1818
class Client
19-
def initialize(api_key)
19+
def initialize(api_key, config = {})
2020
if api_key.nil? || api_key.empty?
2121
raise ButtonClientError, NO_API_KEY_MESSAGE
2222
end
2323

24-
@orders = Orders.new(api_key)
24+
config_with_defaults = merge_defaults(config)
25+
26+
@orders = Orders.new(api_key, config_with_defaults)
27+
end
28+
29+
def merge_defaults(config)
30+
secure = config.fetch(:secure, true)
31+
32+
return {
33+
secure: secure,
34+
timeout: config.fetch(:timeout, nil),
35+
hostname: config.fetch(:hostname, 'api.usebutton.com'),
36+
port: config.fetch(:port, secure ? 443 : 80)
37+
}
2538
end
2639

2740
attr_reader :orders
41+
private :merge_defaults
2842
end
2943
end

lib/button/resources/resource.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ module Button
2424
# end
2525
#
2626
class Resource
27-
HOST = 'api.usebutton.com'.freeze
28-
PORT = 443
2927
USER_AGENT = "Button/#{Button::VERSION} ruby/#{RUBY_VERSION}".freeze
3028

31-
def initialize(api_key)
29+
def initialize(api_key, config)
3230
@api_key = api_key
33-
@http = Net::HTTP.new(HOST, PORT)
34-
@http.use_ssl = true
31+
@config = config
32+
@http = Net::HTTP.new(config[:hostname], config[:port])
33+
@http.use_ssl = config[:secure]
34+
35+
if not config[:timeout].nil?
36+
@http.read_timeout = config[:timeout]
37+
end
38+
end
39+
40+
def timeout
41+
@http.read_timeout
3542
end
3643

3744
# Performs an HTTP GET at the provided path.
@@ -96,6 +103,7 @@ def process_response(response)
96103
raise ButtonClientError, "Invalid response: #{parsed}"
97104
end
98105

106+
attr_accessor :config
99107
private :api_request, :process_response
100108
end
101109
end

lib/button/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Button
2-
VERSION = '1.0.0'.freeze
2+
VERSION = '1.1.0'.freeze
33
end

test/button/client_test.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,31 @@ def test_exposes_orders
1919
client = Button::Client.new('sk-XXX')
2020
assert_respond_to(client, :orders)
2121
end
22+
23+
def test_sets_default_config_parameters
24+
client = Button::Client.new('sk-XXX')
25+
assert_equal(client.orders.config, {
26+
hostname: 'api.usebutton.com',
27+
port: 443,
28+
secure: true,
29+
timeout: nil
30+
})
31+
end
32+
33+
def test_allows_config_overrides
34+
config = {
35+
hostname: 'localhost',
36+
port: 8080,
37+
secure: false,
38+
timeout: 20
39+
}
40+
41+
client = Button::Client.new('sk-XXX', config)
42+
assert_equal(client.orders.config, config)
43+
end
44+
45+
def test_sets_default_port_properly
46+
client = Button::Client.new('sk-XXX', secure: false)
47+
assert_equal(client.orders.config[:port], 80)
48+
end
2249
end

test/button/resources/orders_test.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
require File.expand_path('../../../test_helper', __FILE__)
22

33
class OrdersTest < Test::Unit::TestCase
4+
def setup
5+
@orders = Button::Orders.new('sk-XXX', {
6+
secure: true,
7+
timeout: nil,
8+
hostname: 'api.usebutton.com',
9+
port: 443
10+
})
11+
end
12+
413
def teardown
514
WebMock.reset!
615
end
@@ -10,7 +19,7 @@ def test_get
1019
.with(headers: { Authorization: 'Basic c2stWFhYOg==' })
1120
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
1221

13-
response = Button::Orders.new('sk-XXX').get('btnorder-XXX')
22+
response = @orders.get('btnorder-XXX')
1423
assert_equal(response.a, 1)
1524
end
1625

@@ -19,7 +28,7 @@ def test_create
1928
.with(body: '{"a":1}', headers: { Authorization: 'Basic c2stWFhYOg==' })
2029
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
2130

22-
response = Button::Orders.new('sk-XXX').create(a: 1)
31+
response = @orders.create(a: 1)
2332
assert_equal(response.a, 1)
2433
end
2534

@@ -28,7 +37,7 @@ def test_update
2837
.with(body: '{"a":1}', headers: { Authorization: 'Basic c2stWFhYOg==' })
2938
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
3039

31-
response = Button::Orders.new('sk-XXX').update('btnorder-XXX', a: 1)
40+
response = @orders.update('btnorder-XXX', a: 1)
3241
assert_equal(response.a, 1)
3342
end
3443

@@ -37,7 +46,7 @@ def test_delete
3746
.with(headers: { Authorization: 'Basic c2stWFhYOg==' })
3847
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": null }')
3948

40-
response = Button::Orders.new('sk-XXX').delete('btnorder-XXX')
49+
response = @orders.delete('btnorder-XXX')
4150
assert_equal(response.to_hash, {})
4251
end
4352
end

test/button/resources/resource_test.rb

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
require File.expand_path('../../../test_helper', __FILE__)
22

33
class ResourceTest < Test::Unit::TestCase
4+
def setup
5+
@resource = Button::Resource.new('sk-XXX', {
6+
secure: true,
7+
timeout: nil,
8+
hostname: 'api.usebutton.com',
9+
port: 443
10+
})
11+
end
12+
413
def teardown
514
WebMock.reset!
615
end
@@ -10,7 +19,7 @@ def test_raises_with_empty_response
1019
.to_return(status: 200, body: '')
1120

1221
assert_raises(Button::ButtonClientError) do
13-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
22+
@resource.api_get('/v1/bloop')
1423
end
1524
end
1625

@@ -19,7 +28,7 @@ def test_raises_with_nil_response
1928
.to_return(status: 200, body: nil)
2029

2130
assert_raises(Button::ButtonClientError) do
22-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
31+
@resource.api_get('/v1/bloop')
2332
end
2433
end
2534

@@ -28,7 +37,7 @@ def test_raises_with_invalid_json_response
2837
.to_return(status: 200, body: 'invalid json')
2938

3039
assert_raises(Button::ButtonClientError) do
31-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
40+
@resource.api_get('/v1/bloop')
3241
end
3342
end
3443

@@ -37,7 +46,7 @@ def test_raises_with_a_server_error
3746
.to_return(status: 404, body: '{ "meta": { "status": "error" }, "error": { "message": "bloop" } }')
3847

3948
assert_raises(Button::ButtonClientError.new('bloop')) do
40-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
49+
@resource.api_get('/v1/bloop')
4150
end
4251
end
4352

@@ -46,7 +55,7 @@ def test_raises_with_an_unknown_status_error
4655
.to_return(status: 404, body: '{ "meta": { "status": "wat" }, "error": { "message": "bloop" } }')
4756

4857
assert_raises(Button::ButtonClientError.new('Unknown status: wat')) do
49-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
58+
@resource.api_get('/v1/bloop')
5059
end
5160
end
5261

@@ -55,7 +64,7 @@ def test_raises_if_receives_unknown_error_response
5564
.to_return(status: 404, body: '{}')
5665

5766
assert_raises(Button::ButtonClientError) do
58-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
67+
@resource.api_get('/v1/bloop')
5968
end
6069

6170
WebMock.reset!
@@ -64,7 +73,7 @@ def test_raises_if_receives_unknown_error_response
6473
.to_return(status: 404, body: '{ "meta": "wat" }')
6574

6675
assert_raises(Button::ButtonClientError) do
67-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
76+
@resource.api_get('/v1/bloop')
6877
end
6978

7079
WebMock.reset!
@@ -73,7 +82,7 @@ def test_raises_if_receives_unknown_error_response
7382
.to_return(status: 404, body: '{ "meta": { "status": "error" }, "error": "wat" }')
7483

7584
assert_raises(Button::ButtonClientError) do
76-
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
85+
@resource.api_get('/v1/bloop')
7786
end
7887

7988
WebMock.reset!
@@ -83,7 +92,7 @@ def test_gets_a_resource
8392
stub_request(:get, 'https://api.usebutton.com/v1/bloop')
8493
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
8594

86-
response = Button::Resource.new('sk-XXX').api_get('/v1/bloop')
95+
response = @resource.api_get('/v1/bloop')
8796
assert_equal(response.a, 1)
8897
end
8998

@@ -92,15 +101,31 @@ def test_posts_a_resource
92101
.with(body: '{"a":1}')
93102
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
94103

95-
response = Button::Resource.new('sk-XXX').api_post('/v1/bloop', a: 1)
104+
response = @resource.api_post('/v1/bloop', a: 1)
96105
assert_equal(response.a, 1)
97106
end
98107

99108
def test_deletes_a_resource
100109
stub_request(:delete, 'https://api.usebutton.com/v1/bloop/1')
101110
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": null }')
102111

103-
response = Button::Resource.new('sk-XXX').api_delete('/v1/bloop/1')
112+
response = @resource.api_delete('/v1/bloop/1')
104113
assert_equal(response.to_hash, {})
105114
end
115+
116+
def test_uses_config
117+
stub_request(:get, 'http://localhost:8080/v1/bloop')
118+
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')
119+
120+
resource = Button::Resource.new('sk-XXX', {
121+
secure: false,
122+
timeout: 1989,
123+
hostname: 'localhost',
124+
port: 8080
125+
})
126+
127+
response = resource.api_get('/v1/bloop')
128+
assert_equal(resource.timeout, 1989)
129+
assert_equal(response.a, 1)
130+
end
106131
end

0 commit comments

Comments
 (0)