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 CHANGED
@@ -1,33 +1,30 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "barnie/version"
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'barnie/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "barnie"
6
+ s.name = 'barnie'
7
7
  s.version = Barnie::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.email = ["code@papercavalier.com"]
10
- s.homepage = "http://gloss.papercavalier.com/barnie"
11
- s.authors = ["Paper Cavalier"]
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 = "barnie"
15
+ s.rubyforge_project = 'barnie'
16
16
 
17
- s.add_dependency("mechanize", "~> 1.0.0")
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 = ["lib"]
29
+ s.require_paths = ['lib']
33
30
  end
@@ -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/, "").to_i
13
+ string.gsub!(/\D/, '').to_i
14
14
  end
15
15
  end
16
16
 
17
17
  def title(html)
18
- title = html.search(".title a")
19
- title = html.search(".ebooktitle a") unless title.any?
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/, "").rjust(13,"0") unless string !~ /\S/
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?("hours")
36
+ if string.include?('hours')
36
37
  string.scan(/\d{2}/).last.to_i
37
- elsif string.include?("days")
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["href"].value
44
+ title.first.attributes['href'].value
44
45
  end
45
46
 
46
47
  def extract_authors(html)
47
- authors = html.search(".contrib a")
48
- authors = html.search(".ebookcontrib a") unless authors.any?
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
@@ -1,3 +1,3 @@
1
1
  module Barnie #:nodoc:
2
- VERSION = "0.1.3"
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/barnie.rb CHANGED
@@ -1,16 +1,6 @@
1
- require "mechanize"
2
- require "uri"
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 = Barnie.new("company details")
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('barnie_helpers')
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
- @client.isbns = ["9781400826094"]
56
- @html = @client.request.search("#prod-container").first
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
- @client.isbns = ["9780816614028"]
79
- @html = @client.request.search("#prod-container").first
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 =~ ["Felix Guattari", "Gilles Deleuze"]
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