mroch-campusbooks 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/VERSION.yml +4 -0
- data/lib/campusbooks.rb +15 -0
- data/lib/campusbooks/base.rb +25 -0
- data/lib/campusbooks/book.rb +83 -0
- data/lib/campusbooks/offer.rb +35 -0
- data/lib/isbn/LICENCE +20 -0
- data/lib/isbn/README +60 -0
- data/lib/isbn/tools.rb +173 -0
- data/test/base_test.rb +67 -0
- data/test/book_test.rb +100 -0
- data/test/offer_test.rb +41 -0
- data/test/test_helper.rb +19 -0
- metadata +79 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Marshall Roch
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/VERSION.yml
ADDED
data/lib/campusbooks.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module CampusBooks
|
4
|
+
class Base
|
5
|
+
include HTTParty
|
6
|
+
base_uri 'http://api.campusbooks.com/3/rest'
|
7
|
+
format :xml
|
8
|
+
|
9
|
+
protected
|
10
|
+
def self.get_response(path, options={})
|
11
|
+
res = self.get(path, options)
|
12
|
+
case res['response']['status']
|
13
|
+
when 'ok'
|
14
|
+
return res['response']
|
15
|
+
|
16
|
+
when 'error'
|
17
|
+
err_count = res['response']['errors']['error'].length
|
18
|
+
raise Error, err_count == 1 ? 'An error' : "#{err_count} errors" +
|
19
|
+
" occured while getting path '#{path}' with options #{options.inspect}:\n " +
|
20
|
+
res['response']['errors']['error'].join("\n ")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'isbn/tools'
|
2
|
+
|
3
|
+
module CampusBooks
|
4
|
+
class Book < Base
|
5
|
+
SUPPORTED_PARAMS = [
|
6
|
+
:isbn10, # Ten-Digit ISBN for this book
|
7
|
+
:isbn13, # Thirteen-Digit ISBN for this book
|
8
|
+
:title, # Book Title
|
9
|
+
:author, # Book Author
|
10
|
+
:binding, # Book Binding
|
11
|
+
:msrp, # List price for the book
|
12
|
+
:pages, # Number of pages in the book
|
13
|
+
:publisher, # Book Publisher
|
14
|
+
:published_date, # Published Date
|
15
|
+
:edition, # Edition
|
16
|
+
:description # A text description for the book
|
17
|
+
]
|
18
|
+
attr_reader *SUPPORTED_PARAMS
|
19
|
+
alias_method :isbn, :isbn13
|
20
|
+
|
21
|
+
def self.find(isbn, opts = {})
|
22
|
+
isbn = isbn.to_s
|
23
|
+
canonical_isbn = (ISBN_Tools.is_valid_isbn13?(isbn) ? isbn : ISBN_Tools.isbn10_to_isbn13(isbn)) or raise ArgumentError.new('isbn is invalid')
|
24
|
+
|
25
|
+
include_prices = opts[:include] && [*opts[:include]].include?(:prices)
|
26
|
+
|
27
|
+
raw_result = get_response(include_prices ? '/bookprices' : '/bookinfo', :query => {
|
28
|
+
:isbn => canonical_isbn, :key => CampusBooks.api_key
|
29
|
+
})
|
30
|
+
|
31
|
+
book_params = include_prices ? raw_result['page']['book'] : raw_result['page']
|
32
|
+
|
33
|
+
if raw_result['page']['offers']
|
34
|
+
book_params['offers'] = flatten_offers(raw_result['page']['offers'])
|
35
|
+
end
|
36
|
+
|
37
|
+
new(book_params)
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(params = {})
|
41
|
+
create_offers(params.delete('offers')) if params.key?('offers')
|
42
|
+
|
43
|
+
SUPPORTED_PARAMS.each do |param|
|
44
|
+
instance_variable_set("@#{param}", params[param.to_s]) if params.key?(param.to_s)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def published_date
|
49
|
+
@parsed_published_date ||= Date.parse(@published_date)
|
50
|
+
end
|
51
|
+
|
52
|
+
def offers
|
53
|
+
if !@offers_loaded
|
54
|
+
raw_offers = get_response('/prices', :query => { :isbn => @isbn, :key => CampusBooks.api_key })['page']
|
55
|
+
create_offers(self.class.flatten_offers(raw_offers))
|
56
|
+
end
|
57
|
+
@offers
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def self.flatten_offers(data)
|
62
|
+
result = []
|
63
|
+
data['condition'].each do |condition|
|
64
|
+
if condition['offer']
|
65
|
+
if condition['offer'].is_a?(Array)
|
66
|
+
condition['offer'].each do |offer|
|
67
|
+
result.push(offer)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
result.push(condition['offer'])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_offers(offer_hashes)
|
78
|
+
@offers = offer_hashes.inject([]) { |ary, offer_hash| ary.push(Offer.new(offer_hash)) }
|
79
|
+
@offers_loaded = true
|
80
|
+
return @offers
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CampusBooks
|
2
|
+
class Offer < Base
|
3
|
+
SUPPORTED_PARAMS = [
|
4
|
+
:isbn, # The ISBN for this offer
|
5
|
+
:isbn13, # The thirteen digit ISBN for this offer
|
6
|
+
:isbn10, # The ten digit ISBN for this offer
|
7
|
+
:merchant_id, # A numeric merchant ID (Note, this value may be signed)
|
8
|
+
:merchant_name, # The Name of the merchant (looked up from the defined constants)
|
9
|
+
:price, # The price that this merchant is listing this item for
|
10
|
+
:shipping_ground, # The cost to ship to an address in the US via ground services
|
11
|
+
:total_price, # Seller price plus the ground shipping price
|
12
|
+
:link, # Link to purchase the book
|
13
|
+
:condition_id, # Numeric representation of the condition (see constants)
|
14
|
+
:condition_text, # Text representation of the condition
|
15
|
+
:availability_id, # Numeric representation of the availability (how long it takes for the seller to ship it)
|
16
|
+
:availability_text, # Text representation of the availability
|
17
|
+
:location, # Geographic location where this item ships from (not always present)
|
18
|
+
:their_id, # The merchant's id for this offer (not always present)
|
19
|
+
:comments, # Comments about this offering
|
20
|
+
:condition_text # Text representation of the condition
|
21
|
+
]
|
22
|
+
attr_reader *SUPPORTED_PARAMS
|
23
|
+
|
24
|
+
def initialize(params = {})
|
25
|
+
SUPPORTED_PARAMS.each do |param|
|
26
|
+
instance_variable_set("@#{param}", params[param.to_s]) if params.key?(param.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Fall back on ISBN13 or ISBN10 if @isbn isn't set
|
31
|
+
def isbn
|
32
|
+
@isbn || @isbn13 || @isbn10
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/isbn/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2006, Thierry Godfroid
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
* The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
16
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
17
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
18
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/isbn/README
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
== ISBN-Tools for Ruby
|
2
|
+
|
3
|
+
This library provides the ability to manipulate ISBN numbers.
|
4
|
+
It knows about ISBN-10 and ISBN-13 numbers, has the ability to
|
5
|
+
check if they are valid and convert from one format to the other.
|
6
|
+
It can, of course, compute the check digit of both formats.
|
7
|
+
|
8
|
+
Finally, it has the ability to hyphenate ISBN numbers for the
|
9
|
+
following group identifiers: 0, 1 and 2. Note that only hyphenation
|
10
|
+
methods need to know about ranges, so all others methods (validity,
|
11
|
+
checksum computations and number conversions) can be used with ISBN
|
12
|
+
numbers from any group.
|
13
|
+
|
14
|
+
Other ranges could be added on request but I would need samples
|
15
|
+
ISBN to check the result.
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'isbn/tools'
|
21
|
+
|
22
|
+
isbn_good = "2800107766"
|
23
|
+
isbn_bad = "2810107766"
|
24
|
+
|
25
|
+
def check_and_hyphenate(isbn)
|
26
|
+
if ISBN_Tools.is_valid?(isbn)
|
27
|
+
puts ISBN_Tools.hyphenate(isbn)
|
28
|
+
else
|
29
|
+
cksum = ISBN_Tools.compute_check_digit(isbn)
|
30
|
+
puts "Invalid ISBN number [#{isbn}]. Checksum should be #{cksum}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
check_and_hyphenate isbn_good # => 2-8001-0776-6
|
35
|
+
check_and_hyphenate isbn_bad # => Invalid ISBN number [2810107766]. Checksum should be 9
|
36
|
+
|
37
|
+
== Copyright
|
38
|
+
|
39
|
+
Copyright:: 2006, Thierry Godfroid
|
40
|
+
|
41
|
+
The following sources were used in order to understand ISBN numbers and
|
42
|
+
how to manipulate them. No books were harmed in the process.
|
43
|
+
- http://www.isbn-international.org
|
44
|
+
- "Are You Ready for ISBN-13?" http://www.isbn.org/standards/home/isbn/transition.asp. Note that at the bottom of this page, you can find a link towards a small book "ISBN-13 for Dummies", available as PDF (http://www.bisg.org/isbn-13/ISBN13_For_Dummies.pdf)
|
45
|
+
- Structure of an ISBN number http://www.isbn.org/standards/home/isbn/international/html/usm4.htm
|
46
|
+
|
47
|
+
Ranges information was found at http://www.isbn-international.org/en/identifiers.html.
|
48
|
+
|
49
|
+
== LICENCE NOTE
|
50
|
+
|
51
|
+
MIT-Style license. See LICENCE file in this distribution.
|
52
|
+
|
53
|
+
== Requirements
|
54
|
+
|
55
|
+
ISBN-Tools requires Ruby 1.8.2 or better.
|
56
|
+
|
57
|
+
== Known bugs/Limitations
|
58
|
+
|
59
|
+
- This release code only allows for one-digit groups.
|
60
|
+
- See also the TODO file in this distribution
|
data/lib/isbn/tools.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2006, Thierry Godfroid
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# * The name of the author may not be used to endorse or promote products derived
|
12
|
+
# from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
15
|
+
# copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
18
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
19
|
+
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
# This module provides all the methods of the ISBN-Tools library.
|
26
|
+
# Methods have no state but the library reads the file data/ranges.dat
|
27
|
+
# and fills up the RNG hash when loaded.
|
28
|
+
module ISBN_Tools
|
29
|
+
# Supported groups and associated ranges. Data is read from data/ranges.dat
|
30
|
+
# (provided in gem) at module load.
|
31
|
+
RNG = {}
|
32
|
+
|
33
|
+
File.open(File.join(File.dirname(__FILE__), "../../data/ranges.dat")) do |file|
|
34
|
+
file.each do |line|
|
35
|
+
line.chomp!
|
36
|
+
break if line.empty?
|
37
|
+
ar_line = line.split(/,/)
|
38
|
+
ndx = ar_line.delete_at(0)
|
39
|
+
RNG[ndx] = []
|
40
|
+
ar_line.each { |item|
|
41
|
+
r = item.split(/\.\./)
|
42
|
+
RNG[ndx].push(Range.new(r[0],r[1]))
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Clear all useless characters from an ISBN number and upcase the 'X' sign when
|
48
|
+
# present. Also does the basic check that 'X' must be the last sign of the number,
|
49
|
+
# if present. Returns nil if provided string is nil or X is not at the last position.
|
50
|
+
#
|
51
|
+
# No length check is done: no matter what string is passed in, all characters that
|
52
|
+
# not in the range [0-9xX] are removed.
|
53
|
+
def ISBN_Tools.cleanup(isbn_)
|
54
|
+
isbn_.gsub(/[^0-9xX]/,'').gsub(/x/,'X') unless isbn_.nil? or isbn_.scan(/([xX])/).length > 1
|
55
|
+
end
|
56
|
+
|
57
|
+
# Same as cleanup but alters the argument.
|
58
|
+
def ISBN_Tools.cleanup!(isbn_)
|
59
|
+
isbn_.replace(cleanup(isbn_))
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check that the value is a valid ISBN-10 number. Returns true if it is, false otherwise.
|
63
|
+
# The method will check that the number is exactly 10 digits long and that the tenth digit is
|
64
|
+
# the correct checksum for the number.
|
65
|
+
def ISBN_Tools.is_valid_isbn10?(isbn_)
|
66
|
+
isbn = cleanup(isbn_)
|
67
|
+
return false if isbn.nil? or isbn.match(/^[0-9]{9}[0-9X]$/).nil?
|
68
|
+
sum = 0;
|
69
|
+
0.upto(9) { |ndx| sum += (isbn[ndx]!= 88 ? isbn[ndx].chr.to_i : 10) * (10-ndx) } # 88 is ascii of X
|
70
|
+
sum % 11 == 0
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check that the value is a valid ISBN-13 number. Returns true if it is, false otherwise.
|
74
|
+
# The method will check that the number is exactly 13 digits long and that the thirteenth digit is
|
75
|
+
# the correct checksum for the number.
|
76
|
+
def ISBN_Tools.is_valid_isbn13?(isbn_)
|
77
|
+
isbn = cleanup(isbn_)
|
78
|
+
return false if isbn.nil? or isbn.length!=13 or isbn.match(/^97[8|9][0-9]{10}$/).nil?
|
79
|
+
sum = 0
|
80
|
+
0.upto(12) { |ndx| sum += isbn[ndx].chr.to_i * (ndx % 2 == 0 ? 1 : 3) }
|
81
|
+
sum.remainder(10) == 0
|
82
|
+
end
|
83
|
+
|
84
|
+
# Check that an ISBN is valid or not. Returns true if is, false otherwise. This method will
|
85
|
+
# first call is_valid_isbn10() and, on failure, try is_valid_isbn13(). Returns true if it is
|
86
|
+
# a valid number, false otherwise.
|
87
|
+
# This method is handy if you don't want to be bothered by checking the length of your
|
88
|
+
# isbn before checking its validity. It is a bit slower since cleanup will be called twice.
|
89
|
+
def ISBN_Tools.is_valid?(isbn_)
|
90
|
+
is_valid_isbn10?(isbn_) || is_valid_isbn13?(isbn_)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Computes the check digit of an ISBN-10 number. It will ignore the tenth sign if present
|
94
|
+
# and accepts a number with only 9 digits. Returns the checksum digit or nil. Please note
|
95
|
+
# that the checksum digit of an ISBN-10 may be the character 'X'.
|
96
|
+
def ISBN_Tools.compute_isbn10_check_digit(isbn_)
|
97
|
+
isbn = cleanup(isbn_)
|
98
|
+
return nil if isbn.nil? or isbn.length > 10 or isbn.length < 9
|
99
|
+
sum = 0;
|
100
|
+
0.upto(8) { |ndx| sum += isbn[ndx].chr.to_i * (10-ndx) }
|
101
|
+
(11-sum) % 11 == 10 ? "X" : ((11-sum) % 11).to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
# Computes the check digit of an ISBN-13 number. It will ignore the thirteenth sign if present
|
105
|
+
# and accepts a number with only 12 digits. Returns the checksum digit or nil. Please note
|
106
|
+
# that the checksum digit of an ISBN-13 is always in the range [0-9].
|
107
|
+
def ISBN_Tools.compute_isbn13_check_digit(isbn_)
|
108
|
+
isbn = cleanup(isbn_)
|
109
|
+
return nil if isbn.nil? or isbn.length > 13 or isbn.length < 12
|
110
|
+
sum = 0
|
111
|
+
0.upto(11) { |ndx| sum += isbn[ndx].chr.to_i * (ndx % 2 == 0 ? 1 : 3) }
|
112
|
+
(10-sum.remainder(10)) == 10 ? "0" : (10-sum.remainder(10)).to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
# Compute the check digit of an ISBN number. Try as an ISBN-10 number
|
116
|
+
# first, and if it failed, as an ISBN-13 number. Returns the check digit or
|
117
|
+
# nil if a processing error occured.
|
118
|
+
# This method is a helper for compute_isbn10_check_digit and
|
119
|
+
# compute_isbn13_check_digit.
|
120
|
+
def ISBN_Tools.compute_check_digit(isbn_)
|
121
|
+
compute_isbn10_check_digit(isbn_) || compute_isbn13_check_digit(isbn_)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Convert an ISBN-10 number to its equivalent ISBN-13 number. Returns the converted number or nil
|
125
|
+
# if the provided ISBN-10 number is nil or non valid.
|
126
|
+
def ISBN_Tools.isbn10_to_isbn13(isbn_)
|
127
|
+
isbn = cleanup(isbn_)
|
128
|
+
"978" + isbn[0..8] + compute_isbn13_check_digit("978" + isbn[0..8]) unless isbn.nil? or ! is_valid_isbn10?(isbn)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Convert an ISBN-13 number to its equivalent ISBN-10 number. Returns the converted number or nil
|
132
|
+
# if the provided ISBN-13 number is nil or non valid. Please note that only ISBN-13 numbers starting
|
133
|
+
# with 978 can be converted.
|
134
|
+
def ISBN_Tools.isbn13_to_isbn10(isbn_)
|
135
|
+
isbn = cleanup(isbn_)
|
136
|
+
isbn[3..11] + compute_isbn10_check_digit(isbn[3..11]) unless isbn.nil? or ! is_valid_isbn13?(isbn) or ! isbn_.match(/^978.*/)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Hyphenate a valid ISBN-10 number. Returns nil if the number is invalid or if the group range is
|
140
|
+
# unknown. Works only for groups 0,1 and 2.
|
141
|
+
def ISBN_Tools.hyphenate_isbn10(isbn_)
|
142
|
+
isbn = cleanup(isbn_)
|
143
|
+
group = isbn[0..0]
|
144
|
+
if RNG.has_key?(group) and is_valid_isbn10?(isbn)
|
145
|
+
RNG[group].each { |r| return isbn.sub(Regexp.new("(.{1})(.{#{r.last.length}})(.{#{8-r.last.length}})(.)"),'\1-\2-\3-\4') if r.member?(isbn[1..r.last.length]) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Hyphenate a valid ISBN-13 number. Returns nil if the number is invalid or if the group range is
|
150
|
+
# unknown. Works only for groups 0,1 and 2.
|
151
|
+
def ISBN_Tools.hyphenate_isbn13(isbn_)
|
152
|
+
isbn = cleanup(isbn_)
|
153
|
+
if is_valid_isbn13?(isbn)
|
154
|
+
group = isbn[3..3]
|
155
|
+
if RNG.has_key?(group)
|
156
|
+
RNG[group].each { |r| return isbn.sub(Regexp.new("(.{3})(.{1})(.{#{r.last.length}})(.{#{8-r.last.length}})(.)"),'\1-\2-\3-\4-\5') if r.member?(isbn[1..r.last.length]) }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# This method takes an ISBN then tries to hyphenate it as an ISBN 10 then an ISBN 13. A bit slower
|
162
|
+
# than calling the right one directly but saves you the length checking. Returns an hyphenated value
|
163
|
+
# or nil.
|
164
|
+
def ISBN_Tools.hyphenate(isbn_)
|
165
|
+
hyphenate_isbn10(isbn_) || hyphenate_isbn13(isbn_)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Same as hyphenate() but alters the argument.
|
169
|
+
def ISBN_Tools.hyphenate!(isbn_)
|
170
|
+
isbn_.replace(hyphenate(isbn_))
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
data/test/base_test.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
CampusBooks.api_key = 'SmT8KuLkGvy7SexotRTB'
|
7
|
+
end
|
8
|
+
|
9
|
+
should "parse OK response and return just the response" do
|
10
|
+
expected_response = {
|
11
|
+
"status"=>"ok",
|
12
|
+
"version"=>"1",
|
13
|
+
"page" => {
|
14
|
+
"name"=>"bookinfo",
|
15
|
+
"rating"=>"5.0",
|
16
|
+
"category"=>nil,
|
17
|
+
"title"=>"The Ruby Programming Language",
|
18
|
+
"isbn10"=>"0596516177",
|
19
|
+
"tried_amazon"=>"1",
|
20
|
+
"author"=>"David Flanagan - Yukihiro Matsumoto",
|
21
|
+
"msrp"=>"39.99",
|
22
|
+
"rank"=>"19257",
|
23
|
+
"isbn13"=>"9780596516178",
|
24
|
+
"publisher"=>"O'Reilly Media, Inc.",
|
25
|
+
"pages"=>"446",
|
26
|
+
"edition"=>"Paperback",
|
27
|
+
"binding"=>"Paperback",
|
28
|
+
"published_date"=>"2008-01-25",
|
29
|
+
"image"=>"http://ecx.images-amazon.com/images/I/41n-JSlBHkL._SL75_.jpg"
|
30
|
+
},
|
31
|
+
"label"=>{
|
32
|
+
"name"=>"Scheduleman Tartan",
|
33
|
+
"plid"=>"1709"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
CampusBooks::Base.expects(:get).once.returns({
|
37
|
+
"response" => expected_response
|
38
|
+
})
|
39
|
+
actual_response = CampusBooks::Base.get_response('/bookinfo', :query => { :isbn => '9780596516178' })
|
40
|
+
assert_equal expected_response, actual_response
|
41
|
+
end
|
42
|
+
|
43
|
+
should "raise an exception when an API error occurs" do
|
44
|
+
CampusBooks::Base.expects(:get).once.returns({
|
45
|
+
"response" => {
|
46
|
+
"status" => "error",
|
47
|
+
"errors" => {
|
48
|
+
"error" => [
|
49
|
+
"Permission Denied - Invalid or missing API Key",
|
50
|
+
"'isbn' parameter is required",
|
51
|
+
"'' is not a valid ISBN",
|
52
|
+
"Unable to create a book with isbn "
|
53
|
+
]
|
54
|
+
},
|
55
|
+
"version" => "3"
|
56
|
+
}
|
57
|
+
})
|
58
|
+
exception = assert_raise CampusBooks::Error do
|
59
|
+
CampusBooks::Base.get_response('/bookinfo', :query => { :isbn => '' })
|
60
|
+
end
|
61
|
+
assert_equal %Q{4 errors occured while getting path '/bookinfo' with options {:query=>{:isbn=>""}}:
|
62
|
+
Permission Denied - Invalid or missing API Key
|
63
|
+
'isbn' parameter is required
|
64
|
+
'' is not a valid ISBN
|
65
|
+
Unable to create a book with isbn }, exception.message
|
66
|
+
end
|
67
|
+
end
|
data/test/book_test.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class BookTest < Test::Unit::TestCase
|
4
|
+
VALID_ISBN13 = '9780596516178'
|
5
|
+
VALID_ISBN10 = '0596516177'
|
6
|
+
|
7
|
+
def setup
|
8
|
+
CampusBooks.api_key = 'SmT8KuLkGvy7SexotRTB' # 'AbC123dEFG4H5jkl6MNO'
|
9
|
+
end
|
10
|
+
|
11
|
+
should "store ISBN from constructor" do
|
12
|
+
mock_successful_request
|
13
|
+
book = CampusBooks::Book.find(VALID_ISBN13)
|
14
|
+
assert_equal VALID_ISBN13, book.isbn
|
15
|
+
end
|
16
|
+
|
17
|
+
should "convert ISBN10 to ISBN13" do
|
18
|
+
mock_successful_request
|
19
|
+
book = CampusBooks::Book.find(VALID_ISBN10)
|
20
|
+
assert_equal VALID_ISBN13, book.isbn
|
21
|
+
end
|
22
|
+
|
23
|
+
should "validate ISBN during construction" do
|
24
|
+
assert_nothing_raised do
|
25
|
+
mock_successful_request
|
26
|
+
CampusBooks::Book.find(VALID_ISBN10)
|
27
|
+
end
|
28
|
+
assert_raise ArgumentError, 'isbn is invalid' do
|
29
|
+
CampusBooks::Book.find('0-596-51617-8')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should "get valid values" do
|
34
|
+
mock_successful_request
|
35
|
+
book = CampusBooks::Book.find(VALID_ISBN13)
|
36
|
+
assert_equal VALID_ISBN10, book.isbn10
|
37
|
+
assert_equal VALID_ISBN13, book.isbn13
|
38
|
+
assert_equal VALID_ISBN13, book.isbn
|
39
|
+
assert_equal 'The Ruby Programming Language', book.title
|
40
|
+
assert_equal 'David Flanagan - Yukihiro Matsumoto', book.author
|
41
|
+
assert_equal 'Paperback', book.binding
|
42
|
+
assert_equal '39.99', book.msrp
|
43
|
+
assert_equal '446', book.pages
|
44
|
+
assert_equal "O'Reilly Media, Inc.", book.publisher
|
45
|
+
assert_equal Date.parse('2008-01-25'), book.published_date
|
46
|
+
assert_equal 'Paperback', book.edition
|
47
|
+
assert_equal 'Foo bar', book.description
|
48
|
+
end
|
49
|
+
|
50
|
+
should "include prices" do
|
51
|
+
book = CampusBooks::Book.find(VALID_ISBN13, :include => :prices)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def mock_successful_request
|
56
|
+
CampusBooks::Book.expects(:get).once.returns({
|
57
|
+
"response" => {
|
58
|
+
"status"=>"ok",
|
59
|
+
"version"=>"1",
|
60
|
+
"page" => {
|
61
|
+
"name"=>"bookinfo",
|
62
|
+
"rating"=>"5.0",
|
63
|
+
"category"=>nil,
|
64
|
+
"title"=>"The Ruby Programming Language",
|
65
|
+
"isbn10"=>"0596516177",
|
66
|
+
"tried_amazon"=>"1",
|
67
|
+
"author"=>"David Flanagan - Yukihiro Matsumoto",
|
68
|
+
"msrp"=>"39.99",
|
69
|
+
"rank"=>"19257",
|
70
|
+
"isbn13"=>"9780596516178",
|
71
|
+
"publisher"=>"O'Reilly Media, Inc.",
|
72
|
+
"pages"=>"446",
|
73
|
+
"edition"=>"Paperback",
|
74
|
+
"binding"=>"Paperback",
|
75
|
+
"published_date"=>"2008-01-25",
|
76
|
+
"image"=>"http://ecx.images-amazon.com/images/I/41n-JSlBHkL._SL75_.jpg",
|
77
|
+
"description" => 'Foo bar'
|
78
|
+
},
|
79
|
+
"label"=>{
|
80
|
+
"name"=>"Scheduleman Tartan",
|
81
|
+
"plid"=>"1709"
|
82
|
+
}
|
83
|
+
}
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
def mock_failed_request(isbn = '0-596-51617-8')
|
88
|
+
CampusBooks::Book.expects(:get).once.returns({
|
89
|
+
"response" => {
|
90
|
+
"status" => "error",
|
91
|
+
"errors" => {
|
92
|
+
"error" => [
|
93
|
+
"'#{isbn}' is not a valid ISBN"
|
94
|
+
]
|
95
|
+
},
|
96
|
+
"version" => "3"
|
97
|
+
}
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
data/test/offer_test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OfferTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "set params in constructor" do
|
6
|
+
offer = CampusBooks::Offer.new({
|
7
|
+
"merchant_id" => "24",
|
8
|
+
"price"=>"11.93",
|
9
|
+
"shipping_ground"=>"3.99",
|
10
|
+
"condition_id"=>"2",
|
11
|
+
"availability_id"=>"2",
|
12
|
+
"comments"=>"Good condition. Absolutely no highlighting or markings inside the books. Decent covers subject to prior use.",
|
13
|
+
"isbn10"=>"0596516177",
|
14
|
+
"total_price"=>"15.92",
|
15
|
+
"isbn13"=>"9780596516178",
|
16
|
+
"merchant_name"=>"Amazon Marketplace",
|
17
|
+
"availability_text"=>"Ready to ship",
|
18
|
+
"link"=>"http://partners.campusbooks.com/link.php?params=ABCDEF",
|
19
|
+
"condition_text"=>"Used"
|
20
|
+
})
|
21
|
+
#assert_equal '9780596516178', offer.isbn
|
22
|
+
assert_equal '9780596516178', offer.isbn13
|
23
|
+
assert_equal '0596516177', offer.isbn10
|
24
|
+
assert_equal '24', offer.merchant_id
|
25
|
+
assert_equal 'Amazon Marketplace', offer.merchant_name
|
26
|
+
assert_equal '11.93', offer.price
|
27
|
+
assert_equal '3.99', offer.shipping_ground
|
28
|
+
assert_equal '15.92', offer.total_price
|
29
|
+
assert_equal 'http://partners.campusbooks.com/link.php?params=ABCDEF', offer.link
|
30
|
+
assert_equal '2', offer.condition_id
|
31
|
+
assert_equal 'Used', offer.condition_text
|
32
|
+
assert_equal '2', offer.availability_id
|
33
|
+
assert_equal 'Ready to ship', offer.availability_text
|
34
|
+
|
35
|
+
# FIXME: Need an example of these
|
36
|
+
# assert_equal 'foo', offer.location
|
37
|
+
# assert_equal 'foo', offer.their_id
|
38
|
+
# assert_equal 'foo', offer.comments
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
begin
|
4
|
+
require 'shoulda'
|
5
|
+
rescue LoadError
|
6
|
+
puts "Shoulda not available. Install it with: sudo gem install thoughtbot-shoulda -s http://gems.github.com"
|
7
|
+
end
|
8
|
+
begin
|
9
|
+
require 'mocha'
|
10
|
+
rescue LoadError
|
11
|
+
puts "Mocha not available. Install it with: sudo gem install mocha"
|
12
|
+
end
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
require 'campusbooks'
|
17
|
+
|
18
|
+
class Test::Unit::TestCase
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mroch-campusbooks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marshall Roch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-27 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: httparty
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: A Ruby library for accessing the CampusBooks.com API
|
26
|
+
email: marshall@mroch.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
- LICENSE
|
34
|
+
files:
|
35
|
+
- README.rdoc
|
36
|
+
- VERSION.yml
|
37
|
+
- lib/campusbooks
|
38
|
+
- lib/campusbooks/base.rb
|
39
|
+
- lib/campusbooks/book.rb
|
40
|
+
- lib/campusbooks/offer.rb
|
41
|
+
- lib/campusbooks.rb
|
42
|
+
- lib/isbn
|
43
|
+
- lib/isbn/LICENCE
|
44
|
+
- lib/isbn/README
|
45
|
+
- lib/isbn/tools.rb
|
46
|
+
- test/base_test.rb
|
47
|
+
- test/book_test.rb
|
48
|
+
- test/offer_test.rb
|
49
|
+
- test/test_helper.rb
|
50
|
+
- LICENSE
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/mroch/campusbooks
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --inline-source
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.2.0
|
75
|
+
signing_key:
|
76
|
+
specification_version: 2
|
77
|
+
summary: CampusBooks API for Ruby
|
78
|
+
test_files: []
|
79
|
+
|