vend 0.0.4

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.
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