mroch-campusbooks 0.1.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/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
|
+
|