vend 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +6 -0
  4. data/README.rdoc +8 -0
  5. data/Rakefile +18 -0
  6. data/example.rb +51 -0
  7. data/lib/vend.rb +24 -0
  8. data/lib/vend/base.rb +200 -0
  9. data/lib/vend/base_factory.rb +34 -0
  10. data/lib/vend/client.rb +82 -0
  11. data/lib/vend/exception.rb +35 -0
  12. data/lib/vend/http_client.rb +119 -0
  13. data/lib/vend/logable.rb +9 -0
  14. data/lib/vend/null_logger.rb +9 -0
  15. data/lib/vend/pagination_info.rb +30 -0
  16. data/lib/vend/resource/customer.rb +11 -0
  17. data/lib/vend/resource/outlet.rb +7 -0
  18. data/lib/vend/resource/payment_type.rb +7 -0
  19. data/lib/vend/resource/product.rb +12 -0
  20. data/lib/vend/resource/register.rb +7 -0
  21. data/lib/vend/resource/register_sale.rb +19 -0
  22. data/lib/vend/resource/register_sale_product.rb +21 -0
  23. data/lib/vend/resource/tax.rb +7 -0
  24. data/lib/vend/resource/user.rb +7 -0
  25. data/lib/vend/resource_collection.rb +132 -0
  26. data/lib/vend/scope.rb +27 -0
  27. data/lib/vend/version.rb +3 -0
  28. data/spec/integration/customer_spec.rb +33 -0
  29. data/spec/integration/outlet_spec.rb +15 -0
  30. data/spec/integration/payment_types_spec.rb +15 -0
  31. data/spec/integration/product_spec.rb +76 -0
  32. data/spec/integration/register_sale_spec.rb +35 -0
  33. data/spec/integration/register_spec.rb +16 -0
  34. data/spec/integration/tax_spec.rb +16 -0
  35. data/spec/integration/user_spec.rb +16 -0
  36. data/spec/mock_responses/customers.find_by_email.json +44 -0
  37. data/spec/mock_responses/customers.json +20 -0
  38. data/spec/mock_responses/outlets.json +14 -0
  39. data/spec/mock_responses/payment_types.json +7 -0
  40. data/spec/mock_responses/products.active.since.json +184 -0
  41. data/spec/mock_responses/products.json +115 -0
  42. data/spec/mock_responses/products/page/1.json +130 -0
  43. data/spec/mock_responses/products/page/2.json +130 -0
  44. data/spec/mock_responses/register_sales.find_by_state.json +324 -0
  45. data/spec/mock_responses/register_sales.json +158 -0
  46. data/spec/mock_responses/register_sales/2e658bce-9627-bc27-d77d-6c9ba2e8216e.json +158 -0
  47. data/spec/mock_responses/registers.json +32 -0
  48. data/spec/mock_responses/taxes.json +7 -0
  49. data/spec/mock_responses/users.json +17 -0
  50. data/spec/spec_helper.rb +16 -0
  51. data/spec/support/matchers/have_attributes.rb +11 -0
  52. data/spec/support/shared_examples/integration.rb +79 -0
  53. data/spec/support/shared_examples/logger.rb +25 -0
  54. data/spec/vend/base_factory_spec.rb +48 -0
  55. data/spec/vend/base_spec.rb +348 -0
  56. data/spec/vend/client_spec.rb +93 -0
  57. data/spec/vend/http_client_spec.rb +129 -0
  58. data/spec/vend/null_logger_spec.rb +5 -0
  59. data/spec/vend/pagination_info_spec.rb +48 -0
  60. data/spec/vend/resource/register_sale_product_spec.rb +27 -0
  61. data/spec/vend/resource/register_sale_spec.rb +24 -0
  62. data/spec/vend/resource_collection_spec.rb +312 -0
  63. data/spec/vend/scope_spec.rb +41 -0
  64. data/vend.gemspec +26 -0
  65. metadata +179 -0
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::HttpClient do
4
+
5
+ let(:base_url) { "https://foo/bar/" }
6
+ let(:username) { "username" }
7
+ let(:password) { "password" }
8
+ let(:options) {
9
+ {:base_url => base_url, :username => username, :password => password}
10
+ }
11
+
12
+ subject {
13
+ described_class.new(options)
14
+ }
15
+
16
+ it_should_behave_like "it has a logger"
17
+
18
+ its(:base_url) { should == base_url }
19
+ its(:username) { should == username }
20
+ its(:password) { should == password }
21
+
22
+ describe "#verify_ssl?" do
23
+ its(:verify_ssl) { should be_true }
24
+
25
+ context "when overridden in the options" do
26
+ subject { described_class.new(options.merge(:verify_ssl => false)) }
27
+ end
28
+ end
29
+
30
+ describe "#get_http_connection" do
31
+ let(:http) { mock("http") }
32
+ let(:verify_mode) { mock("verify_mode") }
33
+ let(:host) { "foo" }
34
+ let(:port) { 42 }
35
+
36
+ before do
37
+ subject.stub(:verify_mode => verify_mode)
38
+ http.should_receive(:use_ssl=).with(true)
39
+ http.should_receive(:verify_mode=).with(verify_mode)
40
+ Net::HTTP.stub(:new).with(host, port) { http }
41
+ end
42
+
43
+ it "returns the http_connection" do
44
+ subject.get_http_connection(host, port).should == http
45
+ end
46
+ end
47
+
48
+ describe "#verify_mode" do
49
+
50
+ context "when verify_ssl? is true" do
51
+ before do
52
+ subject.stub(:verify_ssl? => true)
53
+ end
54
+ its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER }
55
+ end
56
+
57
+ context "when verify_ssl? is false" do
58
+ before do
59
+ subject.stub(:verify_ssl? => false)
60
+ end
61
+ its(:verify_mode) { should == OpenSSL::SSL::VERIFY_NONE }
62
+ end
63
+
64
+ end
65
+
66
+ describe "#request" do
67
+
68
+ context "when using invalid credentials" do
69
+
70
+ let(:username) { "invalid" }
71
+
72
+ it "raises an error" do
73
+ stub_request(:get, "https://invalid:password@foo/bar/products").
74
+ to_return(:status => 401)
75
+
76
+ expect {
77
+ subject.request('products')
78
+ }.to raise_error(Vend::Unauthorized)
79
+ end
80
+
81
+ end
82
+
83
+ it "throws an error when an invalid request is made" do
84
+ stub_request(:get, "https://username:password@foo/bar/invalid").
85
+ to_return(:status => 404, :body => '{"foo":"bar"}', :headers => {})
86
+
87
+ expect {
88
+ subject.request('invalid')
89
+ }.to raise_error(Vend::HTTPError)
90
+ end
91
+
92
+ it "returns parsed JSON" do
93
+ stub_request(:get, "https://username:password@foo/bar/bun").
94
+ to_return(:status => 200, :body => '{"foo":"bar"}', :headers => {})
95
+ subject.request("bun").should == {"foo" => "bar"}
96
+ end
97
+
98
+ it "returns nil if the response was empty" do
99
+ stub_request(:get, "https://username:password@foo/bar/bun").
100
+ to_return(:status => 200, :body => '', :headers => {})
101
+ subject.request("bun").should be_nil
102
+ end
103
+ it "allows us to specify HTTP method" do
104
+ stub_request(:post, "https://username:password@foo/bar/foo").
105
+ to_return(:status => 200, :body => '{"foo":"bar"}', :headers => {})
106
+
107
+ response = subject.request('foo', :method => :post)
108
+ response.should == {"foo" => "bar"}
109
+ end
110
+
111
+ it "allows us to set a request body" do
112
+ stub_request(:post, "https://username:password@foo/bar/foo").
113
+ with(:body => "{\"post\":\"data\"}").
114
+ to_return(:status => 200, :body => '{"foo":"bar"}', :headers => {})
115
+
116
+ response = subject.request('foo', :method => :post, :body => '{"post":"data"}')
117
+ response.should == {"foo" => "bar"}
118
+ end
119
+
120
+ it "allows us to specify url parameters" do
121
+ stub_request(:get, "https://username:password@foo/bar/foo?foo=bar&baz=baloo&flum%5B0%5D=blob&flum%5B1%5D=splat").
122
+ to_return(:status => 200, :body => '{"foo":"bar"}', :headers => {})
123
+
124
+ response = subject.request('foo', :url_params => {:foo => "bar", :baz => "baloo", :flum => ["blob","splat"]})
125
+ response.should == {"foo" => "bar"}
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::NullLogger do
4
+ it_should_behave_like "a logger"
5
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::PaginationInfo do
4
+
5
+ subject { described_class.new(response) }
6
+
7
+ let(:response) { mock("response") }
8
+
9
+ its(:response) { should == response }
10
+
11
+ context "when response has pagination info" do
12
+ let(:response) {
13
+ {
14
+ "pagination" => {
15
+ "results" => 7461,
16
+ "page" => 41,
17
+ "page_size" => 100,
18
+ "pages" => 75
19
+ }
20
+ }
21
+ }
22
+
23
+ its(:pages) { should == 75 }
24
+ its(:page) { should == 41 }
25
+ it { should be_paged }
26
+
27
+ describe "#last_page?" do
28
+
29
+ it { should_not be_last_page }
30
+
31
+ context "when page is equal to pages" do
32
+ before do
33
+ subject.stub(:page => 42, :pages => 42)
34
+ end
35
+ it { should be_last_page }
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+ context "when response does not have pagination info" do
42
+ let(:response) { Hash.new }
43
+ its(:pages) { should == 1 }
44
+ its(:page) { should == 1 }
45
+ it { should be_last_page }
46
+ it { should_not be_paged }
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::Resource::RegisterSaleProduct do
4
+ let(:attrs) { mock("attrs") }
5
+
6
+ subject { described_class.new(attrs) }
7
+
8
+ its(:attrs) { should == attrs }
9
+
10
+ describe "provides an attr reader for the attributes in attrs" do
11
+ let(:attr1) { mock("attr1") }
12
+
13
+ let(:attrs) {
14
+ {
15
+ "attr1" => attr1
16
+ }
17
+ }
18
+
19
+ it "responds to attr1" do
20
+ subject.send(:attr1).should == attr1
21
+ end
22
+
23
+ it "does not respond to attr2" do
24
+ lambda { subject.send(:attr2) }.should raise_error NoMethodError
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::Resource::RegisterSale do
4
+
5
+ subject { described_class.new(nil, {}) }
6
+
7
+ describe "#register_sale_products" do
8
+ let(:register_sale_product) { mock("register sale product") }
9
+ let(:raw_register_sale_product) { mock("raw register sale prodcut") }
10
+
11
+ before do
12
+ subject.stub(:attrs) {
13
+ { "register_sale_products" => [raw_register_sale_product] }
14
+ }
15
+ Vend::Resource::RegisterSaleProduct.stub(:new).with(raw_register_sale_product) {
16
+ register_sale_product
17
+ }
18
+ end
19
+
20
+ it "returns all the register sale products" do
21
+ subject.register_sale_products.should == [register_sale_product]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,312 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::ResourceCollection do
4
+
5
+ let(:client) { mock("client") }
6
+ let(:target_class) { mock("target_class") }
7
+ let(:endpoint) { "endpoint" }
8
+ let(:request_args) { mock("request_args") }
9
+
10
+ subject { described_class.new(client, target_class, endpoint, request_args) }
11
+
12
+ its(:client) { should == client }
13
+ its(:target_class) { should == target_class }
14
+ its(:endpoint) { should == endpoint }
15
+ its(:scopes) { should == [] }
16
+
17
+ describe "#request_args" do
18
+
19
+ its(:request_args) { should == request_args }
20
+
21
+ context "when not set in initialize" do
22
+ subject {
23
+ described_class.new(client, target_class, endpoint)
24
+ }
25
+ its(:request_args) { should == {} }
26
+ end
27
+
28
+ end
29
+
30
+ describe "#each" do
31
+
32
+ let(:member_one) { mock("member_one") }
33
+ let(:member_two) { mock("member_two") }
34
+ let(:json) { mock("json") }
35
+ let(:next_page) { mock("next_page") }
36
+
37
+ before do
38
+ target_class.should_receive(:build_from_json).with(client, next_page) {
39
+ [member_one, member_two]
40
+ }
41
+ client.stub(:request).with(endpoint, request_args) { json }
42
+ subject.stub(:get_next_page) { next_page }
43
+ subject.stub(:last_page?).and_return(false, true)
44
+ end
45
+
46
+ it "yields each member and returns self" do
47
+ member_one.should_receive(:ping!)
48
+ member_two.should_receive(:ping!)
49
+ subject.each do |member|
50
+ member.ping!
51
+ end.should == subject
52
+ end
53
+
54
+ end
55
+
56
+ describe "#last_page?" do
57
+
58
+ context "when pagination is set" do
59
+ let(:value) { mock("value") }
60
+ let(:pagination) { mock("pagination", :last_page? => value) }
61
+ before do
62
+ subject.stub(:pagination => pagination)
63
+ end
64
+ it "delegates to #pagination" do
65
+ subject.last_page?.should == value
66
+ end
67
+ end
68
+
69
+ context "when pagination is nil" do
70
+ it { should_not be_last_page }
71
+ end
72
+
73
+ end
74
+
75
+ describe "#paged?" do
76
+
77
+ context "when pagination is set" do
78
+ let(:value) { mock("value") }
79
+ let(:pagination) { mock("pagination", :paged? => value) }
80
+ before do
81
+ subject.stub(:pagination => pagination)
82
+ end
83
+ it "delegates to #pagination" do
84
+ subject.paged?.should == value
85
+ end
86
+ end
87
+
88
+ context "when pagination is nil" do
89
+ specify do
90
+ subject.paged?.should be_false
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ [:pages, :page].each do |method|
97
+ describe method do
98
+ let(:value) { mock("value") }
99
+ let(:pagination) { mock("pagination", method => value) }
100
+ before do
101
+ subject.stub(:pagination => pagination)
102
+ end
103
+ it "delegates to #pagination" do
104
+ subject.send(method).should == value
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "#scope" do
110
+ let(:value) { mock("value") }
111
+ let(:scope) { mock("scope") }
112
+ let(:scopes) { mock("scopes") }
113
+
114
+ context "when scope is not already present" do
115
+ before do
116
+ subject.stub(:has_scope?).with(:name) { false }
117
+ subject.stub(:scopes => scopes)
118
+ Vend::Scope.stub(:new).with(:name, value) { scope }
119
+ scopes.should_receive(:<<).with(scope)
120
+ end
121
+ it "returns self" do
122
+ subject.scope(:name, value).should == subject
123
+ end
124
+ end
125
+
126
+ context "when already scoped" do
127
+ before do
128
+ subject.stub(:has_scope?).with(:name) { true }
129
+ end
130
+ it "raises and AlreadyScopedError" do
131
+ lambda do
132
+ subject.scope(:name, value)
133
+ end.should raise_exception(Vend::ResourceCollection::AlreadyScopedError)
134
+ end
135
+ end
136
+ end
137
+
138
+ describe "#has_scope?" do
139
+ context "when scope is not present" do
140
+ it { should_not have_scope(:name) }
141
+ end
142
+ context "when scope is present" do
143
+ let(:scope) { mock("scope", :name => :name) }
144
+ before do
145
+ subject.stub(:scopes => [scope])
146
+ end
147
+ it { should have_scope(:name) }
148
+ end
149
+ end
150
+
151
+ describe "#accepts_scope?" do
152
+ let(:value) { mock("value") }
153
+ before do
154
+ target_class.stub(:accepts_scope?).with(:name) { value }
155
+ end
156
+ it "delegates to target_class" do
157
+ subject.accepts_scope?(:name).should == value
158
+ end
159
+ end
160
+
161
+ describe "#method_missing" do
162
+
163
+ let(:value) { mock("value") }
164
+
165
+ context "when the method name is a valid scope name" do
166
+
167
+ before do
168
+ subject.stub(:accepts_scope?).with(:foo) { true }
169
+ end
170
+
171
+ it { should respond_to(:foo) }
172
+
173
+ it "adds the relevant scope if it accepts a scope for the method name" do
174
+ subject.should_receive(:scope).with(:foo, value) { subject }
175
+ subject.foo(value).should == subject
176
+ end
177
+
178
+ end
179
+
180
+ context "when the method name is not a valid scope name" do
181
+
182
+ before do
183
+ subject.stub(:accepts_scope?).with(:foo) { false }
184
+ end
185
+
186
+ it { should_not respond_to(:foo) }
187
+
188
+ it "raises method missing" do
189
+ lambda do
190
+ subject.foo(value)
191
+ end.should raise_exception(NoMethodError)
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+
198
+ describe "#url" do
199
+
200
+ let(:endpoint_with_scopes) { "endpoint_with_scopes" }
201
+
202
+ before do
203
+ subject.stub(:endpoint_with_scopes => endpoint_with_scopes)
204
+ subject.should_receive(:increment_page)
205
+ end
206
+
207
+ its(:url) { should == endpoint_with_scopes }
208
+ end
209
+
210
+ describe "#endpoint_with_scopes" do
211
+
212
+ context "when there are no scopes" do
213
+ its(:endpoint_with_scopes) { should == endpoint }
214
+ end
215
+
216
+ context "when there are scopes" do
217
+ let(:scope1) { "scope1" }
218
+ let(:scope2) { "scope2" }
219
+ let(:scopes) { [scope1, scope2] }
220
+ before do
221
+ subject.stub(:scopes => scopes)
222
+ end
223
+
224
+ its(:endpoint_with_scopes) { should == endpoint + scopes.join}
225
+ end
226
+ end
227
+
228
+ describe "#increment_page" do
229
+
230
+ context "when not paged" do
231
+
232
+ before do
233
+ subject.stub(:paged? => false)
234
+ end
235
+
236
+ its(:increment_page) { should be_nil }
237
+
238
+ end
239
+
240
+ context "when paged" do
241
+
242
+ let(:page_scope) { mock("page_scope", :value => 1) }
243
+
244
+ before do
245
+ subject.stub(:paged? => true)
246
+ subject.stub(:page => 1)
247
+ subject.should_receive(:get_or_create_page_scope) { page_scope }
248
+ page_scope.should_receive(:value=).with(2) { 2 }
249
+ end
250
+
251
+ its(:increment_page) { should == 2 }
252
+
253
+ end
254
+
255
+ end
256
+
257
+ describe "#get_or_create_page_scope" do
258
+
259
+ let(:page_scope) { mock("page_scope") }
260
+ let(:page) { 1 }
261
+
262
+ before do
263
+ subject.stub(:page => page)
264
+ subject.should_receive(:get_scope).with(:page) { page_scope }
265
+ end
266
+
267
+ context "when scope is not present" do
268
+ before do
269
+ subject.stub(:has_scope?).with(:page) { false }
270
+ subject.should_receive(:scope).with(:page, page) { page_scope }
271
+ end
272
+ its(:get_or_create_page_scope) { should == page_scope }
273
+ end
274
+
275
+ context "when scope is already present" do
276
+ before do
277
+ subject.stub(:has_scope?).with(:page) { true }
278
+ end
279
+ its(:get_or_create_page_scope) { should == page_scope }
280
+ end
281
+ end
282
+
283
+ describe "#get_scope" do
284
+
285
+ let(:scope_name) { :scope_name }
286
+ let(:scope) { mock("scope", :name => scope_name) }
287
+
288
+ context "when scope is present" do
289
+
290
+ before do
291
+ subject.stub(:scopes => [scope])
292
+ end
293
+
294
+ specify do
295
+ subject.get_scope(scope_name).should == scope
296
+ end
297
+
298
+ end
299
+
300
+ context "when scope is not present" do
301
+ before do
302
+ subject.stub(:scopes => [])
303
+ end
304
+ specify do
305
+ lambda do
306
+ subject.get_scope(scope_name)
307
+ end.should raise_exception(Vend::ResourceCollection::ScopeNotFoundError)
308
+ end
309
+ end
310
+
311
+ end
312
+ end