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/Rakefile CHANGED
@@ -1,49 +1,9 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "isbndb"
16
- gem.homepage = "https://github.com/sethvargo/isbndb"
17
- gem.license = "MIT"
18
- gem.summary = "This gem provides an easy solution for connecting to ISBNdb.com's API"
19
- gem.description = "Ruby ISBNdb is a amazingly fast and accurate gem that reads ISBNdb.com's XML API and gives you incredible flexibilty with the results! The gem uses libxml-ruby, the fastest XML parser for Ruby, so you get blazing fast results every time. Additionally, the newest version of the gem also features caching, so developers minimize API-key usage."
20
- gem.email = "seth.vargo@gmail.com"
21
- gem.authors = ["Seth Vargo"]
22
- end
23
- Jeweler::RubygemsDotOrgTasks.new
24
-
25
- require 'rake/testtask'
26
- Rake::TestTask.new(:test) do |test|
27
- test.libs << 'lib' << 'test'
28
- test.pattern = 'test/**/test_*.rb'
29
- test.verbose = true
30
- end
31
-
32
- require 'rcov/rcovtask'
33
- Rcov::RcovTask.new do |test|
34
- test.libs << 'test'
35
- test.pattern = 'test/**/test_*.rb'
36
- test.verbose = true
37
- end
38
-
39
- task :default => :test
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
40
3
 
41
- require 'rdoc/task'
42
- RDoc::Task.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
4
+ RSpec::Core::RakeTask.new(:spec)
44
5
 
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = "ruby_isbndb #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
6
+ namespace :test do
7
+ desc 'Run tests against the CI'
8
+ task :ci => [:spec]
49
9
  end
@@ -0,0 +1,3 @@
1
+ access_keys:
2
+ - ABC123
3
+ - DEF456
@@ -1,79 +1,25 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
2
+ $:.push File.expand_path("../lib", __FILE__)
5
3
 
6
4
  Gem::Specification.new do |s|
7
- s.name = %q{isbndb}
8
- s.version = "1.5.5"
5
+ s.name = 'isbndb'
6
+ s.version = '2.0.0'
7
+ s.author = 'Seth Vargo'
8
+ s.email = 'sethvargo@gmail.com'
9
+ s.homepage = 'https://github.com/sethvargo/isbndb'
10
+ s.summary = 'Connect with ISBNdb.com\'s API'
11
+ s.description = 'Ruby ISBNdb is a amazingly fast and accurate gem that reads ISBNdb.com\'s XML API and gives you incredible flexibilty with the results! The newest version of the gem also features caching, so developers minimize API-key usage.'
9
12
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = [%q{Seth Vargo}]
12
- s.date = %q{2011-06-23}
13
- s.description = %q{Ruby ISBNdb is a amazingly fast and accurate gem that reads ISBNdb.com's XML API and gives you incredible flexibilty with the results! The gem uses libxml-ruby, the fastest XML parser for Ruby, so you get blazing fast results every time. Additionally, the newest version of the gem also features caching, so developers minimize API-key usage.}
14
- s.email = %q{seth.vargo@gmail.com}
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.markdown"
18
- ]
19
- s.files = [
20
- ".document",
21
- "ACKNOWLEDGEMENTS",
22
- "Gemfile",
23
- "Gemfile.lock",
24
- "LICENSE.txt",
25
- "README.markdown",
26
- "Rakefile",
27
- "VERSION",
28
- "isbndb.gemspec",
29
- "lib/isbndb.rb",
30
- "lib/isbndb/access_key_set.rb",
31
- "lib/isbndb/exceptions.rb",
32
- "lib/isbndb/result.rb",
33
- "lib/isbndb/result_set.rb",
34
- "test/helper.rb",
35
- "test/test_isbndb.rb"
36
- ]
37
- s.homepage = %q{https://github.com/sethvargo/isbndb}
38
- s.licenses = [%q{MIT}]
39
- s.require_paths = [%q{lib}]
40
- s.rubygems_version = %q{1.8.5}
41
- s.summary = %q{This gem provides an easy solution for connecting to ISBNdb.com's API}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
42
17
 
43
- if s.respond_to? :specification_version then
44
- s.specification_version = 3
18
+ s.add_development_dependency 'rspec', '~> 2.10.0'
19
+ s.add_development_dependency 'shoulda', '~> 3.0.1'
20
+ s.add_development_dependency 'simplecov', '~> 0.6.4'
21
+ s.add_development_dependency 'webmock', '~> 1.8.7'
45
22
 
46
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
- s.add_runtime_dependency(%q<activesupport>, [">= 0"])
48
- s.add_runtime_dependency(%q<i18n>, [">= 0"])
49
- s.add_runtime_dependency(%q<rake>, [">= 0"])
50
- s.add_runtime_dependency(%q<rdoc>, [">= 0"])
51
- s.add_runtime_dependency(%q<libxml-ruby>, [">= 1.1.4"])
52
- s.add_development_dependency(%q<shoulda>, [">= 0"])
53
- s.add_development_dependency(%q<bundler>, [">= 0"])
54
- s.add_development_dependency(%q<jeweler>, [">= 0"])
55
- s.add_development_dependency(%q<rcov>, [">= 0"])
56
- else
57
- s.add_dependency(%q<activesupport>, [">= 0"])
58
- s.add_dependency(%q<i18n>, [">= 0"])
59
- s.add_dependency(%q<rake>, [">= 0"])
60
- s.add_dependency(%q<rdoc>, [">= 0"])
61
- s.add_dependency(%q<libxml-ruby>, [">= 1.1.4"])
62
- s.add_dependency(%q<shoulda>, [">= 0"])
63
- s.add_dependency(%q<bundler>, [">= 0"])
64
- s.add_dependency(%q<jeweler>, [">= 0"])
65
- s.add_dependency(%q<rcov>, [">= 0"])
66
- end
67
- else
68
- s.add_dependency(%q<activesupport>, [">= 0"])
69
- s.add_dependency(%q<i18n>, [">= 0"])
70
- s.add_dependency(%q<rake>, [">= 0"])
71
- s.add_dependency(%q<rdoc>, [">= 0"])
72
- s.add_dependency(%q<libxml-ruby>, [">= 1.1.4"])
73
- s.add_dependency(%q<shoulda>, [">= 0"])
74
- s.add_dependency(%q<bundler>, [">= 0"])
75
- s.add_dependency(%q<jeweler>, [">= 0"])
76
- s.add_dependency(%q<rcov>, [">= 0"])
77
- end
23
+ s.add_runtime_dependency 'httparty', '~> 0.8.3'
24
+ s.add_runtime_dependency 'rake', '~> 0.9.2.2'
78
25
  end
79
-
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def blank?
3
+ true
4
+ end
5
+ end
@@ -0,0 +1,49 @@
1
+ class String
2
+ require 'date'
3
+
4
+ def is_plural?
5
+ self.downcase.pluralize == self.downcase
6
+ end
7
+
8
+ def is_singular?
9
+ !self.is_plural?
10
+ end
11
+
12
+ def titleize
13
+ str = self[0].upcase + self[1..-1].downcase
14
+ end
15
+
16
+ def singularize
17
+ str = self.dup
18
+ if str[-3..-1] == 'ies'
19
+ str[0..-4] + 'y'
20
+ elsif str[-1] == 's'
21
+ str[0..-2]
22
+ else
23
+ str
24
+ end
25
+ end
26
+
27
+ def pluralize
28
+ str = self.dup
29
+ if str[-1] == 'y'
30
+ str[0..-2] + 'ies'
31
+ elsif str[-1] == 's'
32
+ str
33
+ else
34
+ str + 's'
35
+ end
36
+ end
37
+
38
+ def blank?
39
+ dup.strip.length == 0 ? true : false
40
+ end
41
+
42
+ def underscore
43
+ self.dup.gsub(/::/, '/').
44
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
45
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
46
+ tr("-", "_").
47
+ downcase
48
+ end
49
+ end
@@ -1,116 +1,12 @@
1
- # require dependencies
2
- require 'libxml'
3
- require 'active_support/inflector'
4
-
5
- # private sub-classes
6
- require 'isbndb/access_key_set'
7
- require 'isbndb/exceptions'
8
- require 'isbndb/result_set'
9
- require 'isbndb/result'
10
-
11
1
  module ISBNdb
12
- # The Query object is the most important class of the ISBNdb Module. It is the only public
13
- # class, and handles the processing power.
14
- class Query
15
- DEFAULT_COLLECTION = :books
16
- DEFAULT_RESULTS = :details
17
- BASE_URL = "http://isbndb.com/api"
18
-
19
- # Access methods of the access_key_set instance variable. This allows developers to manually
20
- # advance, add, remove, and manage keys. See the AccessKeySet class for more information.
21
- attr_reader :access_key_set
22
-
23
- # This method sets an array of access_keys to use for making requests to the ISBNdb API.
24
- def initialize(access_keys)
25
- @access_key_set = ISBNdb::AccessKeySet.new(access_keys)
26
- end
27
-
28
- # This is the generic find method. It accepts a hash of parameters including :collection,
29
- # :where clauses, and :results to show. It builds the corresponding URI and sends that URI
30
- # off to the ResultSet for processing.
31
- def find(params = {})
32
- raise "No parameters specified! You must specify at least one parameter!" unless params[:where]
33
-
34
- collection = params[:collection] ||= DEFAULT_COLLECTION
35
- results = params[:results] ||= DEFAULT_RESULTS
36
- results = [results].flatten
37
-
38
- # build the search clause
39
- searches = []
40
- params[:where].each_with_index do |(key,val), i|
41
- searches << "index#{i+1}=#{key.to_s.strip}"
42
- searches << "value#{i+1}=#{val.to_s.strip}"
43
- end
44
-
45
- # make the request
46
- make_request(collection, results, searches)
47
- end
48
-
49
- # This method returns keystats about your API key, including the number of requests
50
- # and the number of granted requets. Be advised that this request actually counts
51
- # as a request to the server, so use with caution.
52
- def keystats
53
- uri = "#{BASE_URL}/books.xml?access_key=#{@access_key_set.current_key}&results=keystats"
54
- keystats = {}
55
- LibXML::XML::Parser.file(uri).parse.find('KeyStats').first.attributes.each { |attribute| keystats[attribute.name.to_sym] = attribute.value.to_i unless attribute.name == 'access_key' }
56
- return keystats
57
- end
58
-
59
- # Method missing allows for dynamic finders, similar to that of ActiveRecord. See
60
- # the README for more information on using magic finders.
61
- def method_missing(m, *args, &block)
62
- m = m.to_s.downcase
63
-
64
- if m.match(/find_(.+)_by_(.+)/)
65
- split = m.split('_', 4)
66
- collection, search_strs = split[1].downcase.pluralize, [split.last]
67
-
68
- # check and see if we are searching multiple fields
69
- search_strs = search_strs.first.split('_and_') if(search_strs.first.match(/_and_/))
70
- raise "Wrong Number of Arguments (#{args.size} for #{search_strs.size})" if args.size != search_strs.size
71
-
72
- # create the searches hash
73
- searches = {}
74
- search_strs.each_with_index { |str, i| searches[str.strip.to_sym] = args[i].strip }
75
-
76
- return find(:collection => collection, :where => searches)
77
- end
78
-
79
- super
80
- end
81
-
82
- # Pretty print the Query object with the access key.
83
- def to_s
84
- "#<ISBNdb::Query, @access_key=#{@access_key_set.current_key}>"
85
- end
86
-
87
- private
88
- # Make the request to the ResultSet. If the request fails because of an ISBNdb::AccessKeyError
89
- # the system will automatically rollover to the next AccessKey in the AccessKeySet. If one exists,
90
- # a new request is attempted. If not, the ISBNdb::AccessKeyError persists and can be caught by your
91
- # application logic.
92
- def make_request(collection, results, searches)
93
- begin
94
- uri = "#{BASE_URL}/#{collection}.xml?access_key=#{@access_key_set.current_key}&results=#{results.join(',')}&#{searches.join('&')}"
95
- ISBNdb::ResultSet.new(uri, collection.singularize.capitalize)
96
- rescue ISBNdb::AccessKeyError
97
- puts "Access Key Error (#{@access_key_set.current_key}) - You probably reached your limit! Trying the next key."
98
- @access_key_set.next_key!
99
- retry unless @access_key_set.current_key.nil?
100
- raise ISBNdb::AccessKeyError
101
- end
102
- end
103
- end
104
- end
2
+ require 'httparty'
105
3
 
4
+ require 'isbndb/access_key_set'
5
+ require 'isbndb/exceptions'
6
+ require 'isbndb/query'
7
+ require 'isbndb/result_set'
8
+ require 'isbndb/result'
106
9
 
107
- # Add a few methods to the String class
108
- class String
109
- def is_plural?
110
- self.downcase.pluralize == self.downcase
111
- end
112
-
113
- def is_singular?
114
- !self.is_plural?
115
- end
116
- end
10
+ require 'core_extensions/string'
11
+ require 'core_extensions/nil'
12
+ end
@@ -1,53 +1,64 @@
1
1
  module ISBNdb
2
-
3
- private
2
+ protected
4
3
  # The AccessKeySet is a simple class used to manage access keys. It is used primarily
5
4
  # by the ruby_isbndb class to automatically advance access keys when necessary.
6
5
  class AccessKeySet
7
6
  # Create the @access_keys array and then verify that the keys are valid keys.
8
- def initialize(access_keys)
9
- @access_keys = [access_keys].flatten
7
+ def initialize
8
+ @access_keys ||= YAML::load(File.open('config/isbndb.yml'))['access_keys']
10
9
  end
11
-
10
+
11
+ # Returns the total number of access keys in this set.
12
+ def size
13
+ @access_keys.size
14
+ end
15
+
16
+ def current_index
17
+ @current_index ||= 0
18
+ end
19
+
12
20
  # Get the current key. It returns a string of the access key.
13
21
  def current_key
14
- @access_keys[@current_index ||= 0]
22
+ @access_keys[current_index]
15
23
  end
16
-
24
+
17
25
  # Move the key pointer forward.
18
26
  def next_key!
19
- @current_index += 1
27
+ @current_index = current_index + 1
28
+ current_key
20
29
  end
21
-
30
+
22
31
  # Get the next key.
23
32
  def next_key
24
- @access_keys[@current_index+1]
33
+ @access_keys[current_index+1]
25
34
  end
26
-
35
+
27
36
  # Move the key pointer back.
28
37
  def prev_key!
29
- @current_index -= 1
38
+ @current_index = current_index - 1
39
+ current_key
30
40
  end
31
-
41
+
32
42
  # Get the previous key.
33
43
  def prev_key
34
- @access_keys[@current_index-1]
44
+ @access_keys[current_index-1]
35
45
  end
36
-
37
- # Tell Ruby ISBNdb to use a specified key. If the key does not exist, it is
46
+
47
+ # Tell Ruby ISBNdb to use a specified key. If the key does not exist, it is
38
48
  # added to the set and set as the current key.
39
49
  def use_key(key)
40
50
  @current_index = @access_keys.index(key) || @access_keys.push(key).index(key)
51
+ current_key
41
52
  end
42
-
53
+
43
54
  # Remove the given access key from the AccessKeySet.
44
55
  def remove_key(key)
45
56
  @access_keys.delete(key)
46
57
  end
47
-
58
+
48
59
  # Pretty print the AccessKeySet
49
60
  def to_s
50
- "#<AccessKeySet @keys=<#{@access_keys.collect{ |key| key }.join(',')}>>"
61
+ "#<AccessKeySet @keys=#{@access_keys.inspect}>"
51
62
  end
52
63
  end
53
- end
64
+ end
@@ -1,4 +1,3 @@
1
1
  module ISBNdb
2
2
  class AccessKeyError < StandardError; end
3
- class InvalidURIError < StandardError; end
4
- end
3
+ end
@@ -0,0 +1,78 @@
1
+ module ISBNdb
2
+ # The Query object is the most important class of the ISBNdb Module. It is the only public
3
+ # class, and handles the processing power.
4
+ class Query
5
+ include HTTParty
6
+ base_uri 'http://isbndb.com/api'
7
+ headers 'Content-Type' => 'text/xml'
8
+
9
+ # This is the generic find method. It accepts a hash of parameters including :collection,
10
+ # :where clauses, and :results to show. It builds the corresponding URI and sends that URI
11
+ # off to the ResultSet for processing.
12
+ def self.find(params = {})
13
+ raise 'No parameters specified! You must specify at least one parameter!' unless params[:where]
14
+ raise 'params[:where] cannot be a String! It must be a Hash!' if params[:where].is_a?(String)
15
+ raise 'params[:where] cannot be an Array! It must be a Hash!' if params[:where].is_a?(Array)
16
+
17
+ collection = params[:collection] ||= :books
18
+ results = params[:results] ||= :details
19
+ results = [results].flatten
20
+
21
+ # build the search clause
22
+ searches = []
23
+ params[:where].each_with_index do |(key,val), i|
24
+ searches << "index#{i+1}=#{key.to_s.strip}"
25
+ searches << "value#{i+1}=#{val.to_s.strip}"
26
+ end
27
+
28
+ # make the request
29
+ uri = "/#{collection}.xml?access_key=#{access_key_set.current_key}&results=#{results.join(',')}&#{searches.join('&')}"
30
+ ISBNdb::ResultSet.new(uri, collection)
31
+ rescue ISBNdb::AccessKeyError
32
+ access_key_set.next_key!
33
+ retry unless access_key_set.current_key.nil?
34
+ raise
35
+ end
36
+
37
+ # This method returns keystats about your API key, including the number of requests
38
+ # and the number of granted requets. Be advised that this request actually counts
39
+ # as a request to the server, so use with caution.
40
+ def self.keystats
41
+ result = self.get("/books.xml?access_key=#{access_key_set.current_key}&results=keystats")
42
+ result.parsed_response['ISBNdb']['KeyStats'] || {}
43
+ end
44
+
45
+ # Method missing allows for dynamic finders, similar to that of ActiveRecord. See
46
+ # the README for more information on using magic finders.
47
+ def self.method_missing(m, *args, &block)
48
+ method = m.to_s.downcase
49
+
50
+ if method.match(/find_(.+)_by_(.+)/)
51
+ split = method.split('_', 4)
52
+ collection, search_strs = split[1].downcase.pluralize, [split.last]
53
+
54
+ # check and see if we are searching multiple fields
55
+ search_strs = search_strs.first.split('_and_') if(search_strs.first.match(/_and_/))
56
+ raise "Wrong Number of Arguments (#{args.size} for #{search_strs.size})" if args.size != search_strs.size
57
+
58
+ # create the searches hash
59
+ searches = {}
60
+ search_strs.each_with_index { |str, i| searches[str.strip.to_sym] = args[i].strip }
61
+
62
+ return find(:collection => collection, :where => searches)
63
+ end
64
+
65
+ super
66
+ end
67
+
68
+ # Pretty print the Query object with the access key.
69
+ def self.to_s
70
+ "#<ISBNdb::Query, @access_key=#{access_key_set.current_key}>"
71
+ end
72
+
73
+ private
74
+ def self.access_key_set
75
+ @@access_key_set ||= ISBNdb::AccessKeySet.new
76
+ end
77
+ end
78
+ end