goodreads 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/README.md +125 -0
- data/Rakefile +3 -2
- data/goodreads.gemspec +12 -16
- data/lib/goodreads.rb +40 -5
- data/lib/goodreads/client.rb +24 -87
- data/lib/goodreads/client/authors.rb +11 -0
- data/lib/goodreads/client/books.rb +35 -0
- data/lib/goodreads/client/reviews.rb +24 -0
- data/lib/goodreads/client/users.rb +10 -0
- data/lib/goodreads/errors.rb +6 -0
- data/lib/goodreads/request.rb +43 -0
- data/lib/goodreads/version.rb +3 -1
- data/spec/authentication_spec.rb +11 -7
- data/spec/client_spec.rb +23 -15
- data/spec/goodreads_spec.rb +54 -0
- data/spec/spec_helper.rb +5 -4
- metadata +93 -161
- data/README.rdoc +0 -66
- data/lib/goodreads/goodreads.rb +0 -12
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Goodreads API wrapper
|
2
|
+
|
3
|
+
Ruby library to connect with Goodreads API.
|
4
|
+
|
5
|
+
[![Build Status](https://secure.travis-ci.org/sosedoff/goodreads.png)](http://travis-ci.org/sosedoff/goodreads)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
You can install this library via rubygems:
|
10
|
+
|
11
|
+
```
|
12
|
+
gem install goodreads
|
13
|
+
```
|
14
|
+
|
15
|
+
Or using rake:
|
16
|
+
|
17
|
+
```
|
18
|
+
rake install
|
19
|
+
```
|
20
|
+
|
21
|
+
## Getting Started
|
22
|
+
|
23
|
+
In order to use Goodreads API you must obtain an API key for your account.
|
24
|
+
|
25
|
+
Setup client:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
client = Goodreads::Client.new(:api_key => 'YOUR_KEY')
|
29
|
+
|
30
|
+
# or using a shortcut
|
31
|
+
client = Goodreads.new(:api_key => 'YOUR_KEY')
|
32
|
+
```
|
33
|
+
|
34
|
+
### Global configuration
|
35
|
+
|
36
|
+
Library allows you to define a global configuration options.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Goodreads.configure(:api_key => 'YOUR_KEY')
|
40
|
+
```
|
41
|
+
|
42
|
+
Get current options:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Goodreads.configuration # => {:api_key => 'YOUR_KEY'}
|
46
|
+
```
|
47
|
+
|
48
|
+
In case you need to reset options:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Goodreads.reset_configuration
|
52
|
+
```
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
### Lookup books
|
57
|
+
|
58
|
+
Find a book by ISBN:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
book = client.book_by_isbn('ISBN')
|
62
|
+
```
|
63
|
+
|
64
|
+
Find a book by Goodreads ID:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
book = client.book('id')
|
68
|
+
```
|
69
|
+
|
70
|
+
Find a book by title:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
book = client.book_by_title('Book title')
|
74
|
+
```
|
75
|
+
|
76
|
+
Search for books (by title, isbn, genre):
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
search = client.search_books('Your search query')
|
80
|
+
search.results.work.each do |book|
|
81
|
+
book.id # => book ID
|
82
|
+
book.title # => book title
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
### Reviews
|
87
|
+
|
88
|
+
Pull recent reviews:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
client.recent_reviews.each do |r|
|
92
|
+
r.id # => review id
|
93
|
+
r.book.title # => review book title
|
94
|
+
r.body # => review message
|
95
|
+
r.user.name # => review user name
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
Get review details:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
review = client.review('id')
|
103
|
+
review.id # => ID
|
104
|
+
review.user # => User information
|
105
|
+
review.book # => Book information
|
106
|
+
review.rating # => User rating
|
107
|
+
```
|
108
|
+
|
109
|
+
## Contributions
|
110
|
+
|
111
|
+
Feel free to contribute any patches or new features.
|
112
|
+
|
113
|
+
Make sure to add a test coverage so it does not break any existing code.
|
114
|
+
|
115
|
+
For documentation please visit [API Reference](http://www.goodreads.com/api)
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
Copyright © 2011-2012 Dan Sosedoff.
|
120
|
+
|
121
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
122
|
+
|
123
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
124
|
+
|
125
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'bundler'
|
2
|
-
require
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
3
4
|
|
4
5
|
RSpec::Core::RakeTask.new(:test) do |t|
|
5
6
|
t.pattern = 'spec/*_spec.rb'
|
6
7
|
t.verbose = false
|
7
8
|
end
|
8
9
|
|
9
|
-
task :default => :test
|
10
|
+
task :default => :test
|
data/goodreads.gemspec
CHANGED
@@ -9,23 +9,19 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Dan Sosedoff"]
|
10
10
|
s.email = ["dan.sosedoff@gmail.com"]
|
11
11
|
|
12
|
-
s.add_development_dependency 'webmock',
|
13
|
-
s.add_development_dependency 'rake',
|
14
|
-
s.add_development_dependency 'rspec',
|
15
|
-
s.add_development_dependency 'ZenTest', '~> 4.5'
|
12
|
+
s.add_development_dependency 'webmock', '~> 1.6'
|
13
|
+
s.add_development_dependency 'rake', '~> 0.8'
|
14
|
+
s.add_development_dependency 'rspec', '~> 2.5'
|
16
15
|
s.add_development_dependency 'simplecov', '~> 0.4'
|
17
|
-
s.add_development_dependency 'yard',
|
16
|
+
s.add_development_dependency 'yard', '~> 0.6'
|
18
17
|
|
19
|
-
s.add_runtime_dependency 'rest-client',
|
20
|
-
s.add_runtime_dependency 'hashie',
|
21
|
-
s.add_runtime_dependency 'activesupport', '~> 3
|
22
|
-
s.add_runtime_dependency 'i18n',
|
23
|
-
|
24
|
-
s.files
|
25
|
-
s.test_files
|
26
|
-
s.executables
|
18
|
+
s.add_runtime_dependency 'rest-client', '~> 1.6'
|
19
|
+
s.add_runtime_dependency 'hashie', '~> 1.0'
|
20
|
+
s.add_runtime_dependency 'activesupport', '~> 3'
|
21
|
+
s.add_runtime_dependency 'i18n', '~> 0.5'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
27
26
|
s.require_paths = ["lib"]
|
28
|
-
|
29
|
-
s.platform = Gem::Platform::RUBY
|
30
|
-
s.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if s.respond_to? :required_rubygems_version=
|
31
27
|
end
|
data/lib/goodreads.rb
CHANGED
@@ -1,6 +1,41 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'goodreads/version'
|
2
|
+
require 'goodreads/errors'
|
3
|
+
require 'goodreads/request'
|
4
|
+
require 'goodreads/client'
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
module Goodreads
|
7
|
+
@@options = {}
|
8
|
+
|
9
|
+
# Create a new Goodreads::Client instance
|
10
|
+
#
|
11
|
+
def self.new(options={})
|
12
|
+
Goodreads::Client.new(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Define a global configuration
|
16
|
+
#
|
17
|
+
# options[:api_key] - Account API key
|
18
|
+
# options[:api_secret] - Account API secret
|
19
|
+
#
|
20
|
+
def self.configure(options={})
|
21
|
+
unless options.kind_of?(Hash)
|
22
|
+
raise ArgumentError, "Options hash required."
|
23
|
+
end
|
24
|
+
|
25
|
+
@@options[:api_key] = options[:api_key]
|
26
|
+
@@options[:api_secret] = options[:api_secret]
|
27
|
+
@@options
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns global configuration hash
|
31
|
+
#
|
32
|
+
def self.configuration
|
33
|
+
@@options
|
34
|
+
end
|
35
|
+
|
36
|
+
# Resets the global configuration
|
37
|
+
#
|
38
|
+
def self.reset_configuration
|
39
|
+
@@options = {}
|
40
|
+
end
|
41
|
+
end
|
data/lib/goodreads/client.rb
CHANGED
@@ -1,94 +1,31 @@
|
|
1
|
+
require 'goodreads/client'
|
2
|
+
require 'goodreads/client/books'
|
3
|
+
require 'goodreads/client/reviews'
|
4
|
+
require 'goodreads/client/authors'
|
5
|
+
require 'goodreads/client/users'
|
6
|
+
|
1
7
|
module Goodreads
|
2
8
|
class Client
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def search_books(q, params={})
|
20
|
-
params[:q] = q.to_s.strip
|
21
|
-
data = request('/search/index', params)
|
22
|
-
Hashie::Mash.new(data['search'])
|
23
|
-
end
|
24
|
-
|
25
|
-
# Get book details by Goodreads book ID
|
26
|
-
def book(id)
|
27
|
-
Hashie::Mash.new(request('/book/show', :id => id)['book'])
|
28
|
-
end
|
29
|
-
|
30
|
-
# Get book details by book ISBN
|
31
|
-
def book_by_isbn(isbn)
|
32
|
-
Hashie::Mash.new(request('/book/isbn', :isbn => isbn)['book'])
|
33
|
-
end
|
34
|
-
|
35
|
-
# Get book details by book title
|
36
|
-
def book_by_title(title)
|
37
|
-
Hashie::Mash.new(request('/book/title', :title => title)['book'])
|
38
|
-
end
|
39
|
-
|
40
|
-
# Recent reviews from all members.
|
41
|
-
# :skip_cropped - Select only non-cropped reviews
|
42
|
-
def recent_reviews(params={})
|
43
|
-
skip_cropped = params.delete(:skip_cropped) || false
|
44
|
-
data = request('/review/recent_reviews', params)
|
45
|
-
if data['reviews'] && data['reviews'].key?('review')
|
46
|
-
reviews = data['reviews']['review'].map { |r| Hashie::Mash.new(r) }
|
47
|
-
reviews = reviews.select { |r| !r.body.include?(r.url) } if skip_cropped
|
48
|
-
reviews
|
9
|
+
include Goodreads::Request
|
10
|
+
include Goodreads::Books
|
11
|
+
include Goodreads::Reviews
|
12
|
+
include Goodreads::Authors
|
13
|
+
include Goodreads::Users
|
14
|
+
|
15
|
+
attr_reader :api_key, :api_secret
|
16
|
+
|
17
|
+
# Initialize a Goodreads::Client instance
|
18
|
+
#
|
19
|
+
# options[:api_key] - Account API key
|
20
|
+
# options[:api_secret] - Account API secret
|
21
|
+
#
|
22
|
+
def initialize(options={})
|
23
|
+
unless options.kind_of?(Hash)
|
24
|
+
raise ArgumentError, "Options hash required."
|
49
25
|
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Get review details
|
53
|
-
def review(id)
|
54
|
-
data = request('/review/show', :id => id)
|
55
|
-
Hashie::Mash.new(data['review'])
|
56
|
-
end
|
57
|
-
|
58
|
-
# Get author details
|
59
|
-
def author(id, params={})
|
60
|
-
params[:id] = id
|
61
|
-
data = request('/author/show', params)
|
62
|
-
Hashie::Mash.new(data['author'])
|
63
|
-
end
|
64
|
-
|
65
|
-
# Get user details
|
66
|
-
def user(id)
|
67
|
-
data = request('/user/show', :id => id)
|
68
|
-
Hashie::Mash.new(data['user'])
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
# Perform an API request
|
74
|
-
def request(path, params={})
|
75
|
-
raise 'API key required!' unless @@config.key?(:api_key)
|
76
|
-
params.merge!(:format => 'xml', :key => @@config[:api_key])
|
77
|
-
|
78
|
-
resp = RestClient.get("#{API_URL}#{path}", :params => params) { |response, request, result, &block|
|
79
|
-
case response.code
|
80
|
-
when 200
|
81
|
-
response.return!(request, result, &block)
|
82
|
-
when 401
|
83
|
-
raise Goodreads::Unauthorized
|
84
|
-
when 404
|
85
|
-
raise Goodreads::NotFound
|
86
|
-
end
|
87
|
-
}
|
88
26
|
|
89
|
-
|
90
|
-
|
91
|
-
hash
|
27
|
+
@api_key = options[:api_key]
|
28
|
+
@api_secret = options[:api_secret]
|
92
29
|
end
|
93
30
|
end
|
94
31
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Goodreads
|
2
|
+
module Books
|
3
|
+
# Search for books
|
4
|
+
#
|
5
|
+
# query - Text to match against book title, author, and ISBN fields.
|
6
|
+
# options - Optional search parameters
|
7
|
+
#
|
8
|
+
# options[:page] - Which page to returns (default: 1)
|
9
|
+
# options[:field] - Search field. One of: title, author, or genre (default is all)
|
10
|
+
#
|
11
|
+
def search_books(query, params={})
|
12
|
+
params[:q] = query.to_s.strip
|
13
|
+
data = request('/search/index', params)
|
14
|
+
Hashie::Mash.new(data['search'])
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get book details by Goodreads book ID
|
18
|
+
#
|
19
|
+
def book(id)
|
20
|
+
Hashie::Mash.new(request('/book/show', :id => id)['book'])
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get book details by book ISBN
|
24
|
+
#
|
25
|
+
def book_by_isbn(isbn)
|
26
|
+
Hashie::Mash.new(request('/book/isbn', :isbn => isbn)['book'])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get book details by book title
|
30
|
+
#
|
31
|
+
def book_by_title(title)
|
32
|
+
Hashie::Mash.new(request('/book/title', :title => title)['book'])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Goodreads
|
2
|
+
module Reviews
|
3
|
+
# Recent reviews from all members.
|
4
|
+
#
|
5
|
+
# params[:skip_cropped] - Select only non-cropped reviews
|
6
|
+
#
|
7
|
+
def recent_reviews(params={})
|
8
|
+
skip_cropped = params.delete(:skip_cropped) || false
|
9
|
+
data = request('/review/recent_reviews', params)
|
10
|
+
if data['reviews'] && data['reviews'].key?('review')
|
11
|
+
reviews = data['reviews']['review'].map { |r| Hashie::Mash.new(r) }
|
12
|
+
reviews = reviews.select { |r| !r.body.include?(r.url) } if skip_cropped
|
13
|
+
reviews
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get review details
|
18
|
+
#
|
19
|
+
def review(id)
|
20
|
+
data = request('/review/show', :id => id)
|
21
|
+
Hashie::Mash.new(data['review'])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'hashie'
|
4
|
+
|
5
|
+
module Goodreads
|
6
|
+
module Request
|
7
|
+
API_URL = 'http://www.goodreads.com'
|
8
|
+
API_FORMAT = 'xml'
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Perform an API request
|
13
|
+
#
|
14
|
+
# path - Request path
|
15
|
+
# params - Parameters hash
|
16
|
+
#
|
17
|
+
def request(path, params={})
|
18
|
+
token = api_key || Goodreads.configuration[:api_key]
|
19
|
+
|
20
|
+
if token.nil?
|
21
|
+
raise Goodreads::ConfigurationError, 'API key required.'
|
22
|
+
end
|
23
|
+
|
24
|
+
params.merge!(:format => API_FORMAT, :key => token)
|
25
|
+
url = "#{API_URL}#{path}"
|
26
|
+
|
27
|
+
resp = RestClient.get(url, :params => params) do |response, request, result, &block|
|
28
|
+
case response.code
|
29
|
+
when 200
|
30
|
+
response.return!(request, result, &block)
|
31
|
+
when 401
|
32
|
+
raise Goodreads::Unauthorized
|
33
|
+
when 404
|
34
|
+
raise Goodreads::NotFound
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
hash = Hash.from_xml(resp)['GoodreadsResponse']
|
39
|
+
hash.delete('Request')
|
40
|
+
hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/goodreads/version.rb
CHANGED
data/spec/authentication_spec.rb
CHANGED
@@ -2,19 +2,23 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'Client' do
|
4
4
|
before :each do
|
5
|
-
|
5
|
+
Goodreads.reset_configuration
|
6
6
|
end
|
7
7
|
|
8
|
-
it '
|
9
|
-
|
8
|
+
it 'raises Goodreads::ConfigurationError if API key was not provided' do
|
9
|
+
client = Goodreads::Client.new
|
10
|
+
|
11
|
+
proc { client.book_by_isbn('0307463745') }.
|
12
|
+
should raise_error Goodreads::ConfigurationError, 'API key required.'
|
10
13
|
end
|
11
14
|
|
12
|
-
it '
|
13
|
-
Goodreads.
|
15
|
+
it 'raises Goodreads::Unauthorized if API key is not valid' do
|
16
|
+
client = Goodreads::Client.new(:api_key => 'INVALID_KEY')
|
17
|
+
|
14
18
|
stub_request(:get, "http://www.goodreads.com/book/isbn?format=xml&isbn=054748250711&key=INVALID_KEY").
|
15
19
|
to_return(:status => 401)
|
16
20
|
|
17
|
-
proc {
|
21
|
+
proc { client.book_by_isbn('054748250711') }.
|
22
|
+
should raise_error Goodreads::Unauthorized
|
18
23
|
end
|
19
24
|
end
|
20
|
-
|
data/spec/client_spec.rb
CHANGED
@@ -2,11 +2,19 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'Client' do
|
4
4
|
before :each do
|
5
|
-
Goodreads.
|
6
|
-
@client = Goodreads::Client.new
|
5
|
+
Goodreads.reset_configuration
|
6
|
+
@client = Goodreads::Client.new(:api_key => 'SECRET_KEY')
|
7
7
|
end
|
8
8
|
|
9
|
-
it '
|
9
|
+
it 'raises error on invalid config parameter' do
|
10
|
+
proc { Goodreads::Client.new(nil) }.
|
11
|
+
should raise_error ArgumentError, "Options hash required."
|
12
|
+
|
13
|
+
proc { Goodreads::Client.new('foo') }.
|
14
|
+
should raise_error ArgumentError, "Options hash required."
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns a book by isbn' do
|
10
18
|
stub_with_key_get('/book/isbn', {:isbn => '0307463745'}, 'book.xml')
|
11
19
|
|
12
20
|
proc { @book = @client.book_by_isbn('0307463745') }.should_not raise_error
|
@@ -14,24 +22,24 @@ describe 'Client' do
|
|
14
22
|
@book.respond_to?(:title).should == true
|
15
23
|
end
|
16
24
|
|
17
|
-
it '
|
25
|
+
it 'returns a book by goodreads id' do
|
18
26
|
stub_with_key_get('/book/show', {:id => '6732019'}, 'book.xml')
|
19
27
|
proc { @client.book('6732019') }.should_not raise_error
|
20
28
|
end
|
21
29
|
|
22
|
-
it '
|
30
|
+
it 'returns a book by title' do
|
23
31
|
stub_with_key_get('/book/title', {:title => 'Rework'}, 'book.xml')
|
24
32
|
proc { @client.book_by_title('Rework') }.should_not raise_error
|
25
33
|
end
|
26
34
|
|
27
|
-
it '
|
35
|
+
it 'raises Goodreads::NotFound if book was not found' do
|
28
36
|
stub_request(:get, "http://www.goodreads.com/book/isbn?format=xml&isbn=123456789&key=SECRET_KEY").
|
29
37
|
to_return(:status => 404, :body => "", :headers => {})
|
30
38
|
|
31
39
|
proc { @client.book_by_isbn('123456789') }.should raise_error Goodreads::NotFound
|
32
40
|
end
|
33
41
|
|
34
|
-
it '
|
42
|
+
it 'returns recent reviews' do
|
35
43
|
stub_with_key_get('/review/recent_reviews', {}, 'recent_reviews.xml')
|
36
44
|
|
37
45
|
proc { @reviews = @client.recent_reviews }.should_not raise_error
|
@@ -42,7 +50,7 @@ describe 'Client' do
|
|
42
50
|
end
|
43
51
|
end
|
44
52
|
|
45
|
-
it '
|
53
|
+
it 'returns only full reviewes' do
|
46
54
|
stub_with_key_get('/review/recent_reviews', {}, 'recent_reviews.xml')
|
47
55
|
|
48
56
|
proc { @reviews = @client.recent_reviews(:skip_cropped => true) }.should_not raise_error
|
@@ -53,7 +61,7 @@ describe 'Client' do
|
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
56
|
-
it '
|
64
|
+
it 'returns review details' do
|
57
65
|
stub_with_key_get('/review/show', {:id => '166204831'}, 'review.xml')
|
58
66
|
|
59
67
|
proc { @review = @client.review('166204831') }.should_not raise_error
|
@@ -62,14 +70,14 @@ describe 'Client' do
|
|
62
70
|
@review.id.should == '166204831'
|
63
71
|
end
|
64
72
|
|
65
|
-
it '
|
73
|
+
it 'raises Goodreads::NotFound if review was not found' do
|
66
74
|
stub_request(:get, "http://www.goodreads.com/review/show?format=xml&id=12345&key=SECRET_KEY").
|
67
75
|
to_return(:status => 404, :body => "", :headers => {})
|
68
76
|
|
69
77
|
proc { @client.review('12345') }.should raise_error Goodreads::NotFound
|
70
78
|
end
|
71
79
|
|
72
|
-
it '
|
80
|
+
it 'returns author details' do
|
73
81
|
stub_with_key_get('/author/show', {:id => '18541'}, 'author.xml')
|
74
82
|
|
75
83
|
proc { @author = @client.author('18541') }.should_not raise_error
|
@@ -79,14 +87,14 @@ describe 'Client' do
|
|
79
87
|
@author.name.should == "Tim O'Reilly"
|
80
88
|
end
|
81
89
|
|
82
|
-
it '
|
90
|
+
it 'raises Goodreads::NotFound if author was not found' do
|
83
91
|
stub_request(:get, "http://www.goodreads.com/author/show?format=xml&id=12345&key=SECRET_KEY").
|
84
92
|
to_return(:status => 404, :body => "", :headers => {})
|
85
93
|
|
86
94
|
proc { @client.author('12345') }.should raise_error Goodreads::NotFound
|
87
95
|
end
|
88
96
|
|
89
|
-
it '
|
97
|
+
it 'returns user details' do
|
90
98
|
stub_with_key_get('/user/show', {:id => '878044'}, 'user.xml')
|
91
99
|
|
92
100
|
proc { @user = @client.user('878044') }.should_not raise_error
|
@@ -97,14 +105,14 @@ describe 'Client' do
|
|
97
105
|
@user.user_name.should == 'janmt'
|
98
106
|
end
|
99
107
|
|
100
|
-
it '
|
108
|
+
it 'raiser Goodreads::NotFound if user was not found' do
|
101
109
|
stub_request(:get, "http://www.goodreads.com/user/show?format=xml&id=12345&key=SECRET_KEY").
|
102
110
|
to_return(:status => 404, :body => "", :headers => {})
|
103
111
|
|
104
112
|
proc { @client.user('12345') }.should raise_error Goodreads::NotFound
|
105
113
|
end
|
106
114
|
|
107
|
-
it '
|
115
|
+
it 'returns book search results' do
|
108
116
|
stub_with_key_get('/search/index', {:q => 'Rework'}, 'search_books_by_name.xml')
|
109
117
|
|
110
118
|
proc { @search = @client.search_books('Rework') }.should_not raise_error
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Goodreads' do
|
4
|
+
context '.new' do
|
5
|
+
it 'returns a new client instance' do
|
6
|
+
Goodreads.new.should be_a Goodreads::Client
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context '.configure' do
|
11
|
+
it 'sets a global configuration options' do
|
12
|
+
r = Goodreads.configure(:api_key => 'FOO', :api_secret => 'BAR')
|
13
|
+
r.should be_a Hash
|
14
|
+
r.should have_key(:api_key)
|
15
|
+
r.should have_key(:api_secret)
|
16
|
+
r[:api_key].should eql('FOO')
|
17
|
+
r[:api_secret].should eql('BAR')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises ConfigurationError on invalid config parameter' do
|
21
|
+
proc { Goodreads.new(nil) }.
|
22
|
+
should raise_error ArgumentError, "Options hash required."
|
23
|
+
|
24
|
+
proc { Goodreads.new('foo') }.
|
25
|
+
should raise_error ArgumentError, "Options hash required."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '.configuration' do
|
30
|
+
before do
|
31
|
+
Goodreads.configure(:api_key => 'FOO', :api_secret => 'BAR')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns global configuration options' do
|
35
|
+
r = Goodreads.configuration
|
36
|
+
r.should be_a Hash
|
37
|
+
r.should have_key(:api_key)
|
38
|
+
r.should have_key(:api_secret)
|
39
|
+
r[:api_key].should eql('FOO')
|
40
|
+
r[:api_secret].should eql('BAR')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '.reset_configuration' do
|
45
|
+
before do
|
46
|
+
Goodreads.configure(:api_key => 'FOO', :api_secret => 'BAR')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'resets global configuration options' do
|
50
|
+
Goodreads.reset_configuration
|
51
|
+
Goodreads.configuration.should eql({})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -24,14 +24,15 @@ def stub_with_key_get(path, params, fixture_name)
|
|
24
24
|
stub_get(path, params, fixture_name)
|
25
25
|
end
|
26
26
|
|
27
|
-
def fixture_path
|
28
|
-
File.expand_path("../fixtures", __FILE__)
|
27
|
+
def fixture_path(file=nil)
|
28
|
+
path = File.expand_path("../fixtures", __FILE__)
|
29
|
+
file.nil? ? path : File.join(path, file)
|
29
30
|
end
|
30
31
|
|
31
32
|
def fixture(file)
|
32
|
-
File.
|
33
|
+
File.read(fixture_path(file))
|
33
34
|
end
|
34
35
|
|
35
36
|
def api_url(path)
|
36
|
-
"#{Goodreads::API_URL}#{path}"
|
37
|
+
"#{Goodreads::Request::API_URL}#{path}"
|
37
38
|
end
|
metadata
CHANGED
@@ -1,195 +1,137 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: goodreads
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 1
|
10
|
-
version: 0.1.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Dan Sosedoff
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-01-26 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: webmock
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &2157623060 !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
18
|
+
requirements:
|
27
19
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 6
|
33
|
-
version: "1.6"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.6'
|
34
22
|
type: :development
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: rake
|
38
23
|
prerelease: false
|
39
|
-
|
24
|
+
version_requirements: *2157623060
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &2157622560 !ruby/object:Gem::Requirement
|
40
28
|
none: false
|
41
|
-
requirements:
|
29
|
+
requirements:
|
42
30
|
- - ~>
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
segments:
|
46
|
-
- 0
|
47
|
-
- 8
|
48
|
-
version: "0.8"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.8'
|
49
33
|
type: :development
|
50
|
-
version_requirements: *id002
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: rspec
|
53
34
|
prerelease: false
|
54
|
-
|
35
|
+
version_requirements: *2157622560
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &2157622100 !ruby/object:Gem::Requirement
|
55
39
|
none: false
|
56
|
-
requirements:
|
40
|
+
requirements:
|
57
41
|
- - ~>
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
|
60
|
-
segments:
|
61
|
-
- 2
|
62
|
-
- 5
|
63
|
-
version: "2.5"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.5'
|
64
44
|
type: :development
|
65
|
-
version_requirements: *id003
|
66
|
-
- !ruby/object:Gem::Dependency
|
67
|
-
name: ZenTest
|
68
45
|
prerelease: false
|
69
|
-
|
70
|
-
|
71
|
-
requirements:
|
72
|
-
- - ~>
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
hash: 17
|
75
|
-
segments:
|
76
|
-
- 4
|
77
|
-
- 5
|
78
|
-
version: "4.5"
|
79
|
-
type: :development
|
80
|
-
version_requirements: *id004
|
81
|
-
- !ruby/object:Gem::Dependency
|
46
|
+
version_requirements: *2157622100
|
47
|
+
- !ruby/object:Gem::Dependency
|
82
48
|
name: simplecov
|
83
|
-
|
84
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
49
|
+
requirement: &2157621640 !ruby/object:Gem::Requirement
|
85
50
|
none: false
|
86
|
-
requirements:
|
51
|
+
requirements:
|
87
52
|
- - ~>
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
|
90
|
-
segments:
|
91
|
-
- 0
|
92
|
-
- 4
|
93
|
-
version: "0.4"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.4'
|
94
55
|
type: :development
|
95
|
-
version_requirements: *id005
|
96
|
-
- !ruby/object:Gem::Dependency
|
97
|
-
name: yard
|
98
56
|
prerelease: false
|
99
|
-
|
57
|
+
version_requirements: *2157621640
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yard
|
60
|
+
requirement: &2157621180 !ruby/object:Gem::Requirement
|
100
61
|
none: false
|
101
|
-
requirements:
|
62
|
+
requirements:
|
102
63
|
- - ~>
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
|
105
|
-
segments:
|
106
|
-
- 0
|
107
|
-
- 6
|
108
|
-
version: "0.6"
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0.6'
|
109
66
|
type: :development
|
110
|
-
version_requirements: *id006
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rest-client
|
113
67
|
prerelease: false
|
114
|
-
|
68
|
+
version_requirements: *2157621180
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rest-client
|
71
|
+
requirement: &2157620720 !ruby/object:Gem::Requirement
|
115
72
|
none: false
|
116
|
-
requirements:
|
73
|
+
requirements:
|
117
74
|
- - ~>
|
118
|
-
- !ruby/object:Gem::Version
|
119
|
-
|
120
|
-
segments:
|
121
|
-
- 1
|
122
|
-
- 6
|
123
|
-
- 1
|
124
|
-
version: 1.6.1
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '1.6'
|
125
77
|
type: :runtime
|
126
|
-
version_requirements: *id007
|
127
|
-
- !ruby/object:Gem::Dependency
|
128
|
-
name: hashie
|
129
78
|
prerelease: false
|
130
|
-
|
79
|
+
version_requirements: *2157620720
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: hashie
|
82
|
+
requirement: &2157620260 !ruby/object:Gem::Requirement
|
131
83
|
none: false
|
132
|
-
requirements:
|
84
|
+
requirements:
|
133
85
|
- - ~>
|
134
|
-
- !ruby/object:Gem::Version
|
135
|
-
|
136
|
-
segments:
|
137
|
-
- 1
|
138
|
-
- 0
|
139
|
-
- 0
|
140
|
-
version: 1.0.0
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '1.0'
|
141
88
|
type: :runtime
|
142
|
-
version_requirements: *id008
|
143
|
-
- !ruby/object:Gem::Dependency
|
144
|
-
name: activesupport
|
145
89
|
prerelease: false
|
146
|
-
|
90
|
+
version_requirements: *2157620260
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: activesupport
|
93
|
+
requirement: &2157619800 !ruby/object:Gem::Requirement
|
147
94
|
none: false
|
148
|
-
requirements:
|
95
|
+
requirements:
|
149
96
|
- - ~>
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
|
152
|
-
segments:
|
153
|
-
- 3
|
154
|
-
- 0
|
155
|
-
- 0
|
156
|
-
version: 3.0.0
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '3'
|
157
99
|
type: :runtime
|
158
|
-
version_requirements: *id009
|
159
|
-
- !ruby/object:Gem::Dependency
|
160
|
-
name: i18n
|
161
100
|
prerelease: false
|
162
|
-
|
101
|
+
version_requirements: *2157619800
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: i18n
|
104
|
+
requirement: &2157619340 !ruby/object:Gem::Requirement
|
163
105
|
none: false
|
164
|
-
requirements:
|
106
|
+
requirements:
|
165
107
|
- - ~>
|
166
|
-
- !ruby/object:Gem::Version
|
167
|
-
|
168
|
-
segments:
|
169
|
-
- 0
|
170
|
-
- 5
|
171
|
-
version: "0.5"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.5'
|
172
110
|
type: :runtime
|
173
|
-
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *2157619340
|
174
113
|
description: Simple wrapper for the Goodreads API
|
175
|
-
email:
|
114
|
+
email:
|
176
115
|
- dan.sosedoff@gmail.com
|
177
116
|
executables: []
|
178
|
-
|
179
117
|
extensions: []
|
180
|
-
|
181
118
|
extra_rdoc_files: []
|
182
|
-
|
183
|
-
files:
|
119
|
+
files:
|
184
120
|
- .gitignore
|
185
121
|
- .rspec
|
122
|
+
- .travis.yml
|
186
123
|
- Gemfile
|
187
|
-
- README.
|
124
|
+
- README.md
|
188
125
|
- Rakefile
|
189
126
|
- goodreads.gemspec
|
190
127
|
- lib/goodreads.rb
|
191
128
|
- lib/goodreads/client.rb
|
192
|
-
- lib/goodreads/
|
129
|
+
- lib/goodreads/client/authors.rb
|
130
|
+
- lib/goodreads/client/books.rb
|
131
|
+
- lib/goodreads/client/reviews.rb
|
132
|
+
- lib/goodreads/client/users.rb
|
133
|
+
- lib/goodreads/errors.rb
|
134
|
+
- lib/goodreads/request.rb
|
193
135
|
- lib/goodreads/version.rb
|
194
136
|
- spec/authentication_spec.rb
|
195
137
|
- spec/client_spec.rb
|
@@ -199,44 +141,33 @@ files:
|
|
199
141
|
- spec/fixtures/review.xml
|
200
142
|
- spec/fixtures/search_books_by_name.xml
|
201
143
|
- spec/fixtures/user.xml
|
144
|
+
- spec/goodreads_spec.rb
|
202
145
|
- spec/spec_helper.rb
|
203
|
-
has_rdoc: true
|
204
146
|
homepage: http://github.com/sosedoff/goodreads
|
205
147
|
licenses: []
|
206
|
-
|
207
148
|
post_install_message:
|
208
149
|
rdoc_options: []
|
209
|
-
|
210
|
-
require_paths:
|
150
|
+
require_paths:
|
211
151
|
- lib
|
212
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
213
153
|
none: false
|
214
|
-
requirements:
|
215
|
-
- -
|
216
|
-
- !ruby/object:Gem::Version
|
217
|
-
|
218
|
-
|
219
|
-
- 0
|
220
|
-
version: "0"
|
221
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
222
159
|
none: false
|
223
|
-
requirements:
|
224
|
-
- -
|
225
|
-
- !ruby/object:Gem::Version
|
226
|
-
|
227
|
-
segments:
|
228
|
-
- 1
|
229
|
-
- 3
|
230
|
-
- 6
|
231
|
-
version: 1.3.6
|
160
|
+
requirements:
|
161
|
+
- - ! '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
232
164
|
requirements: []
|
233
|
-
|
234
165
|
rubyforge_project:
|
235
|
-
rubygems_version: 1.
|
166
|
+
rubygems_version: 1.8.10
|
236
167
|
signing_key:
|
237
168
|
specification_version: 3
|
238
169
|
summary: Goodreads API wrapper
|
239
|
-
test_files:
|
170
|
+
test_files:
|
240
171
|
- spec/authentication_spec.rb
|
241
172
|
- spec/client_spec.rb
|
242
173
|
- spec/fixtures/author.xml
|
@@ -245,4 +176,5 @@ test_files:
|
|
245
176
|
- spec/fixtures/review.xml
|
246
177
|
- spec/fixtures/search_books_by_name.xml
|
247
178
|
- spec/fixtures/user.xml
|
179
|
+
- spec/goodreads_spec.rb
|
248
180
|
- spec/spec_helper.rb
|
data/README.rdoc
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
= Goodreads -- simple client for Goodreads.com API
|
2
|
-
|
3
|
-
A simple API client to pull book information and reviews from Goodreads.com.
|
4
|
-
Wrapper uses XML feed and transforms it into the object model.
|
5
|
-
|
6
|
-
* Main page: http://github.com/sosedoff/goodreads
|
7
|
-
* Goodreads API: http://goodreads.com/api
|
8
|
-
|
9
|
-
== Getting Started
|
10
|
-
|
11
|
-
First, you need to provide your API token
|
12
|
-
|
13
|
-
Goodreads.configure('API_TOKEN')
|
14
|
-
|
15
|
-
client = Goodreads::Client.new
|
16
|
-
|
17
|
-
== Usage
|
18
|
-
|
19
|
-
=== Lookup books
|
20
|
-
|
21
|
-
Find a book by ISBN:
|
22
|
-
|
23
|
-
book = client.book_by_isbn('ISBN')
|
24
|
-
|
25
|
-
Find a book by Goodreads ID:
|
26
|
-
|
27
|
-
book = client.book('id')
|
28
|
-
|
29
|
-
Find a book by title:
|
30
|
-
|
31
|
-
book = client.book_by_title('Book title')
|
32
|
-
|
33
|
-
Search for books (by title, isbn, genre)
|
34
|
-
|
35
|
-
search = client.search_books('Your search query')
|
36
|
-
search.results.work.each do |book|
|
37
|
-
book.id # => book ID
|
38
|
-
book.title # => book title
|
39
|
-
end
|
40
|
-
|
41
|
-
=== Pull recent reviews
|
42
|
-
|
43
|
-
client.recent_reviews.each do |r|
|
44
|
-
r.id # => review id
|
45
|
-
r.book.title # => review book title
|
46
|
-
r.body # => review message
|
47
|
-
r.user.name # => review user name
|
48
|
-
end
|
49
|
-
|
50
|
-
=== Get review Details
|
51
|
-
|
52
|
-
review = client.review('id')
|
53
|
-
review.id # => review ID
|
54
|
-
review.user # => review user information
|
55
|
-
review.book # => review book information
|
56
|
-
review.reting # => review rating
|
57
|
-
|
58
|
-
== Limitation
|
59
|
-
|
60
|
-
* According to Goodreads API there is a limit of 60 RPM (requests per minute)
|
61
|
-
* Provided data cannot be stored for any usage
|
62
|
-
* Please check original documentation for additional object fields and properties
|
63
|
-
|
64
|
-
== Authors
|
65
|
-
|
66
|
-
* Dan Sosedoff [http://github.com/sosedoff]
|
data/lib/goodreads/goodreads.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
module Goodreads
|
2
|
-
API_URL = 'http://www.goodreads.com'
|
3
|
-
API_FORMAT = 'xml'
|
4
|
-
|
5
|
-
class Error < StandardError; end
|
6
|
-
class Unauthorized < Error ; end
|
7
|
-
class NotFound < Error ; end
|
8
|
-
|
9
|
-
def self.configure(api_key)
|
10
|
-
Goodreads::Client.configure({:api_key => api_key})
|
11
|
-
end
|
12
|
-
end
|