goodreads_17up 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/README.md +211 -0
- data/Rakefile +10 -0
- data/examples/oauth.md +57 -0
- data/goodreads.gemspec +28 -0
- data/lib/goodreads/client/authorized.rb +9 -0
- data/lib/goodreads/client/authors.rb +19 -0
- data/lib/goodreads/client/books.rb +35 -0
- data/lib/goodreads/client/friends.rb +12 -0
- data/lib/goodreads/client/groups.rb +15 -0
- data/lib/goodreads/client/reviews.rb +24 -0
- data/lib/goodreads/client/shelves.rb +26 -0
- data/lib/goodreads/client/users.rb +10 -0
- data/lib/goodreads/client.rb +41 -0
- data/lib/goodreads/errors.rb +6 -0
- data/lib/goodreads/request.rb +67 -0
- data/lib/goodreads/version.rb +5 -0
- data/lib/goodreads.rb +41 -0
- data/spec/authentication_spec.rb +24 -0
- data/spec/client_spec.rb +296 -0
- data/spec/goodreads_spec.rb +54 -0
- data/spec/spec_helper.rb +38 -0
- metadata +212 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2ed19533305d447d2c1378fc490cf2bd7daf0a80
|
4
|
+
data.tar.gz: 01b6e36b14bb6720d122a0dfdf01dc42a254ecff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e6f8b2e4291aea73fc0fb9d8ec3ec86d30ae13ba8d702a0d64f5095f006bab2aec161cb21baf46279aee254d54f9ef4231f8ea2e34cfa231d92be28f1805c53
|
7
|
+
data.tar.gz: 5168872ba353b8ecff0920de10392eee28e7bf38badd5c89691fb6a246eb19a34c4efc30b633b4856e4ee2c358de6bd2f81670dbbf7ab83011f418b3d8449b9f
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.swp
|
4
|
+
*.tmproj
|
5
|
+
*~
|
6
|
+
.DS_Store
|
7
|
+
.\#*
|
8
|
+
.bundle
|
9
|
+
.config
|
10
|
+
.yardoc
|
11
|
+
Gemfile.lock
|
12
|
+
InstalledFiles
|
13
|
+
\#*
|
14
|
+
_yardoc
|
15
|
+
coverage
|
16
|
+
doc/
|
17
|
+
lib/bundler/man
|
18
|
+
pkg
|
19
|
+
rdoc
|
20
|
+
spec/reports
|
21
|
+
test/tmp
|
22
|
+
test/version_tmp
|
23
|
+
tmp
|
24
|
+
tmtags
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Goodreads [![Build Status](https://secure.travis-ci.org/sosedoff/goodreads.png)](http://travis-ci.org/sosedoff/goodreads)
|
2
|
+
|
3
|
+
Ruby wrapper to communicate with Goodreads API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install gem with rubygems:
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install goodreads
|
11
|
+
```
|
12
|
+
|
13
|
+
Or manually:
|
14
|
+
|
15
|
+
```
|
16
|
+
rake install
|
17
|
+
```
|
18
|
+
|
19
|
+
## Getting Started
|
20
|
+
|
21
|
+
Before using Goodreads API you must create a new application. Visit [signup form](http://www.goodreads.com/api/keys) for details.
|
22
|
+
|
23
|
+
Setup client:
|
24
|
+
|
25
|
+
``` ruby
|
26
|
+
client = Goodreads::Client.new(:api_key => 'KEY', :api_secret => 'SECRET')
|
27
|
+
client = Goodreads.new(:api_key => 'KEY') # short version
|
28
|
+
```
|
29
|
+
|
30
|
+
### Global configuration
|
31
|
+
|
32
|
+
You can define client credentials on global level. Just create an initializer file (if using rails) under
|
33
|
+
`config/initializers`:
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
Goodreads.configure(
|
37
|
+
:api_key => 'KEY',
|
38
|
+
:api_secret => 'SECRET'
|
39
|
+
)
|
40
|
+
```
|
41
|
+
|
42
|
+
Get global configuration:
|
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
|
+
You can lookup a book by ISBN, ID or Title:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
client.book('id')
|
62
|
+
client.book_by_isbn('ISBN')
|
63
|
+
client.book_by_title('Book title')
|
64
|
+
```
|
65
|
+
|
66
|
+
Search for books (by title, isbn, genre):
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
search = client.search_books('The Lord Of The Rings')
|
70
|
+
|
71
|
+
search.results.work.each do |book|
|
72
|
+
book.id # => book id
|
73
|
+
book.title # => book title
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
### Authors
|
78
|
+
|
79
|
+
Look up an author by their Goodreads Author ID:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
author = client.author('id')
|
83
|
+
|
84
|
+
author.id # => author id
|
85
|
+
author.name # => author's name
|
86
|
+
author.link # => link to author's Goodreads page
|
87
|
+
author.fans_count # => number of fans author has on Goodreads
|
88
|
+
author.image_url # => link to image of the author
|
89
|
+
author.small_image_url # => link to smaller of the author
|
90
|
+
author.about # => description of the author
|
91
|
+
author.influences # => list of links to author's influences
|
92
|
+
author.works_count # => number of works by the author in Goodreads
|
93
|
+
author.gender # => author's gender
|
94
|
+
author.hometown # => author's hometown
|
95
|
+
author.born_at # => author's birthdate
|
96
|
+
author.died_at # => date of author's death
|
97
|
+
```
|
98
|
+
|
99
|
+
Look up an author by name:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
author = client.author_by_name('Author Name')
|
103
|
+
|
104
|
+
author.id # => author id
|
105
|
+
author.name # => author name
|
106
|
+
author.link # => link to author's Goodreads page
|
107
|
+
```
|
108
|
+
|
109
|
+
### Reviews
|
110
|
+
|
111
|
+
Pull recent reviews:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
client.recent_reviews.each do |r|
|
115
|
+
r.id # => review id
|
116
|
+
r.book.title # => review book title
|
117
|
+
r.body # => review message
|
118
|
+
r.user.name # => review user name
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
Get review details:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
review = client.review('id')
|
126
|
+
|
127
|
+
review.id # => review id
|
128
|
+
review.user # => user information
|
129
|
+
review.book # => uook information
|
130
|
+
review.rating # => user rating
|
131
|
+
```
|
132
|
+
|
133
|
+
### Shelves
|
134
|
+
|
135
|
+
Get the books on a user's shelf:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
shelf = client.shelf(user_id, shelf_name)
|
139
|
+
|
140
|
+
shelf.books # array of books on this shelf
|
141
|
+
shelf.start # start index of this page of paginated results
|
142
|
+
shelf.end # end index of this page of paginated results
|
143
|
+
shelf.total # total number of books on this shelf
|
144
|
+
```
|
145
|
+
|
146
|
+
### Groups
|
147
|
+
|
148
|
+
Get group details:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
group = client.group('id')
|
152
|
+
|
153
|
+
group.id # => group id
|
154
|
+
group.title # => group title
|
155
|
+
group.access # => group's access settings
|
156
|
+
# => (e.g., public or private)
|
157
|
+
group.group_users_count # => number of users in the group
|
158
|
+
```
|
159
|
+
|
160
|
+
List the groups a given user is a member of:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
group_list = client.group_list('user_id', 'sort')
|
164
|
+
|
165
|
+
group_list.total # => total number of groups
|
166
|
+
group_list.group.count # => number of groups returned in the request
|
167
|
+
|
168
|
+
# Loop through the list to get details for each of the groups.
|
169
|
+
|
170
|
+
group_list.group.each do |g|
|
171
|
+
g.id # => group id
|
172
|
+
g.access # => access settings (private, public)
|
173
|
+
g.users_count # => number of members
|
174
|
+
g.title # => title
|
175
|
+
g.image_url # => url of the group's image
|
176
|
+
g.last_activity_at # => date and time of the group's last activity
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
The `sort` parameter is optional, and defaults to `my_activity`. For other sorting options, [see here](http://www.goodreads.com/api#group.list).
|
181
|
+
|
182
|
+
### OAuth
|
183
|
+
|
184
|
+
For API calls requiring permission, such as write operations or browsing friends, see our [OAuth tutorial](examples/oauth.md).
|
185
|
+
|
186
|
+
## Testing
|
187
|
+
|
188
|
+
To run the test suite:
|
189
|
+
|
190
|
+
```
|
191
|
+
rake test
|
192
|
+
```
|
193
|
+
|
194
|
+
## Contributions
|
195
|
+
|
196
|
+
You're welcome to submit patches and new features.
|
197
|
+
|
198
|
+
- Create a new branch for your feature of bugfix
|
199
|
+
- Add tests so it does not break any existing code
|
200
|
+
- Open a new pull request
|
201
|
+
- Check official [API documentation](http://www.goodreads.com/api)
|
202
|
+
|
203
|
+
## License
|
204
|
+
|
205
|
+
Copyright © 2011-2013 Dan Sosedoff.
|
206
|
+
|
207
|
+
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:
|
208
|
+
|
209
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
210
|
+
|
211
|
+
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
ADDED
data/examples/oauth.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Authorizing Goodreads via OAuth
|
2
|
+
|
3
|
+
For services requiring permission, such as write operations or browsing friends, the client must be authorized through OAuth.
|
4
|
+
|
5
|
+
## Request Tokens vs. Access Tokens
|
6
|
+
|
7
|
+
First, get an OAuth *request* token:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
request_token = OAuth::Consumer.new(
|
11
|
+
Goodreads.configuration[:api_key],
|
12
|
+
Goodreads.configuration[:api_secret],
|
13
|
+
:site => 'http://www.goodreads.com'
|
14
|
+
).get_request_token
|
15
|
+
```
|
16
|
+
|
17
|
+
Next, authorize by opening the authorization URL in a browser:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
request_token.authorize_url
|
21
|
+
```
|
22
|
+
|
23
|
+
Then request an OAuth *access* token:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
access_token = request_token.get_access_token
|
27
|
+
```
|
28
|
+
|
29
|
+
Finally, initialize a Goodreads client with it:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
goodreads_client = Goodreads.new :oauth_token => access_token
|
33
|
+
```
|
34
|
+
|
35
|
+
For more info, see the [Goodreads documentation](http://www.goodreads.com/api/oauth_example).
|
36
|
+
|
37
|
+
## User ID
|
38
|
+
|
39
|
+
Get the ID of the user who authorized via OAuth:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
goodreads_client.user_id
|
43
|
+
```
|
44
|
+
|
45
|
+
## Friends
|
46
|
+
|
47
|
+
Get the friend details for a user:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
friends_hash = goodreads_client.friends [user_id]
|
51
|
+
```
|
52
|
+
|
53
|
+
Get a list of their names:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
friends_hash.user.map{ |u| u.name }
|
57
|
+
```
|
data/goodreads.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../lib/goodreads/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "goodreads_17up"
|
5
|
+
s.version = Goodreads::VERSION.dup
|
6
|
+
s.summary = "Goodreads API wrapper"
|
7
|
+
s.description = "Simple wrapper for the Goodreads API"
|
8
|
+
s.homepage = "http://github.com/sosedoff/goodreads"
|
9
|
+
s.authors = ["Dan Sosedoff"]
|
10
|
+
s.email = ["dan.sosedoff@gmail.com"]
|
11
|
+
|
12
|
+
s.add_development_dependency 'webmock', '~> 1.11'
|
13
|
+
s.add_development_dependency 'rake', '~> 0.9'
|
14
|
+
s.add_development_dependency 'rspec', '~> 2.12'
|
15
|
+
s.add_development_dependency 'simplecov', '~> 0.7'
|
16
|
+
s.add_development_dependency 'yard', '~> 0.6'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'rest-client', '~> 1.6'
|
19
|
+
s.add_runtime_dependency 'hashie', '~> 1.2'
|
20
|
+
s.add_runtime_dependency 'activesupport', '~> 3'
|
21
|
+
s.add_runtime_dependency 'i18n', '~> 0.5'
|
22
|
+
s.add_runtime_dependency 'oauth', '~> 0.4'
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Goodreads
|
2
|
+
module Authors
|
3
|
+
# Get author details
|
4
|
+
#
|
5
|
+
def author(id, params={})
|
6
|
+
params[:id] = id
|
7
|
+
data = request('/author/show', params)
|
8
|
+
Hashie::Mash.new(data['author'])
|
9
|
+
end
|
10
|
+
|
11
|
+
# Search for an author by name
|
12
|
+
#
|
13
|
+
def author_by_name(name, params={})
|
14
|
+
params[:id] = name
|
15
|
+
data = request('/api/author_url', params)
|
16
|
+
Hashie::Mash.new(data['author'])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
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,15 @@
|
|
1
|
+
module Goodreads
|
2
|
+
module Groups
|
3
|
+
# Get group details
|
4
|
+
def group(group_id)
|
5
|
+
data = request('/group/show', :id => group_id)
|
6
|
+
Hashie::Mash.new(data['group'])
|
7
|
+
end
|
8
|
+
|
9
|
+
# Get list of groups a given user is a member of
|
10
|
+
def group_list(user_id, sort='my_activity')
|
11
|
+
data = request('/group/list', :id => user_id, :sort => sort)
|
12
|
+
Hashie::Mash.new(data['groups']['list'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
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,26 @@
|
|
1
|
+
module Goodreads
|
2
|
+
module Shelves
|
3
|
+
|
4
|
+
# Get books from a user's shelf
|
5
|
+
def shelf(user_id, shelf_name, options={})
|
6
|
+
options = options.merge(:shelf => shelf_name, :v =>2)
|
7
|
+
data = request("/review/list/#{user_id}.xml", options)
|
8
|
+
reviews = data['reviews']['review']
|
9
|
+
|
10
|
+
books = []
|
11
|
+
unless reviews.nil?
|
12
|
+
# one-book results come back as a single hash
|
13
|
+
reviews = [reviews] if !reviews.instance_of?(Array)
|
14
|
+
books = reviews.map {|e| Hashie::Mash.new(e)}
|
15
|
+
end
|
16
|
+
|
17
|
+
Hashie::Mash.new({
|
18
|
+
:start => data['reviews']['start'].to_i,
|
19
|
+
:end => data['reviews']['end'].to_i,
|
20
|
+
:total => data['reviews']['total'].to_i,
|
21
|
+
:books => books
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
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
|
+
require 'goodreads/client/shelves'
|
7
|
+
require 'goodreads/client/authorized'
|
8
|
+
require 'goodreads/client/groups'
|
9
|
+
require 'goodreads/client/friends'
|
10
|
+
|
11
|
+
module Goodreads
|
12
|
+
class Client
|
13
|
+
include Goodreads::Request
|
14
|
+
include Goodreads::Books
|
15
|
+
include Goodreads::Reviews
|
16
|
+
include Goodreads::Authors
|
17
|
+
include Goodreads::Users
|
18
|
+
include Goodreads::Shelves
|
19
|
+
include Goodreads::Authorized
|
20
|
+
include Goodreads::Groups
|
21
|
+
include Goodreads::Friends
|
22
|
+
|
23
|
+
attr_reader :api_key, :api_secret, :oauth_token
|
24
|
+
|
25
|
+
# Initialize a Goodreads::Client instance
|
26
|
+
#
|
27
|
+
# options[:api_key] - Account API key
|
28
|
+
# options[:api_secret] - Account API secret
|
29
|
+
# options[:oauth_token] - OAuth access token (optional, required for some calls)
|
30
|
+
#
|
31
|
+
def initialize(options={})
|
32
|
+
unless options.kind_of?(Hash)
|
33
|
+
raise ArgumentError, "Options hash required."
|
34
|
+
end
|
35
|
+
|
36
|
+
@api_key = options[:api_key] || Goodreads.configuration[:api_key]
|
37
|
+
@api_secret = options[:api_secret] || Goodreads.configuration[:api_secret]
|
38
|
+
@oauth_token = options[:oauth_token]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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
|
+
parse(resp)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Perform an OAuth API request. Goodreads must have been initialized with a valid OAuth access token.
|
42
|
+
#
|
43
|
+
# path - Request path
|
44
|
+
# params - Parameters hash
|
45
|
+
#
|
46
|
+
def oauth_request(path, params=nil)
|
47
|
+
raise 'OAuth access token required!' unless @oauth_token
|
48
|
+
path = "#{path}?#{params.map{|k,v|"#{k}=#{v}"}.join('&')}" if params
|
49
|
+
resp = @oauth_token.get(path, {'Accept'=>'application/xml'})
|
50
|
+
|
51
|
+
case resp
|
52
|
+
when Net::HTTPUnauthorized
|
53
|
+
raise Goodreads::Unauthorized
|
54
|
+
when Net::HTTPNotFound
|
55
|
+
raise Goodreads::NotFound
|
56
|
+
end
|
57
|
+
|
58
|
+
parse(resp)
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse(resp)
|
62
|
+
hash = Hash.from_xml(resp.body)['GoodreadsResponse']
|
63
|
+
hash.delete('Request')
|
64
|
+
hash
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/goodreads.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'goodreads/version'
|
2
|
+
require 'goodreads/errors'
|
3
|
+
require 'goodreads/request'
|
4
|
+
require 'goodreads/client'
|
5
|
+
|
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
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Client' do
|
4
|
+
before :each do
|
5
|
+
Goodreads.reset_configuration
|
6
|
+
end
|
7
|
+
|
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.'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises Goodreads::Unauthorized if API key is not valid' do
|
16
|
+
client = Goodreads::Client.new(:api_key => 'INVALID_KEY')
|
17
|
+
|
18
|
+
stub_request(:get, "http://www.goodreads.com/book/isbn?format=xml&isbn=054748250711&key=INVALID_KEY").
|
19
|
+
to_return(:status => 401)
|
20
|
+
|
21
|
+
proc { client.book_by_isbn('054748250711') }.
|
22
|
+
should raise_error Goodreads::Unauthorized
|
23
|
+
end
|
24
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'oauth'
|
3
|
+
|
4
|
+
describe 'Client' do
|
5
|
+
let(:client) { Goodreads::Client.new(:api_key => 'SECRET_KEY') }
|
6
|
+
before(:each) { Goodreads.reset_configuration }
|
7
|
+
|
8
|
+
describe '#new' do
|
9
|
+
it 'requires an argument' do
|
10
|
+
expect { Goodreads::Client.new(nil) }.
|
11
|
+
to raise_error ArgumentError, "Options hash required."
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'requires a hash argument' do
|
15
|
+
expect { Goodreads::Client.new('foo') }.
|
16
|
+
to raise_error ArgumentError, "Options hash required."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#book_by_isbn' do
|
21
|
+
before { stub_with_key_get('/book/isbn', {:isbn => '0307463745'}, 'book.xml') }
|
22
|
+
|
23
|
+
it 'returns a book by isbn' do
|
24
|
+
book = client.book_by_isbn('0307463745')
|
25
|
+
|
26
|
+
book.should respond_to :id
|
27
|
+
book.should respond_to :title
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when book does not exist' do
|
31
|
+
before do
|
32
|
+
stub_request(:get, "http://www.goodreads.com/book/isbn?format=xml&isbn=123456789&key=SECRET_KEY").
|
33
|
+
to_return(:status => 404, :body => "", :headers => {})
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises Goodreads::NotFound' do
|
37
|
+
expect { client.book_by_isbn('123456789') }.to raise_error Goodreads::NotFound
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#search_books' do
|
43
|
+
before { stub_with_key_get('/search/index', {:q => 'Rework'}, 'search_books_by_name.xml') }
|
44
|
+
|
45
|
+
it 'returns book search results' do
|
46
|
+
result = client.search_books('Rework')
|
47
|
+
|
48
|
+
result.should be_a Hashie::Mash
|
49
|
+
result.should respond_to :query
|
50
|
+
result.should respond_to :total_results
|
51
|
+
result.should respond_to :results
|
52
|
+
result.results.should respond_to :work
|
53
|
+
result.query.should eq 'Rework'
|
54
|
+
result.results.work.size.should eq 3
|
55
|
+
result.results.work.first.id.should eq 6928276
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#book' do
|
60
|
+
before { stub_with_key_get('/book/show', {:id => '6732019'}, 'book.xml') }
|
61
|
+
|
62
|
+
it 'returns a book by goodreads id' do
|
63
|
+
expect { client.book('6732019') }.not_to raise_error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#book_by_title' do
|
68
|
+
before { stub_with_key_get('/book/title', {:title => 'Rework'}, 'book.xml') }
|
69
|
+
|
70
|
+
it 'returns a book by title' do
|
71
|
+
expect { client.book_by_title('Rework') }.not_to raise_error
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#recent_reviews' do
|
76
|
+
before { stub_with_key_get('/review/recent_reviews', {}, 'recent_reviews.xml') }
|
77
|
+
|
78
|
+
it 'returns recent reviews' do
|
79
|
+
reviews = client.recent_reviews
|
80
|
+
|
81
|
+
reviews.should be_an Array
|
82
|
+
reviews.should_not be_empty
|
83
|
+
reviews.first.should respond_to :id
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with :skip_cropped => true' do
|
87
|
+
before { stub_with_key_get('/review/recent_reviews', {}, 'recent_reviews.xml') }
|
88
|
+
|
89
|
+
it 'returns only full reviews' do
|
90
|
+
reviews = client.recent_reviews(:skip_cropped => true)
|
91
|
+
reviews.should be_an Array
|
92
|
+
reviews.should_not be_empty
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#review' do
|
98
|
+
before { stub_with_key_get('/review/show', {:id => '166204831'}, 'review.xml') }
|
99
|
+
|
100
|
+
it 'returns review details' do
|
101
|
+
review = client.review('166204831')
|
102
|
+
|
103
|
+
review.should be_a Hashie::Mash
|
104
|
+
review.id.should eq '166204831'
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when review does not exist' do
|
108
|
+
before do
|
109
|
+
stub_request(:get, "http://www.goodreads.com/review/show?format=xml&id=12345&key=SECRET_KEY").
|
110
|
+
to_return(:status => 404, :body => "", :headers => {})
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'raises Goodreads::NotFound' do
|
114
|
+
expect { client.review('12345') }.to raise_error Goodreads::NotFound
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#author' do
|
120
|
+
before { stub_with_key_get('/author/show', {:id => '18541'}, 'author.xml') }
|
121
|
+
|
122
|
+
it 'returns author details' do
|
123
|
+
author = client.author('18541')
|
124
|
+
|
125
|
+
author.should be_a Hashie::Mash
|
126
|
+
author.id.should eq '18541'
|
127
|
+
author.name.should eq "Tim O'Reilly"
|
128
|
+
author.link.should eq 'http://www.goodreads.com/author/show/18541.Tim_O_Reilly'
|
129
|
+
author.fans_count.should eq 109
|
130
|
+
author.image_url.should eq 'http://photo.goodreads.com/authors/1199698411p5/18541.jpg'
|
131
|
+
author.small_image_url.should eq 'http://photo.goodreads.com/authors/1199698411p2/18541.jpg'
|
132
|
+
author.about.should eq ''
|
133
|
+
author.influences.should eq ''
|
134
|
+
author.works_count.should eq '34'
|
135
|
+
author.gender.should eq 'male'
|
136
|
+
author.hometown.should eq 'Cork'
|
137
|
+
author.born_at.should eq '1954/06/06'
|
138
|
+
author.died_at.should be_nil
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'when author does not exist' do
|
142
|
+
before do
|
143
|
+
stub_request(:get, "http://www.goodreads.com/author/show?format=xml&id=12345&key=SECRET_KEY").
|
144
|
+
to_return(:status => 404, :body => "", :headers => {})
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'raises Goodreads::NotFound' do
|
148
|
+
expect { client.author('12345') }.to raise_error Goodreads::NotFound
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#author_by_name' do
|
154
|
+
before do
|
155
|
+
stub_with_key_get('/api/author_url', {:id => 'Orson Scott Card'}, 'author_by_name.xml')
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns author details' do
|
159
|
+
author = client.author_by_name('Orson Scott Card')
|
160
|
+
|
161
|
+
author.should be_a Hashie::Mash
|
162
|
+
author.id.should eq '589'
|
163
|
+
author.name.should eq 'Orson Scott Card'
|
164
|
+
author.link.should eq 'http://www.goodreads.com/author/show/589.Orson_Scott_Card?utm_medium=api&utm_source=author_link'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#user' do
|
169
|
+
before { stub_with_key_get('/user/show', {:id => '878044'}, 'user.xml') }
|
170
|
+
|
171
|
+
it 'returns user details' do
|
172
|
+
user = client.user('878044')
|
173
|
+
|
174
|
+
user.should be_a Hashie::Mash
|
175
|
+
user.id.should eq '878044'
|
176
|
+
user.name.should eq 'Jan'
|
177
|
+
user.user_name.should eq 'janmt'
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'when user does not exist' do
|
181
|
+
before do
|
182
|
+
stub_request(:get, "http://www.goodreads.com/user/show?format=xml&id=12345&key=SECRET_KEY").
|
183
|
+
to_return(:status => 404, :body => "", :headers => {})
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'raises Goodreads::NotFound' do
|
187
|
+
expect { client.user('12345') }.to raise_error Goodreads::NotFound
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#friends' do
|
193
|
+
before { client.stub(:oauth_request).and_return(Hash.from_xml(fixture('friends.xml'))['GoodreadsResponse']) }
|
194
|
+
|
195
|
+
it 'returns friend details' do
|
196
|
+
friends = client.friends('878044')
|
197
|
+
|
198
|
+
friends.should be_an_instance_of Hashie::Mash
|
199
|
+
friends.should respond_to :user
|
200
|
+
friends.user.size.should eq friends.end.to_i
|
201
|
+
friends.user.first.should respond_to :name
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '#shelf' do
|
206
|
+
it "returns list of books for a user's specified shelf" do
|
207
|
+
stub_with_key_get('/review/list/1.xml', {:shelf => 'to-read', :v => '2'}, 'to-read.xml')
|
208
|
+
|
209
|
+
shelf = client.shelf('1', 'to-read')
|
210
|
+
|
211
|
+
shelf.should respond_to :start
|
212
|
+
shelf.should respond_to :end
|
213
|
+
shelf.should respond_to :total
|
214
|
+
shelf.should respond_to :books
|
215
|
+
|
216
|
+
shelf.start.should eq 1
|
217
|
+
shelf.end.should eq 20
|
218
|
+
shelf.total.should eq 40
|
219
|
+
shelf.books.length.should eq 20
|
220
|
+
shelf.books.first.id.should eq '45590939'
|
221
|
+
shelf.books.first.book.title.strip.should eq 'The Demon-Haunted World: Science as a Candle in the Dark'
|
222
|
+
end
|
223
|
+
|
224
|
+
it "paginates book lists from a user's shelf" do
|
225
|
+
stub_with_key_get('/review/list/1.xml', {:shelf => 'to-read', :v => '2', :page => '2'}, 'to-read-p2.xml')
|
226
|
+
|
227
|
+
shelf = client.shelf('1', 'to-read', :page => 2)
|
228
|
+
|
229
|
+
shelf.start.should eq 21
|
230
|
+
shelf.end.should eq 40
|
231
|
+
shelf.total.should eq 40
|
232
|
+
shelf.books.length.should eq 20
|
233
|
+
shelf.books.first.id.should eq '107804211'
|
234
|
+
shelf.books.first.book.title.should match /Your Money or Your Life/
|
235
|
+
end
|
236
|
+
|
237
|
+
it "returns an empty array when shelf is empty" do
|
238
|
+
stub_with_key_get('/review/list/1.xml', {:shelf => 'to-read', :v => '2'}, 'empty.xml')
|
239
|
+
|
240
|
+
shelf = client.shelf('1', 'to-read')
|
241
|
+
|
242
|
+
shelf.start.should eq 0
|
243
|
+
shelf.end.should eq 0
|
244
|
+
shelf.total.should eq 0
|
245
|
+
shelf.books.length.should eq 0
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe '#user_id' do
|
250
|
+
let(:consumer) { OAuth::Consumer.new('API_KEY', 'SECRET_KEY', :site => 'http://www.goodreads.com') }
|
251
|
+
let(:token) { OAuth::AccessToken.new(consumer, 'ACCESS_TOKEN', 'ACCESS_SECRET') }
|
252
|
+
|
253
|
+
before do
|
254
|
+
stub_request(:get, "http://www.goodreads.com/api/auth_user").
|
255
|
+
to_return(:status => 200, :body => fixture('oauth_response.xml'), :headers => {})
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'returns id of the user with oauth authentication' do
|
259
|
+
client = Goodreads::Client.new(:api_key => 'SECRET_KEY', :oauth_token => token)
|
260
|
+
client.user_id.should eq '2003928'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#group' do
|
265
|
+
before { stub_with_key_get('/group/show', {:id => '1'}, 'group.xml') }
|
266
|
+
|
267
|
+
it "returns group details" do
|
268
|
+
group = client.group('1')
|
269
|
+
|
270
|
+
group.should be_a Hashie::Mash
|
271
|
+
group.id.should eq '1'
|
272
|
+
group.title.should eq 'Goodreads Feedback'
|
273
|
+
group.access.should eq 'public'
|
274
|
+
group.location.should eq ''
|
275
|
+
group.category.should eq 'Business'
|
276
|
+
group.subcategory.should eq 'Companies'
|
277
|
+
group.group_users_count.should eq '10335'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe '#group_list' do
|
282
|
+
before { stub_with_key_get('/group/list', {:id => '1', :sort => 'my_activity'}, 'group_list.xml') }
|
283
|
+
|
284
|
+
it "returns groups a given user is a member of" do
|
285
|
+
group_list = client.group_list('1')
|
286
|
+
|
287
|
+
group_list.should be_a Hashie::Mash
|
288
|
+
group_list.total.should eq '107'
|
289
|
+
group_list.group.count.should eq 50
|
290
|
+
group_list.group[0].id.should eq '1'
|
291
|
+
group_list.group[0].title.should eq 'Goodreads Feedback'
|
292
|
+
group_list.group[1].id.should eq '220'
|
293
|
+
group_list.group[2].users_count.should eq '530'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Goodreads' do
|
4
|
+
describe '.new' do
|
5
|
+
it 'returns a new client instance' do
|
6
|
+
Goodreads.new.should be_a Goodreads::Client
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.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.configure(nil) }.
|
22
|
+
should raise_error(ArgumentError, "Options hash required.")
|
23
|
+
|
24
|
+
proc { Goodreads.configure('foo') }.
|
25
|
+
should raise_error ArgumentError, "Options hash required."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.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
|
+
describe '.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
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
$:.unshift File.expand_path("../..", __FILE__)
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start do
|
5
|
+
add_group 'Goodreads', 'lib/goodreads'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'goodreads'
|
9
|
+
require 'webmock'
|
10
|
+
require 'webmock/rspec'
|
11
|
+
|
12
|
+
def stub_get(path, params, fixture_name)
|
13
|
+
params[:format] = 'xml'
|
14
|
+
stub_request(:get, api_url(path)).
|
15
|
+
with(:query => params).
|
16
|
+
to_return(
|
17
|
+
:status => 200,
|
18
|
+
:body => fixture(fixture_name)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub_with_key_get(path, params, fixture_name)
|
23
|
+
params[:key] = 'SECRET_KEY'
|
24
|
+
stub_get(path, params, fixture_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fixture_path(file=nil)
|
28
|
+
path = File.expand_path("../fixtures", __FILE__)
|
29
|
+
file.nil? ? path : File.join(path, file)
|
30
|
+
end
|
31
|
+
|
32
|
+
def fixture(file)
|
33
|
+
File.read(fixture_path(file))
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_url(path)
|
37
|
+
"#{Goodreads::Request::API_URL}#{path}"
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: goodreads_17up
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Sosedoff
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webmock
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.12'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rest-client
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.6'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: hashie
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: activesupport
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: i18n
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.5'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.5'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: oauth
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.4'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.4'
|
153
|
+
description: Simple wrapper for the Goodreads API
|
154
|
+
email:
|
155
|
+
- dan.sosedoff@gmail.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- .gitignore
|
161
|
+
- .rspec
|
162
|
+
- .travis.yml
|
163
|
+
- Gemfile
|
164
|
+
- README.md
|
165
|
+
- Rakefile
|
166
|
+
- examples/oauth.md
|
167
|
+
- goodreads.gemspec
|
168
|
+
- lib/goodreads.rb
|
169
|
+
- lib/goodreads/client.rb
|
170
|
+
- lib/goodreads/client/authorized.rb
|
171
|
+
- lib/goodreads/client/authors.rb
|
172
|
+
- lib/goodreads/client/books.rb
|
173
|
+
- lib/goodreads/client/friends.rb
|
174
|
+
- lib/goodreads/client/groups.rb
|
175
|
+
- lib/goodreads/client/reviews.rb
|
176
|
+
- lib/goodreads/client/shelves.rb
|
177
|
+
- lib/goodreads/client/users.rb
|
178
|
+
- lib/goodreads/errors.rb
|
179
|
+
- lib/goodreads/request.rb
|
180
|
+
- lib/goodreads/version.rb
|
181
|
+
- spec/authentication_spec.rb
|
182
|
+
- spec/client_spec.rb
|
183
|
+
- spec/goodreads_spec.rb
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
homepage: http://github.com/sosedoff/goodreads
|
186
|
+
licenses: []
|
187
|
+
metadata: {}
|
188
|
+
post_install_message:
|
189
|
+
rdoc_options: []
|
190
|
+
require_paths:
|
191
|
+
- lib
|
192
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - '>='
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - '>='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
requirements: []
|
203
|
+
rubyforge_project:
|
204
|
+
rubygems_version: 2.0.0
|
205
|
+
signing_key:
|
206
|
+
specification_version: 4
|
207
|
+
summary: Goodreads API wrapper
|
208
|
+
test_files:
|
209
|
+
- spec/authentication_spec.rb
|
210
|
+
- spec/client_spec.rb
|
211
|
+
- spec/goodreads_spec.rb
|
212
|
+
- spec/spec_helper.rb
|