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