bol 0.0.1.beta

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 (46) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.md +1 -0
  3. data/Gemfile +2 -0
  4. data/Guardfile +7 -0
  5. data/LICENSE.md +19 -0
  6. data/README.md +170 -0
  7. data/Rakefile +6 -0
  8. data/bol.gemspec +25 -0
  9. data/lib/bol.rb +52 -0
  10. data/lib/bol/category.rb +5 -0
  11. data/lib/bol/configuration.rb +55 -0
  12. data/lib/bol/parser.rb +34 -0
  13. data/lib/bol/parsers.rb +7 -0
  14. data/lib/bol/parsers/categories.rb +17 -0
  15. data/lib/bol/parsers/products.rb +34 -0
  16. data/lib/bol/parsers/refinements.rb +24 -0
  17. data/lib/bol/product.rb +51 -0
  18. data/lib/bol/proxy.rb +37 -0
  19. data/lib/bol/query.rb +79 -0
  20. data/lib/bol/refinement.rb +5 -0
  21. data/lib/bol/refinement_group.rb +5 -0
  22. data/lib/bol/request.rb +90 -0
  23. data/lib/bol/requests.rb +7 -0
  24. data/lib/bol/requests/list.rb +26 -0
  25. data/lib/bol/requests/product.rb +16 -0
  26. data/lib/bol/requests/search.rb +11 -0
  27. data/lib/bol/scope.rb +55 -0
  28. data/lib/bol/signature.rb +44 -0
  29. data/lib/bol/version.rb +3 -0
  30. data/spec/bol/configuration_spec.rb +46 -0
  31. data/spec/bol/parsers/products_spec.rb +77 -0
  32. data/spec/bol/product_spec.rb +71 -0
  33. data/spec/bol/proxy_spec.rb +34 -0
  34. data/spec/bol/query_spec.rb +129 -0
  35. data/spec/bol/request_spec.rb +119 -0
  36. data/spec/bol/scope_spec.rb +84 -0
  37. data/spec/bol/signature_spec.rb +27 -0
  38. data/spec/bol_spec.rb +55 -0
  39. data/spec/fixtures/categorylist.xml +232 -0
  40. data/spec/fixtures/productlists.xml +164 -0
  41. data/spec/fixtures/products.xml +72 -0
  42. data/spec/fixtures/searchproducts-music.xml +108 -0
  43. data/spec/fixtures/searchproducts.xml +322 -0
  44. data/spec/spec_helper.rb +16 -0
  45. data/spec/support.rb +6 -0
  46. metadata +177 -0
@@ -0,0 +1,3 @@
1
+ module Bol
2
+ VERSION = '0.0.1.beta'
3
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Configuration do
4
+ describe 'as a hash' do
5
+ it 'should should set known keys' do
6
+ c = Bol::Configuration.new access_key: 'foo'
7
+ c[:access_key].should == 'foo'
8
+ end
9
+
10
+ it 'should raise for unknown keys' do
11
+ expect { Bol::Configuration.new foo: 'bar' }.to raise_error ArgumentError
12
+ end
13
+
14
+ it 'should set a single key' do
15
+ c = Bol::Configuration.new
16
+ c[:access_key] = 'bar'
17
+ c[:access_key].should == 'bar'
18
+ end
19
+ end
20
+
21
+ describe 'validation' do
22
+ it 'should raise error when not properly configured' do
23
+ c = Bol::Configuration.new
24
+ expect { c.validate }.to raise_error(Bol::ConfigurationError)
25
+ end
26
+
27
+ it 'should not raise error when all keys are configured' do
28
+ c = Bol::Configuration.new access_key: 'foo', secret: 'bar'
29
+ expect { c.validate }.to_not raise_error(Bol::ConfigurationError)
30
+ end
31
+ end
32
+
33
+ describe 'as attributes' do
34
+ let(:config) { Bol::Configuration.new }
35
+
36
+ it 'should should set known keys' do
37
+ config.access_key = 'bar'
38
+ config.access_key.should == 'bar'
39
+ end
40
+
41
+ it 'should raise for unknown keys' do
42
+ expect { config.foo = 'bar' }.to raise_error NoMethodError
43
+ expect { config.foo }.to raise_error NoMethodError
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Parsers::Products do
4
+ let(:request) do
5
+ double('request').tap do |r|
6
+ r.stub(:fetch).and_return(r)
7
+ r.stub(:body).and_return(body)
8
+ end
9
+ end
10
+ let(:product) { Bol::Parsers::Products.new(request).objects.first }
11
+
12
+ describe 'parsing products' do
13
+ let(:body) { fixture('products.xml') }
14
+
15
+ it 'should set simple attributes' do
16
+ product.id.should == '1001004006016448'
17
+ product.title.should == 'Harry Potter Boxed Set (Adult Edition)'
18
+ product.subtitle.should == 'Volume 1 - 7, paperback'
19
+ product.publisher.should == 'Bloomsbury Publishing PLC'
20
+ product.ean.should == '9780747595847'
21
+ product.attributes[:binding_description].should == 'Paperback'
22
+ product.language_code.should == 'en'
23
+ product.language_description.should == 'Engels'
24
+ product.url.should == 'http://www.bol.com/nl/p/engelse-boeken/harry-potter-boxed-set/1001004006016448/index.html'
25
+ product.rating.should == '5'
26
+ product.authors.should include 'J. K. Rowling'
27
+ product.authors.should include 'J.K. Rowling'
28
+ end
29
+
30
+ it 'should parse release date' do
31
+ product.release_date.year.should == 2008
32
+ product.release_date.month.should == 10
33
+ product.release_date.day.should == 1
34
+ end
35
+
36
+ it 'should parse cover' do
37
+ product.cover.should == 'http://s-bol.com/imgbase0/imagebase/thumb/FC/8/4/4/6/1001004006016448.jpg'
38
+ product.cover(:medium).should == 'http://s-bol.com/imgbase0/imagebase/thumb/FC/8/4/4/6/1001004006016448.jpg'
39
+ product.cover(:extra_small).should == 'http://s-bol.com/imgbase0/imagebase/mini/FC/8/4/4/6/1001004006016448.jpg'
40
+ product.cover(:small).should == 'http://s-bol.com/imgbase0/imagebase/tout/FC/8/4/4/6/1001004006016448.jpg'
41
+ product.cover(:large).should == 'http://s-bol.com/imgbase0/imagebase/regular/FC/8/4/4/6/1001004006016448.jpg'
42
+ product.cover(:extra_large).should == 'http://s-bol.com/imgbase0/imagebase/large/FC/8/4/4/6/1001004006016448.jpg'
43
+ end
44
+ end
45
+
46
+ describe 'parsing product search results' do
47
+ let(:body) { fixture('searchproducts-music.xml') }
48
+ let(:products) { Bol::Parsers::Products.new(request).objects }
49
+
50
+ it 'should parse into array of products' do
51
+ products.size.should == 2
52
+ products[0].should be_instance_of Bol::Product
53
+ products[1].should be_instance_of Bol::Product
54
+ end
55
+
56
+ it 'should have parsed xml into products' do
57
+ products[0].title.should == 'Glass: Violin Concerto, Company etc / Adele Anthony, Takuo Yuasa et al'
58
+ products[1].title.should == 'Adele'
59
+ end
60
+ end
61
+
62
+ describe 'parsing product listings' do
63
+ let(:body) { fixture('productlists.xml') }
64
+ let(:products) { Bol::Parsers::Products.new(request).objects }
65
+
66
+ it 'should parse into array of products' do
67
+ products.size.should == 2
68
+ products[0].should be_instance_of Bol::Product
69
+ products[1].should be_instance_of Bol::Product
70
+ end
71
+
72
+ it 'should have parsed xml into products' do
73
+ products[0].title.should == 'In mijn dromen'
74
+ products[1].title.should == 'Kort'
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Product do
4
+ describe '#find' do
5
+ let(:r) { Bol::Product.find(1) }
6
+
7
+ before do
8
+ Bol.configure access_key: 'foo', secret: 'bar'
9
+ FakeWeb.register_uri(:get, 'https://openapi.bol.com/openapi/services/rest/catalog/v3/products/1?categoryId=0', body: fixture('products.xml'))
10
+ end
11
+
12
+ it 'should return product instance' do
13
+ r.should be_instance_of Bol::Product
14
+ end
15
+
16
+ it 'should raise error when not found' do
17
+ FakeWeb.register_uri(:get, %r{https://openapi.bol.com}, status: ['404', 'Not found'])
18
+ expect { Bol::Product.find(999) }.to raise_error(Bol::NotFound)
19
+ end
20
+ end
21
+
22
+ describe 'attributes' do
23
+ it 'should delegate [] to attributes' do
24
+ product = Bol::Product.new
25
+ product.attributes[:foo] = 'bar'
26
+ product[:foo].should == 'bar'
27
+ end
28
+
29
+ it 'should expose attributes as methods' do
30
+ product = Bol::Product.new
31
+ product.attributes[:foo] = 'bar'
32
+ product.foo.should == 'bar'
33
+ end
34
+
35
+ describe 'referral_url' do
36
+ let(:product) { Bol::Product.new }
37
+
38
+ before do
39
+ product.attributes[:url] = 'http://foo.bar'
40
+ product.attributes[:id] = 'qux'
41
+ product.attributes[:title] = 'bla'
42
+ end
43
+
44
+ it 'should generate referral URL' do
45
+ product.referral_url('foo').should == "http://partnerprogramma.bol.com/click/click?p=1&t=url&s=foo&url=http%3A%2F%2Ffoo.bar&f=API&subid=qux&name=bla"
46
+ end
47
+ end
48
+
49
+ describe '#cover' do
50
+ let(:product) { Bol::Product.new }
51
+ before do
52
+ product.attributes[:cover] = {
53
+ medium: 'foo',
54
+ small: 'bar'
55
+ }
56
+ end
57
+
58
+ it 'should return medium by default' do
59
+ product.cover.should == 'foo'
60
+ end
61
+
62
+ it 'should take format as argument' do
63
+ product.cover(:small).should == 'bar'
64
+ end
65
+
66
+ it 'should raise on invalid format' do
67
+ expect { product.cover(:baz) }.to raise_error KeyError
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Proxy do
4
+ let(:parser) { double('parser') }
5
+ let(:query) { double('query', category_id: 1) }
6
+ let(:request) { double('request', query: query) }
7
+ let(:subject) { described_class.new(request, parser) }
8
+
9
+ it 'should delegate methods to query' do
10
+ subject.category_id.should == 1
11
+ end
12
+
13
+ it 'should delegate query' do
14
+ subject.query.should == query
15
+ end
16
+
17
+ it 'should delegate query methods' do
18
+ query.should_receive(:offset).with(5)
19
+ query.should_receive(:limit).with(10)
20
+ subject.offset(5)
21
+ subject.limit(10)
22
+ end
23
+
24
+ it 'should get objects from parser' do
25
+ parser.should_receive(:objects).and_return([])
26
+ subject.all
27
+ subject.all
28
+ end
29
+
30
+ it 'should get objects from parser when looping' do
31
+ parser.should_receive(:objects).and_return([])
32
+ subject.each {}
33
+ end
34
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Query do
4
+ let(:subject) { Bol::Query.new(0) }
5
+
6
+ describe 'category scope' do
7
+ it 'should set category scope' do
8
+ Bol::Query.new(1).category_id.should == 1
9
+ end
10
+
11
+ it 'should raise unless given a number' do
12
+ expect { Bol::Query.new('foo') }.to raise_error ArgumentError
13
+ end
14
+
15
+ it 'should set category param' do
16
+ Bol::Query.new(1).params[:categoryId].should == 1
17
+ end
18
+
19
+ it 'should accept multiple category IDs' do
20
+ expect { Bol::Query.new('1+2+3') }.to_not raise_error
21
+ end
22
+ end
23
+
24
+ describe '#has_param?' do
25
+ it { should have_param(:categoryId) }
26
+ it { should_not have_param(:term) }
27
+ it { should_not have_param(:foo) }
28
+ end
29
+
30
+ describe '#limit' do
31
+ context 'when set' do
32
+ let(:subject) { Bol::Query.new(0).limit('10') }
33
+ its(:limit) { should == 10 }
34
+ it { should have_param(:nrProducts) }
35
+ end
36
+
37
+ context "when not set" do
38
+ it { should_not have_param(:nrProducts) }
39
+ end
40
+ end
41
+
42
+ describe '#page' do
43
+ context 'page 2' do
44
+ let(:subject) { Bol::Query.new(0).page(2) }
45
+ it { should have_param(:nrProducts) }
46
+ it { should have_param(:offset) }
47
+ its(:limit) { should == 10 }
48
+ its(:offset) { should == 10 }
49
+ end
50
+
51
+ context 'defaults' do
52
+ let(:subject) { Bol::Query.new(0).page(nil) }
53
+ it { should have_param(:nrProducts) }
54
+ it { should have_param(:offset) }
55
+ its(:limit) { should == 10 }
56
+ its(:offset) { should == 0 }
57
+ end
58
+
59
+ context '20 per page' do
60
+ before { Bol.configuration[:per_page] = 20 }
61
+ after { Bol.configuration[:per_page] = 10 }
62
+ let(:subject) { Bol::Query.new(0).page(3) }
63
+ it { should have_param(:nrProducts) }
64
+ it { should have_param(:offset) }
65
+ its(:limit) { should == 20 }
66
+ its(:offset) { should == 40 }
67
+ end
68
+ end
69
+
70
+ describe '#offset' do
71
+ context 'when set' do
72
+ let(:subject) { Bol::Query.new(0).offset('10') }
73
+ its(:offset) { should == 10 }
74
+ it { should have_param(:offset) }
75
+ end
76
+
77
+ context 'when not set' do
78
+ it { should_not have_param(:offset) }
79
+ end
80
+ end
81
+
82
+ describe '#search' do
83
+ context 'when set' do
84
+ let(:subject) { Bol::Query.new(0).search('foo') }
85
+ it { should have_param(:term) }
86
+ its(:search) { should == 'foo' }
87
+ end
88
+
89
+ context 'when not set' do
90
+ it { should_not have_param(:term) }
91
+ end
92
+ end
93
+
94
+ describe '#order' do
95
+ it 'should raise unless wrong format' do
96
+ expect { subject.order('foo') }.to raise_error ArgumentError
97
+ end
98
+
99
+ it 'should add order direction on request' do
100
+ subject.order('price ASC').params[:sortingAscending].should == 'true'
101
+ subject.order('price DESC').params[:sortingAscending].should == 'false'
102
+ end
103
+
104
+ it 'should add order key on request' do
105
+ subject.order('price ASC').params[:sortingMethod].should == 'price'
106
+ end
107
+
108
+ it 'should return query' do
109
+ subject.order('price ASC').should == subject
110
+ end
111
+ end
112
+
113
+ describe "query methods" do
114
+ it 'should include categories' do
115
+ subject.include :categories
116
+ subject.should be_categories
117
+ end
118
+
119
+ it 'should include categories' do
120
+ subject.include :categories
121
+ subject.should be_categories
122
+ end
123
+
124
+ it 'should include categories' do
125
+ subject.include :categories
126
+ subject.should be_categories
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bol::Request do
4
+ let(:query) { Bol::Query.new(1).limit(3) }
5
+ let(:request) { Bol::Request.new(query) }
6
+
7
+ it 'should require a query' do
8
+ expect { Bol::Request.new }.to raise_error ArgumentError
9
+ end
10
+
11
+ describe 'query filtering' do
12
+ it 'should list all params in a hash' do
13
+ request.params.should == {
14
+ includeCategories: false,
15
+ includeProducts: false,
16
+ includeRefinements: false,
17
+ categoryId: 1,
18
+ nrProducts: 3
19
+ }
20
+ end
21
+
22
+ it 'should not list ignored params' do
23
+ Bol::Requests::Product.new(1, query).params.should == {
24
+ categoryId: 1,
25
+ nrProducts: 3
26
+ }
27
+ end
28
+
29
+ it 'should convert query to params' do
30
+ request.query_string.should == "categoryId=1&nrProducts=3&includeCategories=false&includeProducts=false&includeRefinements=false"
31
+ end
32
+ end
33
+
34
+ describe 'signing and headers' do
35
+ it 'should raise error when key and secret are not configured' do
36
+ expect { request.fetch }.should raise_error Bol::ConfigurationError
37
+ end
38
+
39
+ it 'should request application/xml' do
40
+ Bol.configure access_key: 'foo', secret: 'bar'
41
+ Net::HTTP.any_instance.should_receive(:request).with do |req|
42
+ req['Content-Type'] == 'application/xml'
43
+ end.and_return(double(code: 200))
44
+ request.fetch
45
+ end
46
+
47
+ it 'should set special date header' do
48
+ Bol.configure access_key: 'foo', secret: 'bar'
49
+ Net::HTTP.any_instance.should_receive(:request).with do |req|
50
+ !req['X-OpenAPI-Date'].nil?
51
+ end.and_return(double(code: 200))
52
+ request.fetch
53
+ end
54
+
55
+ it 'should add signature to request by default' do
56
+ Bol.configure access_key: 'foo', secret: 'bar'
57
+ Net::HTTP.any_instance.should_receive(:request).with do |req|
58
+ !req['X-OpenAPI-Authorization'].nil?
59
+ end.and_return(double(code: 200))
60
+ request.fetch
61
+ end
62
+ end
63
+
64
+ it 'should trigger event manually' do
65
+ Bol.configure access_key: 'foo', secret: 'bar'
66
+ Net::HTTP.any_instance.should_receive(:request).and_return(double(code: 200))
67
+ request.fetch
68
+ end
69
+
70
+ describe Bol::Requests::Product do
71
+ let(:request) { Bol::Requests::Product.new(1, query) }
72
+
73
+ it 'should get from correct URL' do
74
+ request.uri.to_s.should == 'https://openapi.bol.com/openapi/services/rest/catalog/v3/products/1?categoryId=1&nrProducts=3'
75
+ end
76
+
77
+ it 'should require product id as term' do
78
+ expect { Bol::Requests::Product.new(query) }.to raise_error ArgumentError
79
+ end
80
+
81
+ it 'should URL encode the terms' do
82
+ Bol::Requests::Product.new('foo bar', query).uri.to_s.should ==
83
+ 'https://openapi.bol.com/openapi/services/rest/catalog/v3/products/foo%20bar?categoryId=1&nrProducts=3'
84
+ end
85
+ end
86
+
87
+ describe Bol::Requests::Search do
88
+ let(:query) { Bol::Query.new(1).limit(3).search('foo') }
89
+ let(:request) { Bol::Requests::Search.new(query) }
90
+
91
+ it 'should get from correct URL' do
92
+ request.uri.to_s.should ==
93
+ 'https://openapi.bol.com/openapi/services/rest/catalog/v3/searchresults?categoryId=1&nrProducts=3&term=foo'
94
+ end
95
+
96
+ it 'should require search terms' do
97
+ expect { Bol::Requests::Product.new(Bol::Query.new(0)) }.to raise_error ArgumentError
98
+ end
99
+
100
+ it 'should encode the search terms' do
101
+ query = Bol::Query.new(1).limit(3).search('foo bar')
102
+ Bol::Requests::Search.new(query).uri.to_s.should ==
103
+ 'https://openapi.bol.com/openapi/services/rest/catalog/v3/searchresults?categoryId=1&nrProducts=3&term=foo%20bar'
104
+ end
105
+ end
106
+
107
+ describe Bol::Requests::List do
108
+ let(:query) { Bol::Query.new(0) }
109
+ let(:request) { Bol::Requests::List.new('foo', query) }
110
+
111
+ it 'should get from correct URL' do
112
+ request.uri.to_s.should == 'https://openapi.bol.com/openapi/services/rest/catalog/v3/listresults/foo/0?categoryId=0&includeCategories=false&includeProducts=false&includeRefinements=false'
113
+ end
114
+
115
+ it 'should require param type' do
116
+ expect { Bol::Requests::Product.new(Bol::Query.new(0)) }.to raise_error ArgumentError
117
+ end
118
+ end
119
+ end