olibrary 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ require 'rest-client'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'hashie'
7
+
8
+ module Olibrary
9
+ module Request
10
+ API_URL = 'https://openlibrary.org'
11
+
12
+ protected
13
+
14
+ HANDLE_REST_CLIENT_RESPONSE = lambda do |response, request, result, &block|
15
+ case response.code
16
+ when 200
17
+ response.return!(&block)
18
+ when 401
19
+ raise Olibrary::Unauthorized
20
+ when 404
21
+ raise Olibrary::NotFound
22
+ when 302
23
+ raise Olibrary::Redirect
24
+ end
25
+ end
26
+
27
+ # Perform a GET request
28
+ #
29
+ # path - Request path
30
+ #
31
+ def request(path, params={})
32
+ request_url = "#{API_URL}#{path}"
33
+ perform_get_request(request_url, params)
34
+ end
35
+
36
+
37
+ # Get the history of an object in Open Library
38
+ #
39
+ # object - Object key, e.g., '/books/OL1M'
40
+ #
41
+ def history(object, params={})
42
+ history_url = "#{API_URL}#{object}.json?m=history"
43
+ perform_get_request(history_url, params)
44
+ end
45
+
46
+ # Perform a query using the Query API
47
+ #
48
+ # query - Query path, e.g. "type=/type/edition&isbn_10=XXXXXXXXXX"
49
+ #
50
+ def query(query, params={})
51
+ query_url = "#{API_URL}/query.json?#{query}"
52
+ perform_get_request(query_url, params)
53
+ end
54
+
55
+ def protected_login(username, password, params={})
56
+ params.merge!(content_type: :json, accept: :json)
57
+ url = "#{API_URL}/account/login"
58
+ login = { 'username' => username, 'password' => password }.to_json
59
+
60
+ resp = RestClient.post(url, login, params) do |response|
61
+ case response.code
62
+ when 200
63
+ session = response.cookies
64
+ session
65
+ when 401
66
+ raise Olibrary::Unauthorized
67
+ when 404
68
+ raise Olibrary::NotFound
69
+ when 302
70
+ raise Olibrary::Redirect
71
+ end
72
+ end
73
+ end
74
+
75
+ # Update an Open Library object
76
+ #
77
+ # key - Object key the Open Library resource
78
+ # cookie - Cookie retrieved by the login method
79
+ # update - Complete updated Open Library object in JSON format
80
+ # comment - Comment describing the change(s) to the object
81
+ #
82
+ def update(key, cookie, update, comment, params={})
83
+ cookie_header = { 'Cookie' => cookie }
84
+ comment_header = {
85
+ 'Opt' => '"http://openlibrary.org/dev/docs/api"; ns=42',
86
+ '42-comment' => comment
87
+ }
88
+ params.merge!(cookie_header)
89
+ params.merge!(comment_header)
90
+ params.merge!(content_type: :json, accept: :json)
91
+ update_url = "#{API_URL}#{key}"
92
+
93
+ response = RestClient.put(update_url, update, params, &HANDLE_REST_CLIENT_RESPONSE)
94
+ parse(response)
95
+ end
96
+
97
+ def parse(response)
98
+ JSON.parse(response.body)
99
+ end
100
+ private :parse
101
+
102
+ def perform_get_request(url, params)
103
+ params.merge!(accept: :json)
104
+ response = RestClient.get(url, params, &HANDLE_REST_CLIENT_RESPONSE)
105
+ parse(response)
106
+ end
107
+ private :perform_get_request
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module Olibrary
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,51 @@
1
+ module Olibrary
2
+
3
+ class View
4
+ attr_accessor :bib_key
5
+ attr_accessor :info_url
6
+ attr_accessor :preview
7
+ attr_accessor :preview_url
8
+ attr_accessor :thumbnail_url
9
+
10
+ def self.find_by_isbn(key)
11
+ find("ISBN",key)
12
+ end
13
+
14
+ def self.find_by_lccn(key)
15
+ find("LCCN",key)
16
+ end
17
+
18
+ def self.find_by_oclc(key)
19
+ find("OCLC",key)
20
+ end
21
+
22
+ def self.find_by_olid(key)
23
+ find("OLID",key)
24
+ end
25
+
26
+ def self.find(type,key)
27
+ type_for_uri = URI.encode_www_form_component(type)
28
+ key_for_uri = URI.encode_www_form_component(key)
29
+
30
+ response = RestClient.get "http://openlibrary.org/api/books" +
31
+ "?bibkeys=#{type_for_uri}:#{key_for_uri}&format=json&jscmd=viewapi"
32
+ response_data = JSON.parse(response)
33
+ view = response_data["#{type}:#{key}"]
34
+
35
+ if view
36
+ view_meta = new
37
+
38
+ view_meta.bib_key = view["bib_key"]
39
+ view_meta.info_url = view["info_url"]
40
+ view_meta.preview = view["preview"]
41
+ view_meta.preview_url = view["preview_url"]
42
+ view_meta.thumbnail_url = view["thumbnail_url"]
43
+
44
+ view_meta
45
+ else
46
+ nil
47
+ end
48
+ end
49
+ end
50
+
51
+ end
data/lib/olibrary.rb ADDED
@@ -0,0 +1,21 @@
1
+ require_relative 'olibrary/version'
2
+ require_relative 'olibrary/data'
3
+ require_relative 'olibrary/view'
4
+ require_relative 'olibrary/client'
5
+ require_relative 'olibrary/errors'
6
+ require_relative 'olibrary/request'
7
+ require_relative 'olibrary/details'
8
+
9
+ module Olibrary
10
+ # Create a new Olibrary::Client instance
11
+ #
12
+ def self.new(options={})
13
+ Olibrary::Client.new(options)
14
+ end
15
+
16
+ # Return the openlibrary gem version
17
+ #
18
+ def self.version
19
+ "Olibrary version #{Olibrary::VERSION}"
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "olibrary/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "olibrary"
7
+ s.version = Olibrary::VERSION
8
+ s.authors = ["Yosef Serkez"]
9
+ s.email = ["yosefserkez@gmail.com"]
10
+ s.homepage = "https://github.com/4till2/olibrary"
11
+ s.summary = %q{Ruby Interface for the openlibrary.org API}
12
+ s.description = %q{openlibrary.org API Interface}
13
+
14
+ s.rubyforge_project = "olibrary"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'rspec', '~> 3.11.0'
22
+ s.add_development_dependency 'webmock', '~> 3.14.0'
23
+
24
+ s.add_runtime_dependency 'hashie', '~> 5.0.0'
25
+ s.add_runtime_dependency 'json', '>= 2.6.1'
26
+ s.add_runtime_dependency 'rest-client', '~> 2.1.0'
27
+
28
+ s.add_runtime_dependency 'activesupport', '~> 7.0.2.3'
29
+ end
@@ -0,0 +1,256 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Client' do
4
+ let(:client) { Olibrary::Client.new }
5
+
6
+ describe '#new' do
7
+ it 'requires an argument' do
8
+ expect { Olibrary::Client.new(nil) }.
9
+ to raise_error ArgumentError, "Options hash required."
10
+ end
11
+
12
+ it 'requires a hash argument' do
13
+ expect { Olibrary::Client.new('foo') }.
14
+ to raise_error ArgumentError, "Options hash required."
15
+ end
16
+ end
17
+
18
+ describe '#book' do
19
+ before do
20
+ olid = 'OL23109860M'
21
+ stub_get("/books/#{olid}", 'book.json')
22
+ end
23
+ it 'returns book details' do
24
+ expect { client.book('OL23109860M') }.not_to raise_error
25
+
26
+ book = client.book('OL23109860M')
27
+
28
+ expect(book).to be_a Hashie::Mash
29
+ expect(book.contributors).to be_a Array
30
+ expect(book.covers).to be_a Array
31
+ expect(book.works).to be_a Array
32
+
33
+ expect(book.title).to eq 'The Great Gatsby'
34
+ expect(book.by_statement).to eq 'F. Scott Fitzgerald.'
35
+ expect(book.number_of_pages).to eq 180
36
+ expect(book.contributors[0].name).to eq 'Francis Cugat'
37
+ expect(book.contributors[0].role).to eq 'Cover Art'
38
+ expect(book.copyright_date).to eq '1925'
39
+ expect(book.isbn_10[0]).to eq '0743273567'
40
+ expect(book.identifiers.goodreads[0]).to eq '4671'
41
+ expect(book.identifiers.google[0]).to eq 'iXn5U2IzVH0C'
42
+ expect(book.physical_format).to eq 'Trade Paperback'
43
+ expect(book.publishers[0]).to eq 'Scribner'
44
+ expect(book.subjects[1]).to eq 'First loves -- Fiction'
45
+ expect(book.subjects[0]).to eq 'Traffic accidents -- Fiction'
46
+
47
+ # Because of a conflict with the internal `key?` method of
48
+ # Hashie::Mash, any key actually named 'key' must be referenced
49
+ # with a bang (!) to get the value.
50
+ expect(book.key!).to eq '/books/OL23109860M'
51
+ expect(book.languages[0].key!).to eq '/languages/eng'
52
+ end
53
+ end
54
+
55
+ describe '#book_by_isbn' do
56
+ before do
57
+ isbn_10 = '046503912X'
58
+ type = '/type/edition'
59
+ stub_get("/query.json?type=#{type}&isbn_10=#{isbn_10}",
60
+ 'book_by_isbn.json')
61
+ end
62
+ it 'returns array of books' do
63
+ expect { client.book_by_isbn('046503912X').not_to raise_error }
64
+ expect { client.book_by_isbn('123').
65
+ to raise_error ArgumentError, "ISBN must be 10 or 13 characters." }
66
+
67
+ books = client.book_by_isbn('046503912X')
68
+
69
+ expect(books).to be_a Array
70
+ expect(books[0]).to be_a Hash
71
+
72
+ expect(books[0]['key']).to eq '/books/OL6807502M'
73
+ end
74
+ end
75
+
76
+ describe '#book_by_lccn' do
77
+ before do
78
+ lccn = '00271772'
79
+ type = '/type/edition'
80
+ stub_get("/query.json?type=#{type}&lccn=#{lccn}",
81
+ 'book_by_lccn.json')
82
+ end
83
+ it 'returns array of books' do
84
+ expect { client.book_by_lccn('00271772').not_to raise_error }
85
+
86
+ books = client.book_by_lccn('00271772')
87
+
88
+ expect(books).to be_a Array
89
+ expect(books[0]).to be_a Hash
90
+ end
91
+ end
92
+
93
+ describe '#book_by_oclc' do
94
+ before do
95
+ oclc = '42860053'
96
+ type = '/type/edition'
97
+ stub_get("/query.json?type=#{type}&oclc_numbers=#{42860053}",
98
+ 'book_by_oclc.json')
99
+ end
100
+ it 'returns array of books' do
101
+ expect { client.book_by_oclc('42860053').not_to raise_error }
102
+
103
+ books = client.book_by_oclc('42860053')
104
+
105
+ expect(books).to be_a Array
106
+ expect(books[0]).to be_a Hash
107
+ end
108
+ end
109
+
110
+ describe '#author' do
111
+ before do
112
+ key = 'OL1A'
113
+ stub_get("/authors/#{key}", 'author.json')
114
+ end
115
+
116
+ it 'returns author details' do
117
+ expect { client.author('OL1A') }.not_to raise_error
118
+
119
+ author = client.author('OL1A')
120
+
121
+ expect(author).to be_a Hashie::Mash
122
+ expect(author.name).to eq 'Sachi Rautroy'
123
+ expect(author.personal_name).to eq 'Sachi Rautroy'
124
+ expect(author.death_date).to eq '2004'
125
+ expect(author.birth_date).to eq '1916'
126
+ expect(author.last_modified.type).to eq '/type/datetime'
127
+ expect(author.last_modified.value).to eq '2008-11-16T07:25:54.131674'
128
+ expect(author.id).to eq 97
129
+ expect(author.revision).to eq 6
130
+
131
+ # Because of a conflict with the internal `key?` method of
132
+ # Hashie::Mash, any key actually named 'key' must be referenced
133
+ # with a bang (!) to get the value.
134
+ expect(author.key!).to eq '/authors/OL1A'
135
+ end
136
+ end
137
+
138
+ describe '#rev_history' do
139
+ before do
140
+ key = '/books/OL1M'
141
+ stub_get("#{key}.json?m=history", 'history.json')
142
+ end
143
+
144
+ it 'returns the revision history of an object' do
145
+ expect { client.rev_history('/books/OL1M') }.not_to raise_error
146
+
147
+ history = client.rev_history('/books/OL1M')
148
+
149
+ expect(history).to be_a Array
150
+ expect(history[0]).to be_a Hash
151
+ end
152
+ end
153
+
154
+ describe '#recent' do
155
+ before do
156
+ stub_get("/recentchanges", 'recent.json')
157
+ end
158
+
159
+ it 'returns recent changes to Open Library' do
160
+ expect { client.recent.not_to raise_error }
161
+
162
+ changes = client.recent
163
+
164
+ expect(changes).to be_a Array
165
+ expect(changes[0]).to be_a Hash
166
+ end
167
+ end
168
+
169
+ describe '#editions' do
170
+ before do
171
+ work = 'OL27258W'
172
+ stub_get("/works/#{work}/editions.json?limit=10&offset=0", 'editions.json')
173
+ end
174
+
175
+ it 'returns the editions of a work' do
176
+ expect { client.editions('OL27258W') }.not_to raise_error
177
+
178
+ editions = client.editions('OL27258W', 10, 0)
179
+
180
+ expect(editions).to be_a Hashie::Mash
181
+ expect(editions.entries).to be_a Array
182
+
183
+ expect(editions.size!).to eq 19
184
+ expect(editions.links.next).to eq '/works/OL27258W/editions.json?limit=10&offset=10'
185
+ expect(editions.links.self).to eq '/works/OL27258W/editions.json?limit=10&offset=0'
186
+ expect(editions.links.work).to eq '/works/OL27258W'
187
+
188
+ # Failing tests for iteration through entries
189
+ #
190
+ # editions.entries[0].should be_a Hashie::Mash
191
+ # editions.entries[0].number_of_pages.should eq 322
192
+ end
193
+ end
194
+
195
+ describe '#search' do
196
+ before do
197
+ stub_get("/search.json?author=tolkien&title=lord%20of%20the%20rings", 'search.json')
198
+ stub_get("/search.json?q=capitalism%20and%20freedom", 'free_search.json')
199
+ end
200
+
201
+ it 'returns book search results' do
202
+ search = {title: "lord of the rings", author: "tolkien"}
203
+ expect {client.search(search)}.not_to raise_error
204
+
205
+ search = client.search(search, 5, 10)
206
+
207
+ expect(search.size).to eq 5
208
+ expect(search[0].key!).to eq 'OL14926051W'
209
+ expect(search[0].title).to eq 'The Lord of Rings'
210
+
211
+ expect {client.search("capitalism and freedom")}.not_to raise_error
212
+ free_search = client.search("capitalism and freedom")
213
+
214
+ expect(free_search.size).to eq 10
215
+ expect(free_search[0].key!).to eq 'OL2747782W'
216
+ expect(free_search[0].author_name[0]).to eq 'Milton Friedman'
217
+ end
218
+ end
219
+
220
+ describe '#login' do
221
+ before do
222
+ stub_http_request(:post, "https://openlibrary.org/account/login").
223
+ with( body: "{\"username\":\"username\",\"password\":\"password\"}" ).
224
+ to_return( status: 200, headers: {'Set-Cookie' => 'session=cookie'} )
225
+ end
226
+
227
+ it 'logs in to Open Library' do
228
+ expect { client.login('username', 'password') }.not_to raise_error
229
+
230
+ cookie = client.login('username', 'password')
231
+ expect(cookie).to eq "cookie"
232
+ end
233
+ end
234
+
235
+ describe '#after_save' do
236
+ before do
237
+ key = "/books/OL9674499M"
238
+ comment = 'update weight and number of pages'
239
+ stub_put(key, 'save_after_change.json', comment)
240
+ end
241
+
242
+ it 'PUTs the updated object, and receives the updated object as a response' do
243
+ key = "/books/OL9674499M"
244
+ cookie = 'cookie'
245
+ update = fixture('save_after_change.json')
246
+ comment = 'update weight and number of pages'
247
+
248
+ expect { client.save(key, cookie, update, comment) }.not_to raise_error
249
+
250
+ object = client.save(key, cookie, update, comment)
251
+
252
+ expect(object.weight).to eq '1.5 pounds'
253
+ expect(object.number_of_pages).to eq 1103
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,12 @@
1
+ { "birth_date" : "1916",
2
+ "death_date" : "2004",
3
+ "id" : 97,
4
+ "key" : "/authors/OL1A",
5
+ "last_modified" : { "type" : "/type/datetime",
6
+ "value" : "2008-11-16T07:25:54.131674"
7
+ },
8
+ "name" : "Sachi Rautroy",
9
+ "personal_name" : "Sachi Rautroy",
10
+ "revision" : 6,
11
+ "type" : { "key" : "/type/author" }
12
+ }
@@ -0,0 +1,59 @@
1
+ { "authors" : [ { "key" : "/authors/OL27349A" } ],
2
+ "by_statement" : "F. Scott Fitzgerald.",
3
+ "classifications" : { },
4
+ "contributors" : [ { "name" : "Francis Cugat",
5
+ "role" : "Cover Art"
6
+ } ],
7
+ "copyright_date" : "1925",
8
+ "covers" : [ 6776207,
9
+ 6725316,
10
+ 6390463
11
+ ],
12
+ "created" : { "type" : "/type/datetime",
13
+ "value" : "2009-02-18T18:42:07.162489"
14
+ },
15
+ "dewey_decimal_class" : [ "813" ],
16
+ "genres" : [ "Fiction." ],
17
+ "ia_box_id" : [ "IA119605" ],
18
+ "identifiers" : { "goodreads" : [ "4671" ],
19
+ "google" : [ "iXn5U2IzVH0C" ],
20
+ "librarything" : [ "2964" ]
21
+ },
22
+ "isbn_10" : [ "0743273567" ],
23
+ "isbn_13" : [ "9780743273565" ],
24
+ "key" : "/books/OL23109860M",
25
+ "languages" : [ { "key" : "/languages/eng" } ],
26
+ "last_modified" : { "type" : "/type/datetime",
27
+ "value" : "2011-08-12T01:22:25.394416"
28
+ },
29
+ "latest_revision" : 12,
30
+ "lc_classifications" : [ "PS3511.I9 G7 2004" ],
31
+ "lccn" : [ "25010468" ],
32
+ "notes" : { "type" : "/type/text",
33
+ "value" : "* [Publisher's Website for this edition][1]\r\n\r\n\r\n [1]: http://books.simonandschuster.com/Great-Gatsby/F-Scott-Fitzgerald/9780743273565"
34
+ },
35
+ "number_of_pages" : 180,
36
+ "ocaid" : "greatgat00fitz",
37
+ "oclc_number" : [ "57215622" ],
38
+ "oclc_numbers" : [ "57215622" ],
39
+ "pagination" : "180 p. ;",
40
+ "physical_format" : "Trade Paperback",
41
+ "publish_country" : "nyu",
42
+ "publish_date" : "2004",
43
+ "publish_places" : [ "New York, New York, USA" ],
44
+ "publishers" : [ "Scribner" ],
45
+ "revision" : 12,
46
+ "source_records" : [ "marc:SanFranPL14/SanFranPL14.out:71962031:12930",
47
+ "ia:greatgat00fitz"
48
+ ],
49
+ "subjects" : [ "Traffic accidents -- Fiction",
50
+ "First loves -- Fiction",
51
+ "Rich people -- Fiction",
52
+ "Mistresses -- Fiction",
53
+ "Revenge -- Fiction",
54
+ "Long Island (N.Y.) -- Fiction"
55
+ ],
56
+ "title" : "The Great Gatsby",
57
+ "type" : { "key" : "/type/edition" },
58
+ "works" : [ { "key" : "/works/OL468431W" } ]
59
+ }
@@ -0,0 +1 @@
1
+ [{"key": "/books/OL6807502M"}]
@@ -0,0 +1 @@
1
+ [{"key": "/books/OL6807502M"}]
@@ -0,0 +1 @@
1
+ [{"key": "/books/OL6807502M"}]