barnie 0.1.3 → 0.2.0
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.
- data/barnie.gemspec +15 -18
- data/lib/barnie/helpers.rb +12 -11
- data/lib/barnie/request.rb +34 -0
- data/lib/barnie/response.rb +43 -0
- data/lib/barnie/version.rb +1 -1
- data/lib/barnie.rb +3 -13
- data/spec/{unit/barnie → barnie}/helpers_spec.rb +13 -7
- data/spec/barnie/request_spec.rb +48 -0
- data/spec/barnie/response_spec.rb +91 -0
- data/spec/fixtures/barnie.yml +1215 -0
- data/spec/fixtures/isbns.txt +1000 -0
- data/spec/spec_helper.rb +4 -4
- data/spec/support/isbns.rb +5 -8
- data/spec/support/show_me_the_page.rb +2 -2
- data/spec/support/vcr.rb +1 -1
- metadata +27 -54
- data/.rspec +0 -1
- data/benchmarks/bm_helper.rb +0 -5
- data/benchmarks/stress_test.rb +0 -28
- data/lib/barnie/client.rb +0 -57
- data/lib/barnie/error.rb +0 -7
- data/spec/acceptance/availability_spec.rb +0 -43
- data/spec/acceptance/smokescreen_spec.rb +0 -18
- data/spec/fixtures/asins.txt +0 -10000
- data/spec/fixtures/barnie_availability.yml +0 -507
- data/spec/fixtures/barnie_client.yml +0 -347
- data/spec/fixtures/barnie_helpers.yml +0 -231
- data/spec/fixtures/barnie_smokescreen.yml +0 -112
- data/spec/fixtures/bug.yml +0 -248
- data/spec/unit/barnie/client_spec.rb +0 -122
- data/spec/unit/barnie_spec.rb +0 -9
data/barnie.gemspec
CHANGED
@@ -1,33 +1,30 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path(
|
3
|
-
require
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'barnie/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
6
|
+
s.name = 'barnie'
|
7
7
|
s.version = Barnie::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.email =
|
10
|
-
s.homepage =
|
11
|
-
s.authors = [
|
9
|
+
s.email = 'code@papercavalier.com'
|
10
|
+
s.homepage = 'http://gloss.papercavalier.com/barnie'
|
11
|
+
s.authors = ['Paper Cavalier']
|
12
12
|
s.description = %q{A fake API to a well-known bookstore}
|
13
13
|
s.summary = %q{A fake API to a well-known bookstore}
|
14
14
|
|
15
|
-
s.rubyforge_project =
|
15
|
+
s.rubyforge_project = 'barnie'
|
16
16
|
|
17
|
-
s.add_dependency(
|
17
|
+
s.add_dependency('kosher', '~> 0.2.10')
|
18
|
+
s.add_dependency('mechanize', '~> 1.0.0')
|
19
|
+
s.add_development_dependency('launchy', '~> 0.3.7')
|
20
|
+
s.add_development_dependency('rspec', '~> 2.5.0')
|
21
|
+
s.add_development_dependency('ruby-debug19', '~> 0.11.0')
|
22
|
+
s.add_development_dependency('vcr', '~> 1.7.0')
|
23
|
+
s.add_development_dependency('webmock', '~> 1.6.2')
|
18
24
|
|
19
|
-
s.add_development_dependency("bookland", "0.3.1")
|
20
|
-
s.add_development_dependency("launchy", "~> 0.3.7")
|
21
|
-
s.add_development_dependency("rspec", "~> 2.1.0")
|
22
|
-
s.add_development_dependency("throttler", "~> 0.2.1")
|
23
|
-
s.add_development_dependency("vcr", "~> 1.3.1")
|
24
|
-
s.add_development_dependency("webmock", "~> 1.6.1")
|
25
|
-
if RUBY_VERSION.include?("1.9")
|
26
|
-
s.add_development_dependency("ruby-debug19", "~> 0.11.0")
|
27
|
-
end
|
28
25
|
|
29
26
|
s.files = `git ls-files`.split("\n")
|
30
27
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
31
28
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
32
|
-
s.require_paths = [
|
29
|
+
s.require_paths = ['lib']
|
33
30
|
end
|
data/lib/barnie/helpers.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Barnie
|
2
2
|
module Helpers
|
3
3
|
def sanitize_string(string)
|
4
|
-
string.gsub!(/^"|"$/,
|
5
|
-
string.gsub!(/\\r|\\n/,
|
4
|
+
string.gsub!(/^"|"$/, '')
|
5
|
+
string.gsub!(/\\r|\\n/, '')
|
6
6
|
string.strip
|
7
7
|
end
|
8
8
|
|
@@ -10,13 +10,14 @@ module Barnie
|
|
10
10
|
if string !~ /\S/
|
11
11
|
0
|
12
12
|
else
|
13
|
-
string.gsub!(/\D/,
|
13
|
+
string.gsub!(/\D/, '').to_i
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def title(html)
|
18
|
-
title = html.search(
|
19
|
-
title = html.search(
|
18
|
+
title = html.search('.title a')
|
19
|
+
title = html.search('.ebooktitle a') unless title.any?
|
20
|
+
|
20
21
|
title
|
21
22
|
end
|
22
23
|
|
@@ -25,27 +26,27 @@ module Barnie
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def extract_isbn(string)
|
28
|
-
string.scan(/e\/\d{11,13}\//).first.gsub(/\D/,
|
29
|
+
string.scan(/e\/\d{11,13}\//).first.gsub(/\D/, '').rjust(13, '0') unless string !~ /\S/
|
29
30
|
end
|
30
31
|
|
31
32
|
def extract_ships_in(string)
|
32
33
|
return nil if string !~ /\S/
|
33
34
|
string = sanitize_string(string)
|
34
35
|
|
35
|
-
if string.include?(
|
36
|
+
if string.include?('hours')
|
36
37
|
string.scan(/\d{2}/).last.to_i
|
37
|
-
elsif string.include?(
|
38
|
+
elsif string.include?('days')
|
38
39
|
string.scan(/\d{1}/).last.to_i * 24
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
43
|
def extract_link(title)
|
43
|
-
title.first.attributes[
|
44
|
+
title.first.attributes['href'].value
|
44
45
|
end
|
45
46
|
|
46
47
|
def extract_authors(html)
|
47
|
-
authors = html.search(
|
48
|
-
authors = html.search(
|
48
|
+
authors = html.search('.contrib a')
|
49
|
+
authors = html.search('.ebookcontrib a') unless authors.any?
|
49
50
|
authors.map { |author| sanitize_string(author.text) }
|
50
51
|
end
|
51
52
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Barnie
|
5
|
+
class Request
|
6
|
+
attr_reader :agent
|
7
|
+
|
8
|
+
def initialize(user_agent)
|
9
|
+
@agent = Mechanize.new { |a| a.user_agent = "iPhone #{user_agent}" }
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(isbns)
|
13
|
+
@isbns = isbns
|
14
|
+
end
|
15
|
+
|
16
|
+
def get
|
17
|
+
page = @agent.get(uri)
|
18
|
+
Response.new(page)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def query
|
24
|
+
"ISBNLIST=#{@isbns[0, 50].join(',')}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def uri
|
28
|
+
URI::HTTP.build(
|
29
|
+
:host => 'productsearch.barnesandnoble.com',
|
30
|
+
:path => '/search/results.aspx',
|
31
|
+
:query => query)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'kosher'
|
2
|
+
require 'barnie/helpers'
|
3
|
+
|
4
|
+
module Barnie
|
5
|
+
class Response
|
6
|
+
include Helpers
|
7
|
+
|
8
|
+
attr_accessor :page
|
9
|
+
|
10
|
+
def initialize(page)
|
11
|
+
self.page = page
|
12
|
+
end
|
13
|
+
|
14
|
+
def snapshots
|
15
|
+
raise Error.new('Blank page') if page.body == '' && page.code == 200
|
16
|
+
|
17
|
+
container = page.search('#prod-container')
|
18
|
+
container.map do |html|
|
19
|
+
|
20
|
+
price = extract_price(html.search('.price strong').text)
|
21
|
+
hours = extract_ships_in(html.search('.availability').text) || 999
|
22
|
+
title = title(html)
|
23
|
+
link = extract_link(title)
|
24
|
+
isbn = extract_isbn(link)
|
25
|
+
|
26
|
+
Kosher::Snapshot.new(
|
27
|
+
'bn.com',
|
28
|
+
isbn,
|
29
|
+
nil,
|
30
|
+
nil,
|
31
|
+
price > 0 ? 1 : 0,
|
32
|
+
price > 0 ?
|
33
|
+
[Kosher::Offer.new(
|
34
|
+
nil,
|
35
|
+
Kosher::Item.new(price, 'USD', 1, Kosher::Condition.new(1), Kosher::Description.new('')),
|
36
|
+
Kosher::Seller.new(nil, 'Barnes & Noble.com', nil, Kosher::Location.new('US')),
|
37
|
+
Kosher::Shipping.new(0, 'USD', Kosher::Availability.new(hours))
|
38
|
+
)] :
|
39
|
+
[])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/barnie/version.rb
CHANGED
data/lib/barnie.rb
CHANGED
@@ -1,16 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require "barnie/error"
|
4
|
-
require "barnie/helpers"
|
5
|
-
require "barnie/client"
|
1
|
+
require 'barnie/request'
|
2
|
+
require 'barnie/response'
|
6
3
|
|
7
|
-
# = Barnie
|
8
|
-
# Barnie is a fake Barnes & Noble API
|
9
4
|
module Barnie
|
10
|
-
|
11
|
-
# Instantiates a new Barnie::Request
|
12
|
-
def self.new(args={})
|
13
|
-
Barnie::Client.new(args)
|
14
|
-
end
|
15
|
-
|
5
|
+
class Error < StandardError; end
|
16
6
|
end
|
@@ -4,7 +4,7 @@ require "spec_helper"
|
|
4
4
|
module Barnie
|
5
5
|
describe Helpers do
|
6
6
|
before do
|
7
|
-
@client =
|
7
|
+
@client = Response.new(nil)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "sanitizes string" do
|
@@ -42,8 +42,12 @@ module Barnie
|
|
42
42
|
end
|
43
43
|
|
44
44
|
context "requiring real HTML objects" do
|
45
|
+
let(:request) do
|
46
|
+
Request.new('Company Info')
|
47
|
+
end
|
48
|
+
|
45
49
|
before do
|
46
|
-
VCR.insert_cassette('
|
50
|
+
VCR.insert_cassette('barnie')
|
47
51
|
end
|
48
52
|
|
49
53
|
after do
|
@@ -52,8 +56,9 @@ module Barnie
|
|
52
56
|
|
53
57
|
context "ebook" do
|
54
58
|
before do
|
55
|
-
|
56
|
-
@
|
59
|
+
request << ["9781400826094"]
|
60
|
+
@client = request.get
|
61
|
+
@html = @client.page.search("#prod-container").first
|
57
62
|
end
|
58
63
|
|
59
64
|
it "returns title as XML NodeSet" do
|
@@ -75,8 +80,9 @@ module Barnie
|
|
75
80
|
|
76
81
|
context "paperback" do
|
77
82
|
before do
|
78
|
-
|
79
|
-
@
|
83
|
+
request << ["9780816614028"]
|
84
|
+
@client = request.get
|
85
|
+
@html = @client.page.search("#prod-container").first
|
80
86
|
end
|
81
87
|
|
82
88
|
it "returns title as XML NodeSet" do
|
@@ -92,7 +98,7 @@ module Barnie
|
|
92
98
|
end
|
93
99
|
|
94
100
|
it "extracts authors" do
|
95
|
-
@client.extract_authors(@html).should =~ ["
|
101
|
+
@client.extract_authors(@html).should =~ ["Brian Massumi", "Gilles Deleuze"]
|
96
102
|
end
|
97
103
|
end
|
98
104
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Barnie
|
4
|
+
describe Request do
|
5
|
+
let (:request) { Request.new('Company Info') }
|
6
|
+
|
7
|
+
before do
|
8
|
+
VCR.insert_cassette('barnie')
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
VCR.eject_cassette
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".new" do
|
16
|
+
it "requires user agent" do
|
17
|
+
expect do
|
18
|
+
Request.new
|
19
|
+
end.to raise_error /wrong number of arguments/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets up a Mechanize agent" do
|
23
|
+
request.instance_variable_get(:@agent).should be_an_instance_of Mechanize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#<<" do
|
28
|
+
it "sets the ISBNs to query" do
|
29
|
+
request << isbns
|
30
|
+
request.instance_variable_get(:@isbns).should == isbns
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#get" do
|
35
|
+
it "gets a response" do
|
36
|
+
request << isbns[0, 50]
|
37
|
+
response = request.get
|
38
|
+
response.should be_an_instance_of Response
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises an exception if no ISBNs are specified" do
|
42
|
+
expect do
|
43
|
+
request.get
|
44
|
+
end.to raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Barnie
|
4
|
+
describe Response do
|
5
|
+
let(:request) do
|
6
|
+
Request.new('Company Info')
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:response) do
|
10
|
+
request << isbns[0, 50]
|
11
|
+
request.get
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
VCR.insert_cassette('barnie')
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
VCR.eject_cassette
|
20
|
+
end
|
21
|
+
|
22
|
+
it "includes Helpers" do
|
23
|
+
Response.ancestors.should include Helpers
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#snapshots" do
|
27
|
+
it "returns snapshots" do
|
28
|
+
snapshots = response.snapshots
|
29
|
+
|
30
|
+
snapshots.should be_an_instance_of Array
|
31
|
+
snapshots.first.should be_a Kosher::Snapshot
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns a result for a single ISBN" do
|
35
|
+
request << [isbns.first]
|
36
|
+
response = request.get
|
37
|
+
|
38
|
+
response.snapshots.size.should eql 1
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when page does not include #prod-container element" do
|
42
|
+
|
43
|
+
let(:mock_page) do
|
44
|
+
page = mock("response")
|
45
|
+
page.stub!(:search).and_return(nil)
|
46
|
+
page
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when page body is empty and HTTP is OK" do
|
50
|
+
before do
|
51
|
+
mock_page.stub!(:body).and_return("")
|
52
|
+
mock_page.stub!(:code).and_return(200)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "throws a Barnie error" do
|
56
|
+
expect do
|
57
|
+
Response.new(mock_page).snapshots
|
58
|
+
end.to raise_error Barnie::Error, 'Blank page'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns a book that is available for sale" do
|
64
|
+
request << ['9780816614028']
|
65
|
+
response = request.get
|
66
|
+
snapshot = response.snapshots.first
|
67
|
+
snapshot.offers_count.should eql 1
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns an ebook" do
|
71
|
+
request << ["9781400826094", "9780203029923"]
|
72
|
+
request.get.snapshots.each do |snapshot|
|
73
|
+
snapshot.isbn.should be_an_instance_of String
|
74
|
+
snapshot.offers.first.price.cents.should > 0
|
75
|
+
snapshot.offers.first.shipping.should_not be_kosher
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns a book that is not available for sale" do
|
80
|
+
request << ["9780002189347"]
|
81
|
+
snapshot = request.get.snapshots.first
|
82
|
+
snapshot.offers_count.should eql 0
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not return a book that is not in the Barnes & Noble catalogue" do
|
86
|
+
request << ["9780002196642"]
|
87
|
+
request.get.snapshots.should be_empty
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|