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,25 @@
1
+ shared_examples_for "a logger" do
2
+ it { should respond_to(:debug) }
3
+ it { should respond_to(:info) }
4
+ it { should respond_to(:warn) }
5
+ it { should respond_to(:error) }
6
+ it { should respond_to(:fatal) }
7
+ end
8
+
9
+ shared_examples_for "it has a logger" do
10
+ describe "#logger" do
11
+
12
+ let(:logger) { mock("logger") }
13
+
14
+ it "defaults to a null logger" do
15
+ subject.logger.should be_instance_of(Vend::NullLogger)
16
+ end
17
+
18
+ it "allows the logger to be set" do
19
+ subject.logger = logger
20
+ subject.logger.should == logger
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::BaseFactory do
4
+ class Vend::Resource::FooFactory < Vend::BaseFactory #:nodoc:
5
+ end
6
+
7
+ class Vend::Resource::Foo < Vend::Base #:nodoc:
8
+ end
9
+
10
+ let(:client) { mock() }
11
+ subject { Vend::Resource::FooFactory.new(client, Vend::Resource::Foo) }
12
+
13
+ it "initializes correctly" do
14
+ subject.should be_instance_of(Vend::Resource::FooFactory)
15
+ subject.client.should == client
16
+ subject.target_class.should == Vend::Resource::Foo
17
+ end
18
+
19
+ it "proxies 'find' to the target class" do
20
+ Vend::Resource::Foo.should_receive(:find)
21
+ subject.find
22
+ end
23
+
24
+ it "proxies 'all' to the target class" do
25
+ Vend::Resource::Foo.should_receive(:all)
26
+ subject.all
27
+ end
28
+
29
+ it "proxies 'since' to the target class" do
30
+ Vend::Resource::Foo.should_receive(:since)
31
+ subject.since
32
+ end
33
+
34
+ it "proxies 'outlet_id' to the target class" do
35
+ Vend::Resource::Foo.should_receive(:outlet_id)
36
+ subject.outlet_id
37
+ end
38
+
39
+ it "proxies 'build' to the target class" do
40
+ Vend::Resource::Foo.should_receive(:build)
41
+ subject.build
42
+ end
43
+
44
+ it "returns the target class" do
45
+ subject.target_class.should == Vend::Resource::Foo
46
+ end
47
+
48
+ end
@@ -0,0 +1,348 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::Base do
4
+ class Vend::Resource::Foo < Vend::Base #:nodoc
5
+ url_scope :bar
6
+ end
7
+
8
+ let(:client) { mock(:client) }
9
+ let(:attribute_hash) { {:key => "value"} }
10
+ let(:mock_response) {
11
+ {
12
+ "foos"=>[
13
+ {"id"=>"1","bar"=>"baz"},
14
+ {"id"=>"2","flar"=>"flum"}
15
+ ]
16
+ }
17
+ }
18
+
19
+ subject { Vend::Resource::Foo.new(client, :attrs => attribute_hash) }
20
+
21
+ it "creates an instance of Foo" do
22
+ subject.should be_instance_of(Vend::Resource::Foo)
23
+ end
24
+
25
+ it "assigns the client" do
26
+ subject.client.should == client
27
+ end
28
+
29
+ it "assigns the attributes" do
30
+ subject.attrs.should == attribute_hash
31
+ end
32
+
33
+ describe '.build' do
34
+ it "builds a Foo" do
35
+ Vend::Resource::Foo.build(client, :attrs => attribute_hash).should
36
+ be_instance_of(Vend::Resource::Foo)
37
+ end
38
+ end
39
+
40
+ describe '.initialize_singular' do
41
+
42
+ it "initializes a singular resource from parsed JSON results" do
43
+ resource = Vend::Resource::Foo.initialize_singular(client,mock_response)
44
+ resource.should be_a Vend::Resource::Foo
45
+ resource.bar.should == "baz"
46
+ end
47
+
48
+ end
49
+
50
+ describe '.initialize_collection' do
51
+
52
+ subject { Vend::Resource::Foo }
53
+
54
+ let(:endpoint) { mock("endpoint") }
55
+ let(:args) { mock("args") }
56
+ let(:resource_collection) { mock("resource_collection") }
57
+
58
+ before do
59
+ Vend::ResourceCollection.should_receive(:new).with(
60
+ client, subject, endpoint, args
61
+ ) { resource_collection }
62
+ end
63
+
64
+ it "creates a ResourceCollection instance" do
65
+ subject.initialize_collection(
66
+ client, endpoint, args
67
+ ).should == resource_collection
68
+ end
69
+
70
+ end
71
+
72
+ describe '.endpoint_name' do
73
+
74
+ it "returns the endpoint name" do
75
+ Vend::Resource::Foo.endpoint_name.should == 'foo'
76
+ end
77
+
78
+ end
79
+
80
+ describe '.collection_name' do
81
+
82
+ it "returns the collection name" do
83
+ Vend::Resource::Foo.collection_name.should == 'foos'
84
+ end
85
+
86
+ end
87
+
88
+ describe '.singular_name' do
89
+
90
+ it "returns the collection name plus the id" do
91
+ Vend::Resource::Foo.singular_name("1").should == 'foos/1'
92
+ Vend::Resource::Foo.singular_name(42).should == 'foos/42'
93
+ end
94
+
95
+ end
96
+
97
+ describe "#singular_name" do
98
+
99
+ let(:singular_name) { mock("singular_name")}
100
+ let(:id) { 42 }
101
+
102
+ before do
103
+ subject.stub(:id => id)
104
+ described_class.stub(:singular_name).with(id) { singular_name }
105
+ end
106
+
107
+ its(:singular_name) { should == singular_name }
108
+
109
+ end
110
+
111
+ describe '.find' do
112
+
113
+ let(:id) { 1 }
114
+ let(:singular_name) { "foos/1" }
115
+
116
+ before do
117
+ Vend::Resource::Foo.stub(:singular_name).with(id) { singular_name }
118
+ end
119
+
120
+ it "finds a Foo by id" do
121
+ mock_response = {"foos"=>[{"id"=>"1","bar"=>"baz"}]}
122
+ client.should_receive(:request).with(singular_name) { mock_response }
123
+ foo = Vend::Resource::Foo.find(client, id)
124
+ foo.should be_instance_of(Vend::Resource::Foo)
125
+ foo.bar.should == "baz"
126
+ end
127
+
128
+ end
129
+
130
+ describe '.all' do
131
+
132
+ subject { Vend::Resource::Foo }
133
+
134
+ let(:collection_name) { mock("collection_name") }
135
+ let(:resource_collection) { mock("resource_collection") }
136
+
137
+ before do
138
+ subject.stub(:collection_name => collection_name)
139
+ end
140
+
141
+ it "calls initialize_collection with the collection_name" do
142
+ subject.should_receive(:initialize_collection).with(
143
+ client, collection_name
144
+ ) { resource_collection }
145
+ subject.all(client).should == resource_collection
146
+ end
147
+
148
+ end
149
+
150
+ describe '.url_scope' do
151
+
152
+ subject { Vend::Resource::Foo }
153
+
154
+ let(:collection_name) { mock("collection_name") }
155
+ let(:resource_collection) { mock("resource_collection") }
156
+ let(:bar) { mock("bar") }
157
+
158
+ before do
159
+ subject.stub(:collection_name => collection_name)
160
+ end
161
+
162
+ it "calls initialize_collection with collection_name and :bar arg" do
163
+ subject.should_receive(:initialize_collection).with(
164
+ client, collection_name
165
+ ) { resource_collection }
166
+ resource_collection.should_receive(:scope).with(:bar, bar) {
167
+ resource_collection
168
+ }
169
+ subject.bar(client, bar).should == resource_collection
170
+ end
171
+
172
+ end
173
+
174
+ describe ".search" do
175
+
176
+ subject { Vend::Resource::Foo }
177
+
178
+ let(:collection_name) { mock("collection_name") }
179
+ let(:resource_collection) { mock("resource_collection") }
180
+ let(:field) { "field" }
181
+ let(:query) { "query" }
182
+
183
+ before do
184
+ subject.stub(:collection_name => collection_name)
185
+ end
186
+
187
+ it "calls initialize_collection with collection_name and :outlet_id arg" do
188
+ subject.should_receive(:initialize_collection).with(
189
+ client, collection_name, :url_params => { field.to_sym => query }
190
+ ) { resource_collection }
191
+ subject.search(client, field, query).should == resource_collection
192
+ end
193
+
194
+ end
195
+
196
+ describe ".build_from_json" do
197
+
198
+ subject { Vend::Resource::Foo }
199
+
200
+ let(:json) { {"foos" => attributes_array} }
201
+ let(:attributes_one) { mock("attributes_one") }
202
+ let(:attributes_two) { mock("attributes_two") }
203
+ let(:attributes_array) { [attributes_one, attributes_two] }
204
+ let(:instance_one) { mock("instance_one") }
205
+ let(:instance_two) { mock("instance_two") }
206
+
207
+ specify do
208
+ subject.stub(:build).with(client, attributes_one) { instance_one }
209
+ subject.stub(:build).with(client, attributes_two) { instance_two }
210
+ subject.build_from_json(client, json).should == [
211
+ instance_one, instance_two
212
+ ]
213
+ end
214
+
215
+ end
216
+
217
+ describe "dynamic instance methods" do
218
+ let(:attrs) { { "one" => "foo", "two" => "bar", "object_id" => "fail" } }
219
+ subject { Vend::Resource::Foo.new(client, :attrs => attrs) }
220
+
221
+ it "responds to top level attributes" do
222
+ subject.should respond_to(:one)
223
+ subject.should respond_to(:two)
224
+ subject.should respond_to(:object_id)
225
+
226
+ subject.one.should == "foo"
227
+ subject.two.should == "bar"
228
+ subject.object_id.should_not == "fail"
229
+ subject.attrs['object_id'].should == "fail"
230
+ end
231
+ end
232
+
233
+ describe "delete!" do
234
+
235
+ context "when id is present" do
236
+ subject { Vend::Resource::Foo.new(client, :attrs => {'id' => 1}) }
237
+
238
+ let(:singular_name) { mock("singular_name")}
239
+
240
+ before do
241
+ subject.stub(:singular_name => singular_name)
242
+ end
243
+
244
+ it "deletes the object" do
245
+ client.should_receive(:request).with(singular_name, :method => :delete)
246
+ subject.delete!
247
+ end
248
+ end
249
+
250
+ context "when id is absent" do
251
+ subject { Vend::Resource::Foo.new(client, :attrs => {:foo => 'bar'}) }
252
+
253
+ it "raises Vend::Resource::IllegalAction" do
254
+ client.should_not_receive(:request)
255
+ expect {
256
+ subject.delete!
257
+ }.to raise_error(Vend::Resource::IllegalAction, "Vend::Resource::Foo has no unique ID")
258
+ end
259
+ end
260
+
261
+ end
262
+
263
+ describe 'delete' do
264
+
265
+ it "returns false when no id is present" do
266
+ objekt = Vend::Resource::Foo.new(client, :attrs => {:foo => 'bar'})
267
+ client.should_not_receive(:request)
268
+ objekt.delete.should be_false
269
+ end
270
+
271
+ end
272
+
273
+ describe ".paginates?" do
274
+
275
+ subject { Vend::Resource::Foo }
276
+
277
+ it "defaults to false" do
278
+ subject.paginates?.should be_false
279
+ end
280
+
281
+ end
282
+
283
+ describe ".available_scopes" do
284
+ subject { Vend::Resource::Foo }
285
+ its(:available_scopes) { should == [:bar] }
286
+ end
287
+
288
+ describe ".accepts_scope?" do
289
+ let(:scope_name) {:scope_name}
290
+ subject { Vend::Resource::Foo }
291
+
292
+ context "when scope is accepted" do
293
+ before do
294
+ subject.stub(:available_scopes => [scope_name])
295
+ end
296
+ specify do
297
+ subject.accepts_scope?(scope_name).should be_true
298
+ end
299
+ end
300
+
301
+ context "when scope is not accepted" do
302
+ before do
303
+ subject.stub(:available_scopes => [])
304
+ end
305
+ specify do
306
+ subject.accepts_scope?(scope_name).should be_false
307
+ end
308
+ end
309
+ end
310
+
311
+ describe ".findable_by" do
312
+ subject { Vend::Resource::Foo }
313
+
314
+ let(:args) { mock("args") }
315
+
316
+ it "creates a find_by_foo method on the class" do
317
+ subject.should_not respond_to(:find_by_foo)
318
+ subject.findable_by(:foo)
319
+ subject.should respond_to(:find_by_foo)
320
+ end
321
+
322
+ it "proxies to search" do
323
+ subject.findable_by(:foo)
324
+ subject.should_receive(:search).with(client, :foo, args)
325
+ subject.find_by_foo(client, args)
326
+ end
327
+
328
+ it "allows a different method name" do
329
+ subject.findable_by(:foo, :as => :bar)
330
+ subject.should_receive(:search).with(client, :bar, args)
331
+ subject.find_by_foo(client, args)
332
+ end
333
+
334
+ end
335
+
336
+ describe ".cast_attribute" do
337
+
338
+ subject { Vend::Resource::Foo }
339
+
340
+ let(:attrs) { {'floater' => '1.23'} }
341
+
342
+ it "casts to float" do
343
+ subject.cast_attribute :floater, Float
344
+ foo = Vend::Resource::Foo.new(client, :attrs => attrs)
345
+ foo.floater.should == 1.23
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vend::Client do
4
+
5
+ subject { Vend::Client.new('store','user','password') }
6
+
7
+ it_should_behave_like "it has a logger"
8
+
9
+ its(:store) { should == 'store' }
10
+ its(:username) { should == 'user' }
11
+ its(:password) { should == 'password' }
12
+
13
+ it "creates an instance of Client" do
14
+ subject.should be_instance_of(Vend::Client)
15
+ end
16
+
17
+ it "returns the API base url" do
18
+ subject.base_url.should == "https://store.vendhq.com/api/"
19
+ end
20
+
21
+ it "should set options" do
22
+ options = { :key => :value }
23
+ client = Vend::Client.new('store','user','password', options)
24
+ options.each do |key, value|
25
+ client.options[key].should == value
26
+ end
27
+ end
28
+
29
+ describe "resource factories" do
30
+ it "gets all products" do
31
+ Vend::Resource::Product.should_receive(:all).and_return([])
32
+ subject.Product.all.should == []
33
+ end
34
+ end
35
+
36
+ describe "#http_client" do
37
+
38
+ let(:http_client) { mock("http_client") }
39
+ let(:http_client_options) { mock("http_client_options") }
40
+
41
+ before do
42
+ subject.stub(:http_client_options => http_client_options)
43
+ Vend::HttpClient.should_receive(:new).with(http_client_options) { http_client }
44
+ end
45
+
46
+ it "should return a memoized HttpClient instance" do
47
+ subject.http_client.should == http_client
48
+ subject.http_client.should == http_client
49
+ end
50
+
51
+ end
52
+
53
+ describe "#http_client_options" do
54
+
55
+ let(:options) { {:foo => 'bar'} }
56
+ let(:base_url) { "http://foo/" }
57
+ let(:username) { "username" }
58
+ let(:password) { "password" }
59
+
60
+ before do
61
+ subject.stub(
62
+ :options => options,
63
+ :base_url => base_url,
64
+ :username => username,
65
+ :password => password
66
+ )
67
+ end
68
+
69
+ its(:http_client_options) {
70
+ should == {
71
+ :foo => 'bar',
72
+ :base_url => base_url,
73
+ :username => username,
74
+ :password => password
75
+ }
76
+ }
77
+ end
78
+
79
+ describe "#request" do
80
+
81
+ let(:response) { mock("response") }
82
+ let(:http_client) { mock("http_client") }
83
+
84
+ before do
85
+ subject.stub(:http_client => http_client)
86
+ http_client.should_receive(:request).with("foo", "bar") { response }
87
+ end
88
+
89
+ it "delegates to the http_client" do
90
+ subject.request("foo", "bar").should == response
91
+ end
92
+ end
93
+ end