campusbooks 0.2.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/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 +106 -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/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,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: campusbooks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Marshall Roch
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2009-08-31 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: httparty
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: isbn-tools
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
description: A Ruby library for accessing the CampusBooks.com API
|
50
|
+
email: marshall@mroch.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README.rdoc
|
57
|
+
- LICENSE
|
58
|
+
files:
|
59
|
+
- README.rdoc
|
60
|
+
- VERSION.yml
|
61
|
+
- lib/campusbooks/base.rb
|
62
|
+
- lib/campusbooks/book.rb
|
63
|
+
- lib/campusbooks/offer.rb
|
64
|
+
- lib/campusbooks.rb
|
65
|
+
- test/base_test.rb
|
66
|
+
- test/book_test.rb
|
67
|
+
- test/offer_test.rb
|
68
|
+
- test/test_helper.rb
|
69
|
+
- LICENSE
|
70
|
+
has_rdoc: true
|
71
|
+
homepage: http://github.com/mroch/campusbooks
|
72
|
+
licenses: []
|
73
|
+
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options:
|
76
|
+
- --inline-source
|
77
|
+
- --charset=UTF-8
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 2
|
104
|
+
summary: CampusBooks API for Ruby
|
105
|
+
test_files: []
|
106
|
+
|