isbndb 1.5.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.rvmrc +48 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -14
- data/Gemfile.lock +41 -21
- data/README.markdown +118 -102
- data/Rakefile +6 -46
- data/config/isbndb.example.yml +3 -0
- data/isbndb.gemspec +19 -73
- data/lib/core_extensions/nil.rb +5 -0
- data/lib/core_extensions/string.rb +49 -0
- data/lib/isbndb.rb +9 -113
- data/lib/isbndb/access_key_set.rb +31 -20
- data/lib/isbndb/exceptions.rb +1 -2
- data/lib/isbndb/query.rb +78 -0
- data/lib/isbndb/result.rb +38 -39
- data/lib/isbndb/result_set.rb +32 -33
- data/spec/responses/access_key_error.xml +5 -0
- data/spec/responses/authors_seth.xml +46 -0
- data/spec/responses/books_hello.xml +76 -0
- data/spec/responses/categories_fiction.xml +46 -0
- data/spec/responses/keystats.xml +6 -0
- data/spec/responses/publishers_francis.xml +46 -0
- data/spec/responses/search.xml +76 -0
- data/spec/responses/subjects_ruby.xml +36 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/helpers.rb +8 -0
- data/spec/units/access_key_set_spec.rb +98 -0
- data/spec/units/nil_spec.rb +9 -0
- data/spec/units/query_spec.rb +179 -0
- data/spec/units/result_set_spec.rb +89 -0
- data/spec/units/result_spec.rb +105 -0
- data/spec/units/string_spec.rb +118 -0
- metadata +110 -81
- data/.document +0 -5
- data/ACKNOWLEDGEMENTS +0 -3
- data/LICENSE.txt +0 -20
- data/VERSION +0 -1
- data/test/helper.rb +0 -23
- data/test/test_isbndb.rb +0 -77
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p194@isbndb"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.13.6 (master)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
# if [[ -s Gemfile ]] && {
|
38
|
+
# ! builtin command -v bundle >/dev/null ||
|
39
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
# }
|
41
|
+
# then
|
42
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
# gem install bundler
|
44
|
+
# fi
|
45
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
# then
|
47
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
48
|
+
# fi
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,15 +1,3 @@
|
|
1
|
-
source
|
1
|
+
source :rubygems
|
2
2
|
|
3
|
-
|
4
|
-
gem 'i18n'
|
5
|
-
gem 'rake'
|
6
|
-
gem 'rdoc'
|
7
|
-
gem 'libxml-ruby', '>= 1.1.4'
|
8
|
-
|
9
|
-
# Include everything needed to run rake, tests, features, etc.
|
10
|
-
group :development do
|
11
|
-
gem 'shoulda', '>= 0'
|
12
|
-
gem 'bundler'
|
13
|
-
gem 'jeweler'
|
14
|
-
gem 'rcov', '>= 0'
|
15
|
-
end
|
3
|
+
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
isbndb (2.0.0)
|
5
|
+
httparty (~> 0.8.3)
|
6
|
+
rake (~> 0.9.2.2)
|
7
|
+
|
1
8
|
GEM
|
2
9
|
remote: http://rubygems.org/
|
3
10
|
specs:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rake (0.9.2)
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
addressable (2.2.8)
|
12
|
+
crack (0.3.1)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
httparty (0.8.3)
|
15
|
+
multi_json (~> 1.0)
|
16
|
+
multi_xml
|
17
|
+
multi_json (1.3.6)
|
18
|
+
multi_xml (0.5.1)
|
19
|
+
rake (0.9.2.2)
|
20
|
+
rspec (2.10.0)
|
21
|
+
rspec-core (~> 2.10.0)
|
22
|
+
rspec-expectations (~> 2.10.0)
|
23
|
+
rspec-mocks (~> 2.10.0)
|
24
|
+
rspec-core (2.10.1)
|
25
|
+
rspec-expectations (2.10.0)
|
26
|
+
diff-lcs (~> 1.1.3)
|
27
|
+
rspec-mocks (2.10.1)
|
28
|
+
shoulda (3.0.1)
|
29
|
+
shoulda-context (~> 1.0.0)
|
30
|
+
shoulda-matchers (~> 1.0.0)
|
31
|
+
shoulda-context (1.0.0)
|
32
|
+
shoulda-matchers (1.0.0)
|
33
|
+
simplecov (0.6.4)
|
34
|
+
multi_json (~> 1.0)
|
35
|
+
simplecov-html (~> 0.5.3)
|
36
|
+
simplecov-html (0.5.3)
|
37
|
+
webmock (1.8.7)
|
38
|
+
addressable (>= 2.2.7)
|
39
|
+
crack (>= 0.1.7)
|
16
40
|
|
17
41
|
PLATFORMS
|
18
42
|
ruby
|
19
43
|
|
20
44
|
DEPENDENCIES
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
rake
|
27
|
-
rcov
|
28
|
-
rdoc
|
29
|
-
shoulda
|
45
|
+
isbndb!
|
46
|
+
rspec (~> 2.10.0)
|
47
|
+
shoulda (~> 3.0.1)
|
48
|
+
simplecov (~> 0.6.4)
|
49
|
+
webmock (~> 1.8.7)
|
data/README.markdown
CHANGED
@@ -1,79 +1,90 @@
|
|
1
1
|
Ruby ISBNdb
|
2
2
|
===========
|
3
|
-
About
|
4
|
-
-----
|
5
3
|
Ruby ISBNdb is a simple, Ruby library that connects to [ISBNdb.com's Web Service](http://isbndb.com) and API. Ruby ISBNdb is written to mimic the ease of ActiveRecord and other ORM programs, without all the added hassles. It's still in beta phases, but it is almost fully functional for the basic search features of ISBNdb.
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Instead of dealing with complicated hashes and arrays, Ruby ISBNdb populates a `ResultSet` filled with `Result` objects that behave as one would expect. Simply call `@book.title` or `@author.name`! Once a `Result` object is built, it's persistent too! That means that the XML-DOM returned by ISBNdb is parsed exactly once for each request, instead of every method call like similar versions of this gem.
|
12
|
-
|
13
|
-
Version 1.5.0 now supports API-key management! The new APIKeySet supports auto-rollover - whenever one key is used up, it will automatically try the next key in the set. Once it runs out of keys, it will raise an ISBNdb::AccessKeyError. See the docs below for sample usage!
|
14
|
-
|
15
|
-
Ruby ISBNdb is under active development! More features will be coming soon!
|
5
|
+
Version 1.x
|
6
|
+
-----------
|
7
|
+
*ISBNdb 1.x.x has been deprecated!*. You should upgrade to the new version as soon as possible. The old documentation is still available in the [git history](https://github.com/sethvargo/isbndb/tree/75cfe76d096f92b2dfaf1c1b42d7c84ff86fcbc0). There are *significant* changes in this new version, so please test appropriately.
|
16
8
|
|
17
9
|
Installation
|
18
10
|
------------
|
19
|
-
|
11
|
+
To get started, install the gem:
|
20
12
|
|
21
13
|
gem install isbndb
|
22
14
|
|
23
|
-
Alternatively, you can
|
24
|
-
|
25
|
-
**Special Thanks** to:
|
15
|
+
Alternatively, you can add it to your Gemfile:
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
for giving up the `isbndb` gem! Thank you!
|
17
|
+
```ruby
|
18
|
+
gem 'isbndb', '~> 2.0.0'
|
19
|
+
```
|
31
20
|
|
32
21
|
Basic Setup
|
33
22
|
-----------
|
34
|
-
|
23
|
+
To get started, you'll need to create a `config/isbndb.yml` file in your project root. It should look like this:
|
24
|
+
|
25
|
+
```yml
|
26
|
+
access_keys:
|
27
|
+
- KEY_1
|
28
|
+
- KEY_2
|
29
|
+
...
|
30
|
+
```
|
35
31
|
|
36
|
-
|
37
|
-
|
32
|
+
Where you list your access keys. This was in response to security holes in version 1.x where values were passed directly to the initializer.
|
33
|
+
|
34
|
+
Now you're ready to get started:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
@query = ISBNdb::Query.find_book_by_name('Ruby')
|
38
|
+
```
|
38
39
|
|
39
40
|
ActiveRecord-like Usage
|
40
41
|
-----------------------
|
41
42
|
Another reason why you'll love Ruby ISBNdb is it's similarity to ActiveRecord. In fact, it's *based* on ActiveRecord, so it should look similar. It's best to lead by example, so here are a few ways to search for books, authors, etc:
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
```ruby
|
45
|
+
ISBNdb::Query.find_book_by_isbn("978-0-9776-1663-3")
|
46
|
+
ISBNdb::Query.find_books_by_title("Agile Development")
|
47
|
+
ISBNdb::Query.find_author_by_name("Seth Vargo")
|
48
|
+
ISBNdb::Query.find_publisher_by_name("Pearson")
|
49
|
+
```
|
47
50
|
|
48
51
|
Advanced Usage
|
49
52
|
--------------
|
50
53
|
Additionally, you can also use a more advanced syntax for complete control:
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
```ruby
|
56
|
+
ISBNdb::Query.find(:collection => 'books', :where => { :isbn => '978-0-9776-1663-3' })
|
57
|
+
ISBNdb::Query.find(:collection => 'books', :where => { :author => 'Seth Vargo' }, :results => 'prices')
|
58
|
+
```
|
59
|
+
|
55
60
|
Options for `:collection` include **books**, **subjects**, **categories**, **authors**, and **publishers**.
|
56
|
-
|
61
|
+
|
57
62
|
If you are unfamiliar with some of these options, have a look at the [ISBNdb API](http://isbndb.com/docs/api/)
|
58
63
|
|
59
64
|
Processing Results
|
60
65
|
------------------
|
61
66
|
A `ResultSet` is nothing more than an enhanced array of `Result` objects. The easiest way to process results from Ruby ISBNdb is most easily done using the `.each` method.
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
```ruby
|
69
|
+
results = ISBNdb::Query.find_books_by_title("Agile Development")
|
70
|
+
results.each do |result|
|
71
|
+
puts "title: #{result.title}"
|
72
|
+
puts "isbn10: #{result.isbn}"
|
73
|
+
puts "authors: #{result.authors_text}"
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
70
77
|
**Note**: calling a method on a `Result` object that is `empty?`, `blank?`, or `nil?` will *always* return `nil`. This was a calculated decision so that developers can do the following:
|
71
78
|
|
72
|
-
|
73
|
-
|
79
|
+
```ruby
|
80
|
+
puts "title: #{result.title}" unless result.title.nil?
|
81
|
+
```
|
82
|
+
|
74
83
|
versus
|
75
84
|
|
76
|
-
|
85
|
+
```ruby
|
86
|
+
puts "title: #{result.title}" unless result.title.nil? || result.title.blank? || result.title.empty?
|
87
|
+
```
|
77
88
|
|
78
89
|
because ISBNdb.com API is generally inconsistent with respect to returning empty strings, whitespace characters, or nothing at all.
|
79
90
|
|
@@ -81,30 +92,36 @@ because ISBNdb.com API is generally inconsistent with respect to returning empty
|
|
81
92
|
|
82
93
|
Pagination
|
83
94
|
----------
|
84
|
-
|
95
|
+
Pagination is based on the `ResultSet` object. The `ResultSet` object contains the methods `go_to_page`, `next_page`, and `prev_page`... Their function should not require too much explanation. Here's a basic example:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
results = ISBNdb::Query.find_books_by_title("ruby")
|
99
|
+
results.next_page.each do |result|
|
100
|
+
puts "title: #{result.title}"
|
101
|
+
end
|
102
|
+
```
|
85
103
|
|
86
|
-
results = @query.find_books_by_title("ruby")
|
87
|
-
results.next_page.each do |result|
|
88
|
-
puts "title: #{result.title}"
|
89
|
-
end
|
90
|
-
|
91
104
|
A more realistic example - getting **all** books of a certain title:
|
92
105
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
106
|
+
```ruby
|
107
|
+
results = ISBNdb::Query.find_books_by_title("ruby")
|
108
|
+
while results
|
109
|
+
results.each do |result|
|
110
|
+
puts "title: #{title}"
|
111
|
+
end
|
112
|
+
|
113
|
+
results = results.next_page
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
102
117
|
It seems incredibly unlikely that a developer would ever use `prev_page`, but it's still there if you need it.
|
103
118
|
|
104
119
|
Because there may be cases where a developer may need a specific page, the `go_to_page` method also exists. Consider an example where you batch-process books into your own database (which is probably against Copyright laws, but you don't seem to care...):
|
105
120
|
|
106
|
-
|
107
|
-
|
121
|
+
```ruby
|
122
|
+
results = ISBNdb::Query.find_books_by_title("ruby")
|
123
|
+
results = results.go_to_page(50) # where 50 is the page number you want
|
124
|
+
```
|
108
125
|
|
109
126
|
**Note**: `go_to_page`, `next_page` and `prev_page` return `nil` if the `ResultSet` is out of `Result` objects. If you try something like `results.next_page.next_page`, you could get a whiny nil. Think `LinkedLists` when working with `go_to_page`, `next_page` and `prev_page`.
|
110
127
|
|
@@ -112,73 +129,72 @@ Because there may be cases where a developer may need a specific page, the `go_t
|
|
112
129
|
|
113
130
|
Advanced Key Management
|
114
131
|
-----------------------
|
115
|
-
|
116
|
-
|
117
|
-
@access_key_set = @query.access_key_set
|
118
|
-
|
119
|
-
@access_key_set.current_key # gets the current key
|
120
|
-
@access_key_set.next_key # gets the next key
|
121
|
-
@access_key_set.next_key! # advance the pointer (equivalent to @access_key_set.current_key = @access_key_set.next_key)
|
122
|
-
@access_key_set.prev_key # gets the previous key
|
123
|
-
@access_key_set.prev_key! # advance the pointer (equivalent to @access_key_set.current_key = @access_key_set.prev_key)
|
124
|
-
@access_key_set.use_key('abc123foobar') # use and existing key (or add it if doesn't exist)
|
125
|
-
|
126
|
-
All methods will return `nil` (except `use_key`) whenever the key does not exist.
|
132
|
+
As over version 2.0, all access key management has moved into the `config/isbndb.yml` file. ISBNdb will auto-rollover if you specify multiple keys.
|
127
133
|
|
128
134
|
Statistics
|
129
135
|
----------
|
130
136
|
Ruby ISBNdb now supports basic statistics (from the server):
|
131
137
|
|
132
|
-
|
133
|
-
|
134
|
-
|
138
|
+
```ruby
|
139
|
+
ISBNdb::Query.keystats # => {:requests => 50, :granted => 49}
|
140
|
+
ISBNdb::Query.keystats[:granted] # => 49
|
141
|
+
```
|
142
|
+
|
135
143
|
**Note**: Ironically, this information also comes from the server, so it counts as a request...
|
136
144
|
|
137
145
|
Exceptions
|
138
146
|
----------
|
139
147
|
Ruby ISBNdb could raise the following possible exceptions:
|
140
148
|
|
141
|
-
|
142
|
-
|
143
|
-
|
149
|
+
```ruby
|
150
|
+
ISBNdb::AccessKeyError
|
151
|
+
```
|
152
|
+
|
144
153
|
You will most likely encounter `ISBNdb::AccessKeyError` when you have reached your 500-request daily limit. `ISBNdb::InvalidURIError` usually occurs when using magic finder methods with typographical errors.
|
145
154
|
|
146
155
|
A Real-Life Example
|
147
156
|
-------------------
|
148
157
|
Here is a real-life example of how to use Ruby ISBNdb. Imagine a Rails application that recommends books. You have written a model, `Book`, that has a variety of methods. One of those class methods, `similar`, returns a list of book isbn values that are similar to the current book. Here's how one may do that:
|
149
158
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
159
|
+
```ruby
|
160
|
+
# books_controller.rb
|
161
|
+
def simliar
|
162
|
+
@book = Book.find(params[:id])
|
163
|
+
@query = ISBNdb::Query.new(['API-KEY-1', 'API-KEY-2'])
|
164
|
+
@isbns = @book.similar # returns an array like [1234567890, 0987654321, 3729402827...]
|
165
|
+
|
166
|
+
@isbns.each do |isbn|
|
167
|
+
begin
|
168
|
+
(@books ||= []) << ISBNdb::Query.find_book_by_isbn(isbn).first
|
169
|
+
rescue ISBNdb::AccessKeyError
|
170
|
+
SomeMailer.send_limit_email.deliver!
|
163
171
|
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
# similar.html.erb
|
178
|
+
<h1>The following books are recommeded for you:</h1>
|
179
|
+
<% @books.each do |book| %>
|
180
|
+
<div class="book">
|
181
|
+
<h2><%= book.title_long %></h2>
|
182
|
+
<p><strong>authors</strong>: <%= book.authors_text %></p>
|
183
|
+
</div>
|
184
|
+
<% end %>
|
185
|
+
```
|
164
186
|
|
165
|
-
# similar.html.erb
|
166
|
-
<h1>The following books are recommeded for you:</h1>
|
167
|
-
<% @books.each do |book| %>
|
168
|
-
<div class="book">
|
169
|
-
<h2><%= book.title_long %></h2>
|
170
|
-
<p><strong>authors</strong>: <%= book.authors_text %></p>
|
171
|
-
</div>
|
172
|
-
<% end %>
|
173
|
-
|
174
187
|
Testing
|
175
188
|
-------
|
176
189
|
[![Build Status](http://travis-ci.org/sethvargo/isbndb.png)](http://travis-ci.org/sethvargo/isbndb)
|
177
190
|
|
178
|
-
Know Bugs and Limitations
|
179
|
-
-------------------------
|
180
|
-
- Result sets that return multiple sub-lists (like prices, pricehistory, and authors) are only populated with the *last* result
|
181
|
-
|
182
191
|
Change Log
|
183
192
|
----------
|
184
|
-
|
193
|
+
2012-6-17 - Released v2.0
|
194
|
+
2011-3-11 - Officially changed from ruby_isbndb to isbndb with special thanks to [Terje Tjervaag](https://github.com/terje) for giving up the gem name :)
|
195
|
+
|
196
|
+
Acknowledgments
|
197
|
+
----------------
|
198
|
+
Special thanks to Terje Tjervaag (https://github.com/terje) for giving up the gem name 'isbndb'!
|
199
|
+
|
200
|
+
Special thanks to Lazlo (https://github.com/lazlo) for forwarding his project here!
|