bol 0.0.1.beta

Sign up to get free protection for your applications and to get access to all the features.
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