goodreads 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7f5dc59e124930f2aa9295a56d4a0058d46b7aa2
4
- data.tar.gz: 7a7053c2a4916fe3ae4bd29a978072597351cb4a
2
+ SHA256:
3
+ metadata.gz: 328c7dcd4de9aaf162c5dba7982e50451abfe96ca12d078367a9dd0293b5d6da
4
+ data.tar.gz: 413ab442ee0b50901b3e519e1d3cbd3bda0f18d04e28fb98ae65676b73399696
5
5
  SHA512:
6
- metadata.gz: 16a77a800dba967e1941b2d9f546d2fbc11e4230dc4e25602c35d1513a17b2787193b5aeb8132bfbdf29cda2415db26e1db829ea7900e1edb792c8ddc5f1a2a6
7
- data.tar.gz: b11fe6e4d95124964b4f1ba328f2c098a3699a8015d311932e258a535910c35503556d5bfe8c779443dfdca77b7d3402c532bcf0c5fb90da27822644dd4eb5bc
6
+ metadata.gz: 74594d009980dedd882344a3674da6bc5393ad399861b1cd1a0a1a59e112586c15975be0d2f691debf287074795e0e52e00035aac44575bbd1b710842e69c794
7
+ data.tar.gz: e355ba7c55d71f9fb1b3ac62b549eba718468f1cab8e8b8a2c922401da594c63927c9b060f305ad2c16439cbb1ba24684a866dcd486133f2d0d13969d94bd960
data/.travis.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  sudo: false
2
2
 
3
3
  rvm:
4
- - 2.1
5
- - 2.2
6
4
  - 2.3
7
5
  - 2.4
6
+ - 2.5
7
+ - 2.6
8
8
 
9
9
  script:
10
10
  - bundle exec rake test
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2011-2015 Dan Sosedoff <dan.sosedoff@gmail.com>
3
+ Copyright (c) 2011-2020 Dan Sosedoff <dan.sosedoff@gmail.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
@@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
17
  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
18
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
19
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Ruby wrapper to communicate with Goodreads API.
4
4
 
5
+ **NOTE: The Goodreads API [is being discontinued](https://www.goodreads.com/api).**
6
+
5
7
  ## Requirements
6
8
 
7
9
  - Ruby 1.9.3+
@@ -110,6 +112,17 @@ author.name # => author name
110
112
  author.link # => link to author's Goodreads page
111
113
  ```
112
114
 
115
+ Look up books by an author:
116
+
117
+ ```ruby
118
+ author = client.author_Book("id")
119
+
120
+ author.id # => author id
121
+ author.name # => author name
122
+ author.link # => link to author's Goodreads page
123
+ author.books # => array of books by this author
124
+ ```
125
+
113
126
  ### Reviews
114
127
 
115
128
  Pull recent reviews:
@@ -170,7 +183,6 @@ group_list.total # => total number of groups
170
183
  group_list.group.count # => number of groups returned in the request
171
184
 
172
185
  # Loop through the list to get details for each of the groups.
173
-
174
186
  group_list.group.each do |g|
175
187
  g.id # => group id
176
188
  g.access # => access settings (private, public)
@@ -184,6 +196,14 @@ end
184
196
  The `sort` parameter is optional, and defaults to `my_activity`.
185
197
  For other sorting options, [see here](http://www.goodreads.com/api#group.list).
186
198
 
199
+ ### Pagination
200
+
201
+ To retrieve results for a particular page use the `page` param when making calls:
202
+
203
+ ```ruby
204
+ books = client.search_books("Term", page: 2)
205
+ ```
206
+
187
207
  ### OAuth
188
208
 
189
209
  For API calls requiring permission, such as write operations or browsing friends,
@@ -209,5 +229,3 @@ You're welcome to submit patches and new features.
209
229
  ## License
210
230
 
211
231
  The MIT License (MIT)
212
-
213
- Copyright (c) 2011-2015 Dan Sosedoff, <dan.sosedoff@gmail.com>
data/goodreads.gemspec CHANGED
@@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.add_development_dependency "yard", "~> 0.9"
18
18
 
19
19
  spec.add_runtime_dependency "rest-client", "~> 2.0"
20
- spec.add_runtime_dependency "hashie", "~> 2.0"
20
+ spec.add_runtime_dependency "hashie", "~> 4.1.0"
21
21
  spec.add_runtime_dependency "activesupport", ">= 3.0"
22
- spec.add_runtime_dependency "i18n", "~> 0.6"
22
+ spec.add_runtime_dependency "i18n", ">= 0.6", "< 2"
23
23
  spec.add_runtime_dependency "oauth", "~> 0.4"
24
24
 
25
25
  spec.files = `git ls-files`.split("\n")
@@ -31,9 +31,18 @@ module Goodreads
31
31
  def initialize(options = {})
32
32
  fail(ArgumentError, "Options hash required.") unless options.is_a?(Hash)
33
33
 
34
- @api_key = options[:api_key] || Goodreads.configuration[:api_key]
35
- @api_secret = options[:api_secret] || Goodreads.configuration[:api_secret]
34
+ @api_key = options[:api_key] || Goodreads.configuration[:api_key]
35
+ @api_secret = options[:api_secret] || Goodreads.configuration[:api_secret]
36
36
  @oauth_token = options[:oauth_token]
37
37
  end
38
+
39
+ # Return if this client is configured with OAuth credentials
40
+ # for a single user
41
+ #
42
+ # False when client is instantiated with an api_key and secret,
43
+ # true when client is instantiated with an oauth_token
44
+ def oauth_configured?
45
+ !oauth_token.nil?
46
+ end
38
47
  end
39
48
  end
@@ -8,6 +8,14 @@ module Goodreads
8
8
  Hashie::Mash.new(data["author"])
9
9
  end
10
10
 
11
+ # Get an author's books
12
+ #
13
+ def author_books(id, params = {})
14
+ params[:id] = id
15
+ data = request("/author/list", params)
16
+ Hashie::Mash.new(data["author"])
17
+ end
18
+
11
19
  # Search for an author by name
12
20
  #
13
21
  def author_by_name(name, params = {})
@@ -4,8 +4,8 @@ module Goodreads
4
4
  #
5
5
  # user_id - integer or string
6
6
  #
7
- def friends(user_id)
8
- data = oauth_request("/friend/user/#{user_id}")
7
+ def friends(user_id, options={})
8
+ data = oauth_request("/friend/user/#{user_id}", options)
9
9
  Hashie::Mash.new(data["friends"])
10
10
  end
11
11
  end
@@ -31,5 +31,61 @@ module Goodreads
31
31
  []
32
32
  end
33
33
  end
34
+
35
+ # Get a user's review for a given book
36
+ def user_review(user_id, book_id, params = {})
37
+ data = request('/review/show_by_user_and_book.xml', params.merge(v: "2", user_id: user_id, book_id: book_id))
38
+ Hashie::Mash.new(data["review"])
39
+ end
40
+
41
+ # Add review for a book
42
+ #
43
+ # Params can include :review, :rating, and :shelf
44
+ #
45
+ # review: text of the review (optional)
46
+ # rating: rating (0-5) (optional, default is 0 (no rating))
47
+ # shelf: Name of shelf to add book to (optional, must exist, see: shelves.list)
48
+ #
49
+ # Note that Goodreads API documentation says that this endpoint accepts
50
+ # the read_at parameter but it does not appear to work as of 2018-06.
51
+ def create_review(book_id, params = {})
52
+ params = params.merge(book_id: book_id, v: "2")
53
+
54
+ params[:read_at] = params[:read_at].strftime('%Y-%m-%d') if params[:read_at].is_a?(Time)
55
+
56
+ params[:'review[review]'] = params.delete(:review) if params[:review]
57
+ params[:'review[rating]'] = params.delete(:rating) if params[:rating]
58
+ params[:'review[read_at]'] = params.delete(:read_at) if params[:read_at]
59
+
60
+ data = oauth_request_method(:post, '/review.xml', params)
61
+
62
+ Hashie::Mash.new(data["review"])
63
+ end
64
+
65
+ # Edit review for a book
66
+ #
67
+ # Params can include :review, :rating, :read_at and :shelf, and :finished
68
+ #
69
+ # review: text of the review (optional)
70
+ # rating: rating (0-5) (optional, default is 0 (no rating))
71
+ # shelf: Name of shelf to add book to (optional, must exist, see: shelves.list)
72
+ # read_at: Time object or String in YYYY-MM-DD format (optional)
73
+ # finished: true to mark finished reading (optional)
74
+ # shelf: Name of shelf to add book to (optional, must exist, see: shelves.list)
75
+ def edit_review(review_id, params = {})
76
+ params = params.merge(v: "2")
77
+
78
+ params[:read_at] = params[:read_at].strftime('%Y-%m-%d') if params[:read_at].is_a?(Time)
79
+
80
+ params[:'review[review]'] = params.delete(:review) if params[:review]
81
+ params[:'review[rating]'] = params.delete(:rating) if params[:rating]
82
+ params[:'review[read_at]'] = params.delete(:read_at) if params[:read_at]
83
+
84
+ # Documentation says that you should use HTTP PUT, but API returns
85
+ # 401 Unauthorized when PUT is used instead of POST
86
+ data = oauth_request_method(:post, "/review/#{review_id}.xml", params)
87
+
88
+ Hashie::Mash.new(data["review"])
89
+ end
34
90
  end
35
91
  end
@@ -1,5 +1,36 @@
1
1
  module Goodreads
2
2
  module Shelves
3
+ # Lists shelves for a user
4
+ def shelves(user_id, options = {})
5
+ options = options.merge(user_id: user_id, v: 2)
6
+ data = request("/shelf/list.xml", options)
7
+ shelves = data["shelves"]
8
+
9
+ shelves = data["shelves"]["user_shelf"].map do |s|
10
+ Hashie::Mash.new({
11
+ id: s["id"],
12
+ name: s["name"],
13
+ book_count: s["book_count"],
14
+ exclusive: s["exclusive_flag"],
15
+ description: s["description"],
16
+ sort: s["sort"],
17
+ order: s["order"],
18
+ per_page: s["per_page"],
19
+ display_fields: s["display_fields"],
20
+ featured: s["featured"],
21
+ recommend_for: s["recommend_for"],
22
+ sticky: s["sticky"],
23
+ })
24
+ end
25
+
26
+ Hashie::Mash.new(
27
+ start: data["shelves"]["start"].to_i,
28
+ end: data["shelves"]["end"].to_i,
29
+ total: data["shelves"]["total"].to_i,
30
+ shelves: shelves
31
+ )
32
+ end
33
+
3
34
  # Get books from a user's shelf
4
35
  def shelf(user_id, shelf_name, options = {})
5
36
  options = options.merge(shelf: shelf_name, v: 2)
@@ -20,5 +51,42 @@ module Goodreads
20
51
  books: books
21
52
  )
22
53
  end
54
+
55
+ # Add book to a user's shelf
56
+ #
57
+ # Returns the user's review for the book, which includes all its current shelves
58
+ def add_to_shelf(book_id, shelf_name, options = {})
59
+ options = options.merge(book_id: book_id, name: shelf_name, v: 2)
60
+ data = oauth_request_method(:post, "/shelf/add_to_shelf.xml", options)
61
+
62
+ # when a book is on a single shelf it is a single hash
63
+ shelves = data["my_review"]["shelves"]["shelf"]
64
+ shelves = [shelves] unless shelves.instance_of?(Array)
65
+ shelves = shelves.map do |s|
66
+ Hashie::Mash.new({
67
+ id: s["id"].to_i,
68
+ name: s["name"],
69
+ exclusive: s["exclusive"] == "true",
70
+ sortable: s["sortable"] == "true",
71
+ })
72
+ end
73
+
74
+ Hashie::Mash.new(
75
+ id: data["my_review"]["id"].to_i,
76
+ book_id: data["my_review"]["book_id"].to_i,
77
+
78
+ rating: data["my_review"]["rating"].to_i,
79
+ body: data["my_review"]["body"],
80
+ body_raw: data["my_review"]["body_raw"],
81
+ spoiler: data["my_review"]["spoiler_flag"] == "true",
82
+
83
+ shelves: shelves,
84
+
85
+ read_at: data["my_review"]["read_at"],
86
+ started_at: data["my_review"]["started_at"],
87
+ date_added: data["my_review"]["date_added"],
88
+ updated_at: data["my_review"]["updated_at"],
89
+ )
90
+ end
23
91
  end
24
92
  end
@@ -4,4 +4,6 @@ module Goodreads
4
4
  class Forbidden < Error; end
5
5
  class Unauthorized < Error; end
6
6
  class NotFound < Error; end
7
+ class ServerError < Error; end
8
+ class UnknownError < Error; end
7
9
  end
@@ -9,12 +9,26 @@ module Goodreads
9
9
 
10
10
  protected
11
11
 
12
- # Perform an API request
12
+ # Perform an API request using API key or OAuth token
13
13
  #
14
14
  # path - Request path
15
15
  # params - Parameters hash
16
16
  #
17
+ # Will make a request with the configured API key (application
18
+ # authentication) or OAuth token (user authentication) if available.
17
19
  def request(path, params = {})
20
+ if oauth_configured?
21
+ oauth_request(path, params)
22
+ else
23
+ http_request(path, params)
24
+ end
25
+ end
26
+
27
+ # Perform an API request using API key
28
+ #
29
+ # path - Request path
30
+ # params - Parameters hash
31
+ def http_request(path, params)
18
32
  token = api_key || Goodreads.configuration[:api_key]
19
33
 
20
34
  fail(Goodreads::ConfigurationError, "API key required.") if token.nil?
@@ -32,24 +46,45 @@ module Goodreads
32
46
  fail(Goodreads::Forbidden)
33
47
  when 404
34
48
  fail(Goodreads::NotFound)
49
+ when 500..599
50
+ fail(Goodreads::ServerError.new(response.code))
51
+ else
52
+ fail(Goodreads::UnknownError.new(response.code))
35
53
  end
36
54
  end
37
55
 
38
56
  parse(resp)
39
57
  end
40
58
 
59
+ # Perform an OAuth API GET request. Goodreads must have been initialized with a valid OAuth access token.
60
+ #
61
+ # path - Request path
62
+ # params - Parameters hash
63
+ #
64
+ def oauth_request(path, params = {})
65
+ oauth_request_method(:get, path, params)
66
+ end
67
+
41
68
  # Perform an OAuth API request. Goodreads must have been initialized with a valid OAuth access token.
42
69
  #
70
+ # http_method - HTTP verb supported by OAuth gem (one of :get, :post, :delete, etc.)
43
71
  # path - Request path
44
72
  # params - Parameters hash
45
73
  #
46
- def oauth_request(path, params = nil)
74
+ def oauth_request_method(http_method, path, params = {})
47
75
  fail "OAuth access token required!" unless @oauth_token
48
- if params
49
- url_params = params.map { |k, v| "#{k}=#{v}" }.join("&")
50
- path = "#{path}?#{url_params}"
76
+
77
+ headers = { "Accept" => "application/xml" }
78
+
79
+ resp = if http_method == :get || http_method == :delete
80
+ if params
81
+ url_params = params.map { |k, v| "#{k}=#{v}" }.join("&")
82
+ path = "#{path}?#{url_params}"
83
+ end
84
+ @oauth_token.request(http_method, path, headers)
85
+ else
86
+ @oauth_token.request(http_method, path, params || {}, headers)
51
87
  end
52
- resp = @oauth_token.get(path, "Accept" => "application/xml")
53
88
 
54
89
  case resp
55
90
  when Net::HTTPUnauthorized
@@ -1,3 +1,3 @@
1
1
  module Goodreads
2
- VERSION = "0.6.0"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -1,24 +1,37 @@
1
1
  require "spec_helper"
2
2
 
3
- describe "Client" do
3
+ describe Goodreads::Client do
4
4
  before :each do
5
5
  Goodreads.reset_configuration
6
6
  end
7
7
 
8
- it "raises Goodreads::ConfigurationError if API key was not provided" do
9
- client = Goodreads::Client.new
8
+ let(:client) do
9
+ described_class.new(api_key: api_key)
10
+ end
11
+
12
+ context "API key is missing" do
13
+ let(:api_key) {}
10
14
 
11
- expect { client.book_by_isbn("0307463745") }
12
- .to raise_error(Goodreads::ConfigurationError, "API key required.")
15
+ it "throws an error" do
16
+ expect { client.book_by_isbn("0307463745") }
17
+ .to raise_error(Goodreads::ConfigurationError, "API key required.")
18
+ end
13
19
  end
14
20
 
15
- it "raises Goodreads::Unauthorized if API key is not valid" do
16
- client = Goodreads::Client.new(api_key: "INVALID_KEY")
21
+ context "API key is invalid" do
22
+ let(:api_key) { "INVALID_KEY" }
23
+
24
+ before do
25
+ stub_request(:get, "https://www.goodreads.com/book/isbn?format=xml&isbn=1&key=INVALID_KEY")
26
+ .to_return(status: 401)
17
27
 
18
- stub_request(:get, "https://www.goodreads.com/book/isbn?format=xml&isbn=054748250711&key=INVALID_KEY")
19
- .to_return(status: 401)
28
+ stub_request(:get, "https://www.goodreads.com/book/isbn?format=xml&isbn=2&key=INVALID_KEY")
29
+ .to_return(status: 403)
30
+ end
20
31
 
21
- expect { client.book_by_isbn("054748250711") }
22
- .to raise_error(Goodreads::Unauthorized)
32
+ it "throws errors" do
33
+ expect { client.book_by_isbn("1") }.to raise_error(Goodreads::Unauthorized)
34
+ expect { client.book_by_isbn("2") }.to raise_error(Goodreads::Forbidden)
35
+ end
23
36
  end
24
37
  end
data/spec/client_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
  require "oauth"
3
3
 
4
- describe "Client" do
4
+ describe Goodreads::Client do
5
5
  let(:client) { Goodreads::Client.new(api_key: "SECRET_KEY") }
6
6
  before(:each) { Goodreads.reset_configuration }
7
7
 
@@ -17,6 +17,45 @@ describe "Client" do
17
17
  end
18
18
  end
19
19
 
20
+ describe "#oauth_configured?" do
21
+ it "is true when OAuth token provided to constructor" do
22
+ client = Goodreads::Client.new(oauth_token: "a token")
23
+ expect(client.oauth_configured?).to be true
24
+ end
25
+
26
+ it "is true when oauth token is not provided to constructor" do
27
+ client = Goodreads::Client.new(api_key: "SECRET_KEY")
28
+ expect(client.oauth_configured?).to be false
29
+ end
30
+ end
31
+
32
+ describe "#request" do
33
+ context "with oauth token" do
34
+ it "makes an oauth request" do
35
+ oauth_token = double
36
+ response = double
37
+
38
+ allow(oauth_token).to receive(:request) { response }
39
+ allow(response).to receive(:body) { fixture("book.xml") }
40
+
41
+ client = Goodreads::Client.new(oauth_token: oauth_token)
42
+ expect(client.book(123)).to be_a Hash
43
+ end
44
+ end
45
+
46
+ context "without oauth token" do
47
+ before do
48
+ allow(client).to receive(:http_request) {
49
+ Hash.from_xml(fixture("book.xml"))["GoodreadsResponse"]
50
+ }
51
+ end
52
+
53
+ it "makes a request" do
54
+ expect(client.book(123)).to be_a Hash
55
+ end
56
+ end
57
+ end
58
+
20
59
  describe "#book_by_isbn" do
21
60
  before { stub_with_key_get("/book/isbn", { isbn: "0307463745" }, "book.xml") }
22
61
 
@@ -154,6 +193,26 @@ describe "Client" do
154
193
  end
155
194
  end
156
195
 
196
+ describe "#user_review" do
197
+ let(:consumer) { OAuth::Consumer.new("API_KEY", "SECRET_KEY", site: "https://www.goodreads.com") }
198
+ let(:token) { OAuth::AccessToken.new(consumer, "ACCESS_TOKEN", "ACCESS_SECRET") }
199
+
200
+ it "returns a user's existing review" do
201
+ stub_request(:get, "https://www.goodreads.com/review/show_by_user_and_book.xml?book_id=50&user_id=1&v=2")
202
+ .to_return(status: 200, body: fixture("review_show_by_user_and_book.xml"))
203
+
204
+ client = Goodreads::Client.new(api_key: "SECRET_KEY", oauth_token: token)
205
+ review = client.user_review(1, 50)
206
+
207
+ expect(review.id).to eq("21")
208
+ expect(review.book.id).to eq(50)
209
+
210
+ expect(review.rating).to eq("5")
211
+ expect(review.body.strip).to eq("")
212
+ expect(review.date_added).to eq("Tue Aug 29 11:20:01 -0700 2006")
213
+ end
214
+ end
215
+
157
216
  describe "#author" do
158
217
  before { stub_with_key_get("/author/show", { id: "18541" }, "author.xml") }
159
218
 
@@ -203,6 +262,23 @@ describe "Client" do
203
262
  end
204
263
  end
205
264
 
265
+ describe "#author_books" do
266
+ before do
267
+ stub_with_key_get("/author/list", { id: "18541" }, "author_books.xml")
268
+ end
269
+
270
+ it "returns author's books" do
271
+ author = client.author_books("18541")
272
+
273
+ expect(author).to be_a(Hashie::Mash)
274
+ expect(author.id).to eq("18541")
275
+ expect(author.name).to eq("Tim O'Reilly")
276
+ expect(author.link).to eq("https://www.goodreads.com/author/show/18541.Tim_O_Reilly")
277
+
278
+ expect(author.books.book.size).to eq(30)
279
+ end
280
+ end
281
+
206
282
  describe "#user" do
207
283
  before { stub_with_key_get("/user/show", { id: "878044" }, "user.xml") }
208
284
 
@@ -287,6 +363,131 @@ describe "Client" do
287
363
  end
288
364
  end
289
365
 
366
+ describe "#add_to_shelf" do
367
+ let(:consumer) { OAuth::Consumer.new("API_KEY", "SECRET_KEY", site: "https://www.goodreads.com") }
368
+ let(:token) { OAuth::AccessToken.new(consumer, "ACCESS_TOKEN", "ACCESS_SECRET") }
369
+
370
+ it "adds a book to a user's shelf" do
371
+ stub_request(:post, "https://www.goodreads.com/shelf/add_to_shelf.xml")
372
+ .with(:body => {"book_id"=>"456", "name"=>"read", "v"=>"2"})
373
+ .to_return(status: 201, body: fixture("shelf_add_to_shelf.xml"))
374
+
375
+ client = Goodreads::Client.new(api_key: "SECRET_KEY", oauth_token: token)
376
+ review = client.add_to_shelf(456, "read")
377
+
378
+ expect(review.id).to eq(2416981504)
379
+ expect(review.book_id).to eq(456)
380
+
381
+ expect(review.rating).to eq(0)
382
+ expect(review.body).to be nil
383
+ expect(review.body_raw).to be nil
384
+ expect(review.spoiler).to be false
385
+
386
+ expect(review.shelves.size).to eq(1)
387
+ expect(review.shelves.first.name).to eq("read")
388
+ expect(review.shelves.first.id).to eq(269274694)
389
+ expect(review.shelves.first.exclusive).to be true
390
+ expect(review.shelves.first.sortable).to be false
391
+
392
+
393
+ expect(review.read_at).to be nil
394
+ expect(review.started_at).to be nil
395
+ expect(review.date_added).to eq("Thu Jun 07 19:58:19 -0700 2018")
396
+ expect(review.updated_at).to eq("Thu Jun 07 19:58:53 -0700 2018")
397
+
398
+ expect(review.body).to be nil
399
+ expect(review.body_raw).to be nil
400
+ expect(review.spoiler).to be false
401
+ end
402
+ end
403
+
404
+ describe "#create_review" do
405
+ let(:consumer) { OAuth::Consumer.new("API_KEY", "SECRET_KEY", site: "https://www.goodreads.com") }
406
+ let(:token) { OAuth::AccessToken.new(consumer, "ACCESS_TOKEN", "ACCESS_SECRET") }
407
+
408
+ it "creates a new review for a book" do
409
+ stub_request(:post, "https://www.goodreads.com/review.xml")
410
+ .with(:body => {
411
+ "book_id"=>"456",
412
+ "review" => {
413
+ "rating" => "3",
414
+ "review" => "Good book.",
415
+ "read_at" => "2018-01-02",
416
+ },
417
+ "shelf" => "read",
418
+ "v"=>"2",
419
+ })
420
+ .to_return(status: 201, body: fixture("review_create.xml"))
421
+
422
+ client = Goodreads::Client.new(api_key: "SECRET_KEY", oauth_token: token)
423
+ review = client.create_review(456, {
424
+ :review => "Good book.",
425
+ :rating => 3,
426
+ :read_at => Time.parse('2018-01-02'),
427
+ :shelf => "read",
428
+ })
429
+
430
+ expect(review.id).to eq("67890")
431
+ expect(review.book.id).to eq(456)
432
+ expect(review.rating).to eq("3")
433
+ expect(review.body).to eq("Good book.")
434
+ end
435
+ end
436
+
437
+ describe "#edit_review" do
438
+ let(:consumer) { OAuth::Consumer.new("API_KEY", "SECRET_KEY", site: "https://www.goodreads.com") }
439
+ let(:token) { OAuth::AccessToken.new(consumer, "ACCESS_TOKEN", "ACCESS_SECRET") }
440
+
441
+ it "creates a new review for a book" do
442
+ stub_request(:post, "https://www.goodreads.com/review/67890.xml")
443
+ .with(:body => {
444
+ "finished" => "true",
445
+ "review" => {
446
+ "rating" => "5",
447
+ "review" => "Fantastic book.",
448
+ "read_at" => "2018-04-15",
449
+ },
450
+ "shelf" => "read",
451
+ "v"=>"2",
452
+ })
453
+ .to_return(status: 201, body: fixture("review_update.xml"))
454
+
455
+ client = Goodreads::Client.new(api_key: "SECRET_KEY", oauth_token: token)
456
+ review = client.edit_review(67890, {
457
+ :finished => true,
458
+ :review => "Fantastic book.",
459
+ :rating => 5,
460
+ :read_at => Time.parse('2018-04-15'),
461
+ :shelf => "read",
462
+ })
463
+
464
+ expect(review.id).to eq("67890")
465
+ expect(review.book.id).to eq(456)
466
+ expect(review.rating).to eq("5")
467
+ expect(review.body).to eq("Fantastic book.")
468
+ end
469
+ end
470
+
471
+ describe "#shelves" do
472
+ it "returns list of user's shelves" do
473
+ stub_with_key_get("/shelf/list.xml", { user_id: 123, v: "2" }, "shelf_list.xml")
474
+
475
+ response = client.shelves(123)
476
+
477
+ expect(response).to respond_to(:start)
478
+ expect(response).to respond_to(:end)
479
+ expect(response).to respond_to(:total)
480
+ expect(response).to respond_to(:shelves)
481
+
482
+ expect(response.start).to eq(1)
483
+ expect(response.end).to eq(3)
484
+ expect(response.total).to eq(3)
485
+ expect(response.shelves.length).to eq(3)
486
+ expect(response.shelves.first.id).to eq(9049904)
487
+ expect(response.shelves.first.name).to eq("read")
488
+ end
489
+ end
490
+
290
491
  describe "#user_id" do
291
492
  let(:consumer) { OAuth::Consumer.new("API_KEY", "SECRET_KEY", site: "https://www.goodreads.com") }
292
493
  let(:token) { OAuth::AccessToken.new(consumer, "ACCESS_TOKEN", "ACCESS_SECRET") }