alexandrite 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +115 -0
- data/Rakefile +8 -0
- data/alexandrite.gemspec +33 -0
- data/lib/alexandrite/version.rb +5 -0
- data/lib/alexandrite.rb +84 -0
- data/lib/alexandrite_books.rb +73 -0
- data/lib/alexandrite_bookshelf.rb +15 -0
- data/lib/alexandrite_google.rb +76 -0
- data/lib/alexandrite_oclc.rb +137 -0
- data/lib/error_type/errors.rb +7 -0
- data/lib/helpers/format.rb +18 -0
- data/lib/helpers/validation.rb +23 -0
- data/spec/alexandrite_spec.rb +26 -0
- data/spec/factories/book.rb +8 -0
- data/spec/helpers/format_spec.rb +53 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/vcr_setup.rb +15 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e1d0a59468bf1b22e33d17848c07d9af25172aadbc78723fbf014d42021bcd55
|
4
|
+
data.tar.gz: ffb2f6b45788d233ebb91b601f1ea256ba21b54da4bad154e548f4e20bb9f59b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a706821501297809cad6e2c88c917ae8ea4fd276b5d9fde402b0fddf3aeac0094fdc13f42ac48edd551e397f0b7d34cadab6f3aed3ffa1c21abd1b8c02f7526c
|
7
|
+
data.tar.gz: f448f181f9980721ef93df28392ffcd8210687033c8257b470cde53693b07c63015f14807a15a70ce649f45789034c2d10fb3e306e007e96d9f083b77dd6d64e
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.idea
|
24
|
+
.rvmrc
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Juan Paulo Pérez-Tejada
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# gisbn GEM [](https://travis-ci.org/eftakhairul/gisbn) [](https://github.com/eftakhairul/gisbn/issues) [](https://raw.githubusercontent.com/eftakhairul/gisbn/master/LICENSE.txt)
|
2
|
+
|
3
|
+
It fetches book's information by ISBN number based on Google Book API.
|
4
|
+
|
5
|
+
- Easy to get all information by ISBN
|
6
|
+
- Structure data
|
7
|
+
|
8
|
+
You don't have to call manually Google API with ISBN number. This gem will do everything for you.
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
### Version
|
13
|
+
0.0.1
|
14
|
+
|
15
|
+
|
16
|
+
### Installation
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'alexandrite'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
$ bundle
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as globally:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
gem install alexandrite
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
## Examples
|
37
|
+
book = Gisbn::Book.new "0262033844", "AIzaSyDKepjfaVBRcgsnPALw5s2UNyfOk-1FHUU", "ca"
|
38
|
+
|
39
|
+
book.title
|
40
|
+
#=> "Introduction to Algorithms"
|
41
|
+
|
42
|
+
book.description
|
43
|
+
#=> "A new edition of the essential text and professional reference, with substantial newmaterial on such topics as vEB trees, multithreaded algorithms, dynamic programming, and edge-baseflow."
|
44
|
+
|
45
|
+
book.publisher
|
46
|
+
#=> "MIT Press"
|
47
|
+
|
48
|
+
book.published_date
|
49
|
+
#=> <Date: 2001-02-03 ...>
|
50
|
+
|
51
|
+
book.isbn_10
|
52
|
+
#=> 0262033844
|
53
|
+
|
54
|
+
book.isbn_13
|
55
|
+
#=> 9780262033848
|
56
|
+
|
57
|
+
book.thumbnail
|
58
|
+
#=> http://books.google.com/books/content?id=i-bUBQAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api
|
59
|
+
|
60
|
+
book.preview
|
61
|
+
#=> http://books.google.ca/books?id=i-bUBQAAQBAJ&printsec=frontcover&dq=isbn:0262033844&hl=&cd=1&source=gbs_api
|
62
|
+
|
63
|
+
|
64
|
+
book.gisbn.isbn = 9780321573513
|
65
|
+
book.fetch.title => "Algorithms"
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
## Methods
|
70
|
+
title >> Returns Book's title as string
|
71
|
+
description >> Returns the description of book as string
|
72
|
+
isbn(isbn_number) >> Set new isbn
|
73
|
+
fetch >> Call to Google Book API and process book information on provided ISBN
|
74
|
+
authors >> Return authors' name as comma separated as string
|
75
|
+
authors_as_array >> Return authors' name as ruby array. If no book is associated with ISBN number,
|
76
|
+
then it return empty array
|
77
|
+
publisher >> Return publisher name as string
|
78
|
+
isbn_10 >> Return 10 digit ISBN numbers as string
|
79
|
+
isbn_13 >> Return 13 digit ISBN numbers as string
|
80
|
+
categories >> Return category names as comma separated as string
|
81
|
+
categories_as_array >> Return category names as ruby array. If no book is associated with ISBN number,
|
82
|
+
then it return empty array
|
83
|
+
thumbnail_small >> Return the link of small thumnail
|
84
|
+
thumbnail >> Return the link of standard thumnail
|
85
|
+
preview_link >> Return the link for previewing the book
|
86
|
+
page_count >> Return page count as integer
|
87
|
+
published_date >> Return the published date as Ruby object
|
88
|
+
|
89
|
+
|
90
|
+
### Development
|
91
|
+
|
92
|
+
Want to contribute? Great!
|
93
|
+
|
94
|
+
1. Fork it ( https://github.com/eftakhairul/gisbn/fork )
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create a new Pull Request
|
99
|
+
|
100
|
+
|
101
|
+
Author
|
102
|
+
-----------
|
103
|
+
[Eftakhairul Islam](https://eftakhairul.com)
|
104
|
+
|
105
|
+
Contributors
|
106
|
+
-----------
|
107
|
+
[Matt Seeberger](https://github.com/thebeardedgeek)
|
108
|
+
|
109
|
+
License
|
110
|
+
----
|
111
|
+
MIT
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
data/Rakefile
ADDED
data/alexandrite.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/alexandrite/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'alexandrite'
|
7
|
+
spec.version = Alexandrite::VERSION
|
8
|
+
spec.authors = ['J. P. Pérez-Tejada']
|
9
|
+
spec.email = ['jpereztejada@apexsystems.com', 'juan@daiuk.com']
|
10
|
+
spec.summary = 'Tools for fetching books data from Google and OCLC'
|
11
|
+
spec.description = "Alexandrite is a gem that fetches public book's information by different params based
|
12
|
+
on Google Book API and OCLC API . Powered by Faraday."
|
13
|
+
spec.homepage = 'https://github.com/maclenn77/alexandrite'
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.0')
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '>= 2.1.0'
|
23
|
+
spec.add_development_dependency 'faraday'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
|
26
|
+
# #dependency for testing automation
|
27
|
+
spec.add_development_dependency 'factory_bot'
|
28
|
+
spec.add_development_dependency 'pry-byebug'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'rspec-nc'
|
31
|
+
spec.add_development_dependency 'vcr'
|
32
|
+
spec.add_development_dependency 'webmock'
|
33
|
+
end
|
data/lib/alexandrite.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'alexandrite_books'
|
4
|
+
require_relative 'alexandrite_google'
|
5
|
+
require_relative 'alexandrite_oclc'
|
6
|
+
require_relative 'error_type/errors'
|
7
|
+
|
8
|
+
# Main gem methods for creating books from ISBN
|
9
|
+
module Alexandrite
|
10
|
+
include ErrorType
|
11
|
+
|
12
|
+
# Get and Process data from Google API
|
13
|
+
class Google
|
14
|
+
extend Alexandrite::GoogleAPI
|
15
|
+
|
16
|
+
attr_reader :result
|
17
|
+
|
18
|
+
def initialize(key, query)
|
19
|
+
@result = search_by(key, query)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Alexandrite::Book]
|
23
|
+
def self.create_book(isbn)
|
24
|
+
volume_info = get_volume_info(isbn)
|
25
|
+
Alexandrite::Book.new(volume_info)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get and Process data from OCLC API
|
30
|
+
class OCLC
|
31
|
+
extend Alexandrite::OCLCAPI
|
32
|
+
|
33
|
+
attr_reader :result
|
34
|
+
|
35
|
+
# return [Alexandrite::OCLC]
|
36
|
+
def initialize(key, query, api: :oclc)
|
37
|
+
@result = search_by(key, query, api: api)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Alexandrite::Book]
|
41
|
+
def self.create_book(type, identifier)
|
42
|
+
query = Alexandrite::OCLC.new(type, identifier)
|
43
|
+
response_code = get_response_code(query.result)
|
44
|
+
|
45
|
+
response(response_code, query)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
API = {
|
50
|
+
google: Alexandrite::Google,
|
51
|
+
oclc: Alexandrite::OCLC
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
def search_by(key, query, api: :google)
|
55
|
+
API[api].search_by(key, query)
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_book(key, query)
|
59
|
+
book = API[:google].create_book(query)
|
60
|
+
return API[:oclc].create_book(key, query) if book.error_message
|
61
|
+
|
62
|
+
book
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param isbns [Array]
|
66
|
+
# @return [Nil]
|
67
|
+
def bulk_create(isbns)
|
68
|
+
isbns.each do |isbn|
|
69
|
+
volume_info = volume_info(isbn)
|
70
|
+
book = Alexandrite::Book.new(volume_info)
|
71
|
+
add_to_collection(book)
|
72
|
+
end
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def get_volume_info(isbn)
|
79
|
+
query = search_by(:isbn, isbn)
|
80
|
+
return query[:books].first[:volume_info] unless query[:error_message]
|
81
|
+
|
82
|
+
query
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'alexandrite'
|
4
|
+
|
5
|
+
module Alexandrite
|
6
|
+
# Create a book objects
|
7
|
+
class Book
|
8
|
+
attr_accessor :title, :authors, :publisher, :published_date, :page_count, :description, :categories, :language,
|
9
|
+
:country, :isbn10, :isbn13, :error_message, :ddc, :lcc, :suggested_classifications
|
10
|
+
|
11
|
+
# @param volume_info [Hash]
|
12
|
+
# @return [Alexandrite::Books]
|
13
|
+
def initialize(volume_info)
|
14
|
+
add_origin(volume_info)
|
15
|
+
add_basic_values(volume_info)
|
16
|
+
add_industry_identifiers(volume_info, @origin)
|
17
|
+
@error_message = volume_info[:error_message]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.collection = @@collection
|
21
|
+
|
22
|
+
def self.add_to_collection(book)
|
23
|
+
@@collection.push(book)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [String] ddc_code
|
27
|
+
# @return [NilClass]
|
28
|
+
def add_ddc(ddc_code) = @ddc = ddc_code
|
29
|
+
|
30
|
+
# @param [String] lcc_code
|
31
|
+
# @return [NilClass]
|
32
|
+
def add_lcc(lcc_code) = @lcc = lcc_code
|
33
|
+
|
34
|
+
# @param [Array<String>] suggestions
|
35
|
+
# @return [NilClass]
|
36
|
+
def add_classification_suggested(suggestions) = @suggested_classifications = suggestions
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def add_basic_values(volume_info)
|
41
|
+
@title = volume_info['title']
|
42
|
+
@authors = volume_info['authors']
|
43
|
+
@publisher = volume_info['publisher']
|
44
|
+
@published_date = volume_info['publishedDate']
|
45
|
+
@page_count = volume_info['pageCount']
|
46
|
+
@description = volume_info['description']
|
47
|
+
@categories = volume_info['categories']
|
48
|
+
@language = volume_info['language']
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param [Hash] volume_info
|
52
|
+
# @return [String]
|
53
|
+
def add_origin(volume_info) = @origin = volume_info['origin'] || 'Google API'
|
54
|
+
|
55
|
+
# @param [Hash] volume_info
|
56
|
+
# @param [String] origin
|
57
|
+
# @return [NilClass]
|
58
|
+
def add_industry_identifiers(volume_info, origin)
|
59
|
+
return nil unless volume_info[:error_message].nil?
|
60
|
+
|
61
|
+
if origin == 'OCLC API'
|
62
|
+
@isbn10 = volume_info['ISBN_10']
|
63
|
+
@isbn13 = volume_info['ISBN_13']
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
volume_info['industryIdentifiers'].each do |identifier|
|
68
|
+
@isbn10 = identifier['identifier'] if identifier['type'] == 'ISBN_10'
|
69
|
+
@isbn13 = identifier['identifier'] if identifier['type'] == 'ISBN_13'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'alexandrite'
|
4
|
+
require_relative 'book'
|
5
|
+
|
6
|
+
# Create, Read, Update and Delete books in a book collection
|
7
|
+
module Alexandrite
|
8
|
+
# Create a collection of books
|
9
|
+
class Bookshelf
|
10
|
+
def initialize
|
11
|
+
@book_collection = []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
def add_book(book) = @book_collection << book if book.instance_of?(Alexandrite::Book)
|
15
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require all files
|
4
|
+
require_relative 'alexandrite/version'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'faraday'
|
7
|
+
require 'json'
|
8
|
+
require_relative 'helpers/format'
|
9
|
+
require_relative 'helpers/validation'
|
10
|
+
require 'pry'
|
11
|
+
|
12
|
+
module Alexandrite
|
13
|
+
# Methods for consuming Google API
|
14
|
+
module GoogleAPI
|
15
|
+
include Alexandrite::Helpers::Format
|
16
|
+
include Alexandrite::Helpers::Validation
|
17
|
+
|
18
|
+
attr_reader :result, :isbn
|
19
|
+
|
20
|
+
BASE_URL = 'https://www.googleapis.com/books/v1/volumes?q='
|
21
|
+
|
22
|
+
FILTERS = {
|
23
|
+
author: 'inauthor:',
|
24
|
+
isbn: 'isbn:',
|
25
|
+
lccn: 'lccn:',
|
26
|
+
oclc: 'oclc:',
|
27
|
+
publisher: 'inpublisher:',
|
28
|
+
subject: 'subject:',
|
29
|
+
title: 'intitle:'
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
FIELDS = {
|
33
|
+
title: 'volumeInfo/title',
|
34
|
+
authors: 'volumeInfo/authors'
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
# @param query [String]
|
38
|
+
# @return [Faraday::Response]
|
39
|
+
def search(query, projection: 'lite')
|
40
|
+
conn = Faraday.new("#{BASE_URL}#{query}&projection=#{projection}") do |r|
|
41
|
+
r.response :json
|
42
|
+
end
|
43
|
+
|
44
|
+
conn.get.body
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param key [Symbol]
|
48
|
+
# @param query [String]
|
49
|
+
# @param fields [Array<String>]
|
50
|
+
# @return [Faraday::Response]
|
51
|
+
def search_by(key, query, projection: 'lite', **fields)
|
52
|
+
projection = "&projection=#{projection}"
|
53
|
+
url = fields.any? ? "#{BASE_URL}#{FILTERS[key]}#{query}#{add_fields(fields)}#{projection}" : "#{BASE_URL}#{FILTERS[key]}#{query}"
|
54
|
+
conn = Faraday.new(url) do |r|
|
55
|
+
r.response :json
|
56
|
+
end
|
57
|
+
|
58
|
+
response = conn.get.body
|
59
|
+
|
60
|
+
process_response(response)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def zero_results?(response_body) = response_body['totalItems'].zero?
|
66
|
+
|
67
|
+
def process_response(response)
|
68
|
+
if zero_results?(response)
|
69
|
+
{ error_message: 'Not results' }
|
70
|
+
else
|
71
|
+
{ results_size: response['totalItems'],
|
72
|
+
books: response['items'] }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require all files
|
4
|
+
require_relative 'alexandrite/version'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'faraday'
|
7
|
+
require 'faraday/decode_xml'
|
8
|
+
require 'json'
|
9
|
+
require_relative 'helpers/format'
|
10
|
+
require_relative 'helpers/validation'
|
11
|
+
require 'pry'
|
12
|
+
require 'nokogiri'
|
13
|
+
|
14
|
+
module Alexandrite
|
15
|
+
# Consume OCLC API
|
16
|
+
module OCLCAPI
|
17
|
+
include Nokogiri
|
18
|
+
include Alexandrite::Helpers::Format
|
19
|
+
include Alexandrite::Helpers::Validation
|
20
|
+
|
21
|
+
BASE_URL = 'http://classify.oclc.org/classify2/Classify?'
|
22
|
+
|
23
|
+
# @param query [String]
|
24
|
+
# @return [Faraday::Response]
|
25
|
+
def search_by(key, query, summary: true)
|
26
|
+
search = "#{BASE_URL}#{key}=#{query}&summary=#{summary}"
|
27
|
+
conn = Faraday.new(search) do |r|
|
28
|
+
r.get(search).body
|
29
|
+
end
|
30
|
+
Nokogiri::XML(conn.get(search).body)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_book_data(result)
|
34
|
+
isbn = result.css('input').children.text
|
35
|
+
isbn_type = define_isbn_type(isbn)
|
36
|
+
{
|
37
|
+
'origin' => 'OCLC API',
|
38
|
+
'title' => result.css('work').attribute('title').value,
|
39
|
+
'authors' => result.css('work').attribute('author').value,
|
40
|
+
'ddc' => result.css('mostPopular').attribute('nsfa').value,
|
41
|
+
isbn_type => isbn
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def recommend_classification(key, query, length = 5, mode: 'ddc')
|
46
|
+
results = search_by(key, query)
|
47
|
+
|
48
|
+
response_cases(results, length, mode)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def get_response_code(result) = result.css('response').attribute('code').value.to_i
|
54
|
+
|
55
|
+
def get_owis(result, length)
|
56
|
+
owis = []
|
57
|
+
result.css('work').map { |work| owis << work.attribute('owi').value }
|
58
|
+
|
59
|
+
owis[0...length]
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_recommendations(owis, mode)
|
63
|
+
results = []
|
64
|
+
classifications_recommended = []
|
65
|
+
|
66
|
+
owis.map { |owi_value| results << search_by('owi', owi_value) }
|
67
|
+
|
68
|
+
results.each do |classification|
|
69
|
+
classifications_recommended << get_classification(classification, mode)
|
70
|
+
end
|
71
|
+
|
72
|
+
classifications_recommended
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_classification(result, mode)
|
76
|
+
acronym = { 'ddc': 'Dewey Decimal Classification',
|
77
|
+
'lcc': 'Library of Congress Classification' }
|
78
|
+
|
79
|
+
result.css(mode).css('mostPopular').attribute('nsfa').value
|
80
|
+
rescue NoMethodError
|
81
|
+
"A #{acronym[mode.to_sym]} is not available for this book"
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_new_book(query)
|
85
|
+
volume_info = get_book_data(query.result)
|
86
|
+
Alexandrite::Book.new(volume_info)
|
87
|
+
end
|
88
|
+
|
89
|
+
def retry_create_new_book(query)
|
90
|
+
owi = get_owis(query.result, 1)[0]
|
91
|
+
new_query = Alexandrite::OCLC.new('owi', owi)
|
92
|
+
volume_info = get_book_data(new_query.result)
|
93
|
+
Alexandrite::Book.new(volume_info)
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_error_creating_book(query)
|
97
|
+
raise ErrorType::DataNotFound, response_cases(query, 0, 'none')
|
98
|
+
rescue StandardError => e
|
99
|
+
Alexandrite::Book.new('error_message' => e.message)
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param [Nokogiri::XML] result
|
103
|
+
# @param [Integer] length
|
104
|
+
# @param [String] mode Possible: 'ddc' or 'lcc'
|
105
|
+
# @return [String]
|
106
|
+
def response_cases(result, length, mode)
|
107
|
+
code = get_response_code(result).to_i
|
108
|
+
case code
|
109
|
+
when 0
|
110
|
+
get_classification(result, mode)
|
111
|
+
when 2
|
112
|
+
get_classification(result, mode)
|
113
|
+
when 4
|
114
|
+
get_recommendations(get_owis(result, length), mode)
|
115
|
+
when 100
|
116
|
+
'No input. The method requires an input argument.'
|
117
|
+
when 101
|
118
|
+
'Invalid input. The standard number argument is invalid.'
|
119
|
+
when 102
|
120
|
+
'Not found. No data found for the input argument.'
|
121
|
+
when 200
|
122
|
+
'Unexpected error.'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def response(response_code, query)
|
127
|
+
case response_code
|
128
|
+
when 0, 2
|
129
|
+
create_new_book(query)
|
130
|
+
when 4
|
131
|
+
retry_create_new_book(query)
|
132
|
+
else
|
133
|
+
handle_error_creating_book(query)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alexandrite
|
4
|
+
module Helpers
|
5
|
+
# Format inputs and options
|
6
|
+
module Format
|
7
|
+
# @param string [String]
|
8
|
+
# @return [String]
|
9
|
+
def remove_non_digits(string)
|
10
|
+
string.gsub!(/[^+\d]/, '')
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_fields(*fields)
|
14
|
+
"&fields=items(volumeInfo/#{fields.join(',volumeInfo/')})"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alexandrite
|
4
|
+
module Helpers
|
5
|
+
# Validate inputs
|
6
|
+
module Validation
|
7
|
+
# @param
|
8
|
+
# @return
|
9
|
+
def valid_isbn_length?(isbn) = isbn.size == 10 || isbn.size == 13
|
10
|
+
|
11
|
+
# @param
|
12
|
+
# @return [String] or [Nil]
|
13
|
+
def validate_isbn(isbn)
|
14
|
+
remove_non_digits(isbn.to_s)
|
15
|
+
return isbn if valid_isbn_length?(isbn)
|
16
|
+
|
17
|
+
'ISBN should have 10 or 13 digits'
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_isbn_type(isbn) = "ISBN_#{isbn.length}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'book'
|
5
|
+
|
6
|
+
RSpec.describe Alexandrite::Book do
|
7
|
+
subject { Alexandrite::Book.new('0262033844') }
|
8
|
+
|
9
|
+
describe 'alexandrite_base' do
|
10
|
+
describe '#process' do
|
11
|
+
let(:expected_title) { 'Introduction to Algorithms, Third Edition' }
|
12
|
+
let(:expected_pages) { 1314 }
|
13
|
+
let(:result) { subject.fetch }
|
14
|
+
|
15
|
+
VCR.use_cassette('get_book') do
|
16
|
+
it 'book title' do
|
17
|
+
expect(result.title.downcase).to eq(expected_title.downcase)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'book page count' do
|
21
|
+
expect(result.page_count.to_i).to eq(expected_pages)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
describe 'validate ISBN format' do
|
7
|
+
let(:book_class) { Class.new }
|
8
|
+
|
9
|
+
describe 'valid ISBNs' do
|
10
|
+
let(:valid_isbn10) { '0123456789' }
|
11
|
+
let(:valid_isbn13) { '1234567890123' }
|
12
|
+
let(:valid_isbn_non_digit) { '1-23456789012-3' }
|
13
|
+
|
14
|
+
it 'valid ISBN of 13 digits' do
|
15
|
+
isbn = valid_isbn13
|
16
|
+
result = book_class.validate_isbn(isbn)
|
17
|
+
expect(result).to eq(valid_isbn13)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'valid ISBN of 10 digits' do
|
21
|
+
result = book_class.validate_isbn(valid_isbn10)
|
22
|
+
expect(result).to eq(valid_isbn10)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'valid ISBN with dashes' do
|
26
|
+
result = book_class.validate_isbn(valid_isbn_non_digit)
|
27
|
+
expect(result).to eq(valid_isbn13)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'invalid ISBNs' do
|
32
|
+
let(:error_message) { 'ISBN should have 10 or 13 digits' }
|
33
|
+
let(:valid_isbn_white_spaces) { '1234567890123 ' }
|
34
|
+
let(:invalid_isbn_non_digit) { 'ABCDEFGHIJ ' }
|
35
|
+
let(:invalid_isbn_too_short) { '1234' }
|
36
|
+
let(:invalid_isbn_too_long) { '123456789123456789' }
|
37
|
+
|
38
|
+
it 'ISBN without digits is invalid' do
|
39
|
+
result = book_class.validate_isbn(invalid_isbn_non_digit)
|
40
|
+
expect(result).to eq(error_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'ISBN is invalid because is too short' do
|
44
|
+
result = book_class.validate_isbn(invalid_isbn_too_short)
|
45
|
+
expect(result).to eq(error_message)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'ISBN is invalid because is too long' do
|
49
|
+
result = book_class.validate_isbn(invalid_isbn_too_short)
|
50
|
+
expect(result).to eq(error_message)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# frozen_string_literal: tru
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'net/http'
|
6
|
+
require 'json'
|
7
|
+
require 'factory_bot'
|
8
|
+
require 'vcr'
|
9
|
+
require 'pry-byebug'
|
10
|
+
require 'helpers/format'
|
11
|
+
require 'support/vcr_setup'
|
12
|
+
require 'alexandrite'
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.include FactoryBot::Syntax::Methods
|
16
|
+
|
17
|
+
config.before(:suite) do
|
18
|
+
FactoryBot.find_definitions
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'vcr'
|
4
|
+
|
5
|
+
VCR.configure do |c|
|
6
|
+
c.configure_rspec_metadata!
|
7
|
+
c.cassette_library_dir = 'vcr/cassettes'
|
8
|
+
c.hook_into :webmock
|
9
|
+
c.default_cassette_options = {
|
10
|
+
erb: true,
|
11
|
+
record: :once,
|
12
|
+
decode_compressed_response: false,
|
13
|
+
match_requests_on: %i[method uri body]
|
14
|
+
}
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alexandrite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- J. P. Pérez-Tejada
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.1.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_bot
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-nc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: vcr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: |-
|
140
|
+
Alexandrite is a gem that fetches public book's information by different params based
|
141
|
+
on Google Book API and OCLC API . Powered by Faraday.
|
142
|
+
email:
|
143
|
+
- jpereztejada@apexsystems.com
|
144
|
+
- juan@daiuk.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- ".gitignore"
|
150
|
+
- Gemfile
|
151
|
+
- LICENSE
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- alexandrite.gemspec
|
155
|
+
- lib/alexandrite.rb
|
156
|
+
- lib/alexandrite/version.rb
|
157
|
+
- lib/alexandrite_books.rb
|
158
|
+
- lib/alexandrite_bookshelf.rb
|
159
|
+
- lib/alexandrite_google.rb
|
160
|
+
- lib/alexandrite_oclc.rb
|
161
|
+
- lib/error_type/errors.rb
|
162
|
+
- lib/helpers/format.rb
|
163
|
+
- lib/helpers/validation.rb
|
164
|
+
- spec/alexandrite_spec.rb
|
165
|
+
- spec/factories/book.rb
|
166
|
+
- spec/helpers/format_spec.rb
|
167
|
+
- spec/spec_helper.rb
|
168
|
+
- spec/support/vcr_setup.rb
|
169
|
+
homepage: https://github.com/maclenn77/alexandrite
|
170
|
+
licenses:
|
171
|
+
- MIT
|
172
|
+
metadata: {}
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '3.0'
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubygems_version: 3.3.3
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: Tools for fetching books data from Google and OCLC
|
192
|
+
test_files:
|
193
|
+
- spec/alexandrite_spec.rb
|
194
|
+
- spec/factories/book.rb
|
195
|
+
- spec/helpers/format_spec.rb
|
196
|
+
- spec/spec_helper.rb
|
197
|
+
- spec/support/vcr_setup.rb
|