lexile 0.0.2
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 +18 -0
- data/.travis.yml +14 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +93 -0
- data/Rakefile +30 -0
- data/lexile.gemspec +25 -0
- data/lib/lexile/api/books.rb +26 -0
- data/lib/lexile/api/client.rb +105 -0
- data/lib/lexile/api/endpoints.rb +20 -0
- data/lib/lexile/api/resource.rb +27 -0
- data/lib/lexile/book.rb +41 -0
- data/lib/lexile/configuration.rb +73 -0
- data/lib/lexile/errors.rb +27 -0
- data/lib/lexile/model.rb +103 -0
- data/lib/lexile/version.rb +3 -0
- data/lib/lexile.rb +22 -0
- data/spec/lexile/api/books_spec.rb +113 -0
- data/spec/lexile/api/client_spec.rb +97 -0
- data/spec/lexile/configuration_spec.rb +51 -0
- data/spec/lexile/errors_spec.rb +18 -0
- data/spec/lexile/model_spec.rb +41 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/lexile_helper.rb +29 -0
- data/spec/vcr_tapes/books.yml +342 -0
- data/spec/vcr_tapes/errors.yml +410 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b3572b7bf5426c44e0887f4c77425129a96fd3d
|
4
|
+
data.tar.gz: e7e3897e33a3c90d8d83bd63e3c5b04b7d11430a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9d503217391a94ffe678eb940d0037557c32a5f9faf194f018c3f1065009ea61ffb266422f5bafa612894106ec24d968db3798852f3b23ef10b345d3cb7eacf3
|
7
|
+
data.tar.gz: b0aea8b1285bf7c94cca0fc3431e32d6e36bc2bac1be29489c9bab0ac833db4aa810550d30c239f6111a1a635553f033fa6d4ea8a3554ea28ae8c96d571b00f5
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
|
4
|
+
rvm:
|
5
|
+
- 2.1.2
|
6
|
+
|
7
|
+
script: 'CODECLIMATE_REPO_TOKEN=cd173d6c860a8493b813ea4ed982824b180b0c50604e3a27c3a3c83e94868d71 bundle exec rake'
|
8
|
+
|
9
|
+
notifications:
|
10
|
+
email:
|
11
|
+
recipients:
|
12
|
+
- mauricio@curriculet.com
|
13
|
+
on_failure: change
|
14
|
+
on_success: never
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in lexile.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'httparty', '~> 0.10'
|
7
|
+
gem 'hashie', '~> 2.0'
|
8
|
+
|
9
|
+
group :development, :test do
|
10
|
+
gem 'rspec', '~> 2'
|
11
|
+
gem 'webmock'
|
12
|
+
gem 'vcr', '~> 2.8'
|
13
|
+
gem 'jeweler'
|
14
|
+
gem 'factory_girl'
|
15
|
+
end
|
16
|
+
|
17
|
+
gem "codeclimate-test-reporter", group: :test, require: nil
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2014 Curriculet Inc
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
[](https://travis-ci.org/Curriculet/Lexile)
|
2
|
+
[](https://codeclimate.com/github/curriculet/lexile)
|
3
|
+
[](https://codeclimate.com/github/curriculet/lexile)
|
4
|
+
[](http://badge.fury.io/gh/curriculet%2Flexile)
|
5
|
+
|
6
|
+
# Lexile®
|
7
|
+
|
8
|
+
This gem wraps a portion of the Lexile database API. You need to obtain an
|
9
|
+
authorized username and password from Lexile to use the API. This gem is not
|
10
|
+
meant to be a comprenhensive implementation of the API just a bare bones
|
11
|
+
"Find the Lexile for a Specific Book" solution.
|
12
|
+
|
13
|
+
The gem only covers the endpoints to the `book` resource, the `category` and
|
14
|
+
`serial` resources are beyond the scope of this gem . If you need access
|
15
|
+
to these resources feel free to submit a PR with code and specs.
|
16
|
+
|
17
|
+
The gem does not support pagination for result-sets longer than 20 books, while
|
18
|
+
the API contains all provisions for pagination, it is beyond the scope of the
|
19
|
+
gem to expose those.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
gem 'lexile'
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
$ bundle install
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
$ gem install lexile
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
>> b = Lexile.books.show("315833")
|
38
|
+
#<Lexile::Book id= "315833" ISBN="006201272X" ISBN13="9780062012722" ... >
|
39
|
+
|
40
|
+
>> b.title
|
41
|
+
"It Happened to Nancy: By An Anonymous Teenager: A True Story from Her Diary"
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
>> b = Lexile.books.find_by_isbn13("9780062012722")
|
46
|
+
[#<Lexile::Book ISBN="006201272X" ISBN13="9780062012722" ... >]
|
47
|
+
|
48
|
+
>> b[0].title
|
49
|
+
"It Happened to Nancy: By An Anonymous Teenager: A True Story from Her Diary"
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
>> b = Lexile.books.find_by_title("to Nancy")
|
54
|
+
[#<Lexile::Book ISBN="0380773155" ISBN13="9780380773152" ... >, #<Lexile::Book ISBN="006201272X" ISBN13="9780062012722" ... >]
|
55
|
+
|
56
|
+
>> b.length
|
57
|
+
2
|
58
|
+
|
59
|
+
>> b[0].title
|
60
|
+
"It Happened to Nancy: By An Anonymous Teenager: A True Story from Her Diary"
|
61
|
+
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
1. Fork it ( http://github.com/<my-github-username>/lexile/fork )
|
66
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
67
|
+
3. Commit your changes and specs(`git commit -am 'Add some feature'`)
|
68
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
69
|
+
5. Create new Pull Request
|
70
|
+
|
71
|
+
## License
|
72
|
+
|
73
|
+
The MIT License (MIT)
|
74
|
+
|
75
|
+
Copyright (c) 2014 Curriculet Inc
|
76
|
+
|
77
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
78
|
+
of this software and associated documentation files (the "Software"), to deal
|
79
|
+
in the Software without restriction, including without limitation the rights
|
80
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
81
|
+
copies of the Software, and to permit persons to whom the Software is
|
82
|
+
furnished to do so, subject to the following conditions:
|
83
|
+
|
84
|
+
The above copyright notice and this permission notice shall be included in
|
85
|
+
all copies or substantial portions of the Software.
|
86
|
+
|
87
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
88
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
89
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
90
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
91
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
92
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
93
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "rubygems/package_task"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
gem_spec = eval(File.read("./lexile.gemspec")) rescue nil
|
7
|
+
Gem::PackageTask.new(gem_spec) do |pkg|
|
8
|
+
pkg.need_zip = false
|
9
|
+
pkg.need_tar = false
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
13
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
17
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
18
|
+
spec.rcov = true
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :spec
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'yard'
|
25
|
+
YARD::Rake::YardocTask.new
|
26
|
+
rescue LoadError
|
27
|
+
task :yardoc do
|
28
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
29
|
+
end
|
30
|
+
end
|
data/lexile.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'lexile/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "lexile"
|
8
|
+
spec.version = Lexile::VERSION
|
9
|
+
|
10
|
+
spec.authors = ["Mauricio Alvarez"]
|
11
|
+
spec.email = ["mauricio@curriculet.com"]
|
12
|
+
spec.date = "2014-10-25"
|
13
|
+
spec.description = "A gem for the Lexile® database API "
|
14
|
+
spec.summary = "A gem to find a book's Lexile DB entry by name or ISBN13"
|
15
|
+
spec.homepage = "https://github.com/curriculet/lexile"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency "httparty", "~> 0.10"
|
24
|
+
spec.add_runtime_dependency "hashie", "~> 2.0.0"
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Lexile
|
2
|
+
module Api
|
3
|
+
class Books < Resource
|
4
|
+
api_model Lexile::Book
|
5
|
+
|
6
|
+
def show id
|
7
|
+
response = @client.get( "#{ api_model.api_path }/#{id}" )
|
8
|
+
api_model.parse(response.body)
|
9
|
+
end
|
10
|
+
|
11
|
+
def find( query_params )
|
12
|
+
response = @client.get( "#{ api_model.api_path }", query_params )
|
13
|
+
api_model.parse(response.body)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_isbn13 isbn13
|
17
|
+
self.find( {"ISBN13" => isbn13 })
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_title title
|
21
|
+
self.find( {"title__contains" => title } )
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Lexile
|
2
|
+
module API
|
3
|
+
class Client
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
# initialize
|
7
|
+
#
|
8
|
+
# The Lexile Api Client can be configured via Lexile.configure block
|
9
|
+
# or via a hash with the appropriate values set
|
10
|
+
#
|
11
|
+
# endpoint: required. The API endpoing i.e https://api.lexile.com
|
12
|
+
# api_version: required. The API version (v1 or V1.1)
|
13
|
+
# username: required. Your lexile username
|
14
|
+
# password: required. Your lexile password
|
15
|
+
# timeout: optional. The HTTP timeout
|
16
|
+
# testing: optional. Set it to true to activate debugging output
|
17
|
+
#
|
18
|
+
# @param configuration [Lexile Hash] A hash or Lexile::Configuration that contains the parameters to
|
19
|
+
# be used when issuing HTTP Calls
|
20
|
+
def initialize( configuration )
|
21
|
+
|
22
|
+
if configuration.is_a?(Module) && configuration.to_s == 'Lexile'
|
23
|
+
config = configuration
|
24
|
+
elsif configuration.is_a?(Hash)
|
25
|
+
config = Hashie::Mash.new( configuration )
|
26
|
+
config.api_url = [config.endpoint,config.api_version].join('/')
|
27
|
+
end
|
28
|
+
|
29
|
+
#if config.api_key
|
30
|
+
#self.class.default_params({ 'api_key' => config.api_key})
|
31
|
+
#else
|
32
|
+
#raise Lexile::InvalidCredentials.new("Api Key is not present but it is required.")
|
33
|
+
#end
|
34
|
+
|
35
|
+
self.class.base_uri( config.api_url ) if config.api_url
|
36
|
+
self.class.default_timeout config.timeout.to_f if config.timeout
|
37
|
+
self.class.debug_output if config.testing
|
38
|
+
self.class.headers({'User-Agent' => config.user_agent}) if config.user_agent
|
39
|
+
end
|
40
|
+
|
41
|
+
# get
|
42
|
+
# send an HTTPS Get request against Edelement's API.
|
43
|
+
# Yields the get_options before sending making the request.
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# @param path [String] The path for the api endpoint (with leading slash)
|
47
|
+
# @param params [Hash] A hash of query arguments for the request (optional)
|
48
|
+
# @param headers [Hash] A hash of header fields (optional).
|
49
|
+
#
|
50
|
+
#
|
51
|
+
# @return [String]. The raw response body (JSON is expected) Raises appropriate exception if it fails
|
52
|
+
# @raise Lexile::HTTPError when any HTTP error response is received
|
53
|
+
def get( path, params={}, headers={})
|
54
|
+
get_options = build_get_options( params, headers)
|
55
|
+
yield get_options if block_given?
|
56
|
+
|
57
|
+
#puts "CALLING API: #{Lexile.api_url}#{path} ===#{get_options}"
|
58
|
+
response = self.class.get( path, get_options)
|
59
|
+
|
60
|
+
case response.code
|
61
|
+
when 200..201
|
62
|
+
response
|
63
|
+
when 400
|
64
|
+
raise Lexile::BadRequest.new(response, params)
|
65
|
+
when 401
|
66
|
+
raise Lexile::AuthenticationFailed.new(response, params)
|
67
|
+
when 404
|
68
|
+
raise Lexile::NotFound.new(response, params)
|
69
|
+
when 500
|
70
|
+
raise Lexile::ServerError.new(response, params)
|
71
|
+
when 502
|
72
|
+
raise Lexile::Unavailable.new(response, params)
|
73
|
+
when 503, 504
|
74
|
+
raise Lexile::RateLimited.new(response, params)
|
75
|
+
else
|
76
|
+
raise Lexile::UnknownStatusCode.new(response, params)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# build_get_options
|
81
|
+
# Build the hash of options for an HTTP get request.
|
82
|
+
#
|
83
|
+
# @param params [Hash] optional. Any query parameters to add to the request.
|
84
|
+
# @param user_headers [Hash] optional. Any query parameters to add to the request.
|
85
|
+
#
|
86
|
+
# @return [Hash] The properly formated get_options.
|
87
|
+
def build_get_options( params={}, user_headers={})
|
88
|
+
get_options = {}
|
89
|
+
query ={ format: 'json'} #all requests get this query params
|
90
|
+
|
91
|
+
query.merge!(params)
|
92
|
+
|
93
|
+
# pass any headers
|
94
|
+
headers ={}
|
95
|
+
headers.merge!( user_headers )
|
96
|
+
|
97
|
+
get_options[:query] = query
|
98
|
+
get_options[:headers] = headers
|
99
|
+
get_options[:basic_auth] = { username: Lexile.options[:username],
|
100
|
+
password: Lexile.options[:password]}
|
101
|
+
get_options
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Lexile
|
2
|
+
module Api
|
3
|
+
module Endpoints
|
4
|
+
|
5
|
+
|
6
|
+
# books
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Lexile.books.find(title: or isbn: ) returns the details of book
|
10
|
+
#
|
11
|
+
# @return [Lexile::Api::Books] A properly initialized books api client ready for calls
|
12
|
+
|
13
|
+
|
14
|
+
def books
|
15
|
+
@books ||= Lexile::Api::Books.new( client )
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Lexile
|
2
|
+
module Api
|
3
|
+
class Resource
|
4
|
+
class << self
|
5
|
+
#api_model
|
6
|
+
#
|
7
|
+
# It tells the parser which Lexile::Model to use when parsing JSON and creating objects
|
8
|
+
#
|
9
|
+
# @param klass [Lexile::Model] The Lexile::Model class to be used when parsing and objectifying responses.
|
10
|
+
def api_model(klass)
|
11
|
+
class_eval <<-END
|
12
|
+
def api_model
|
13
|
+
#{klass}
|
14
|
+
end
|
15
|
+
END
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# initialize
|
20
|
+
# @param client [Lexile::API::Client] required to issue HTTP calls
|
21
|
+
def initialize( client )
|
22
|
+
@client = client
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/lexile/book.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Lexile
|
2
|
+
class Book < Hashie::Mash
|
3
|
+
include Lexile::Model
|
4
|
+
|
5
|
+
api_path '/book'
|
6
|
+
|
7
|
+
# Book Resource
|
8
|
+
#
|
9
|
+
# This is the resource you will likely be using most often. This represents a book.
|
10
|
+
#
|
11
|
+
# Field Type Filter Description
|
12
|
+
# ISBN string ALL International Standard Book Number
|
13
|
+
# booktype string ALL Booktype, e.g. Fiction
|
14
|
+
# lexile_code string n/a Lexile Code, e.g. GN (graphic novel)
|
15
|
+
# series string ALL Book Series, if any
|
16
|
+
# lexile_code_spanish string n/a Lexile Code for Spanish Lexile measure if available
|
17
|
+
# word_count integer ALL Number of words contained in the book
|
18
|
+
# keyword string ALL List of keywords
|
19
|
+
# serial integer gt Serial Number this Resource was last modified on
|
20
|
+
# id integer n/a ID
|
21
|
+
# max_age integer ALL Max age of book
|
22
|
+
# call_number string n/a Call Number
|
23
|
+
# copyright string n/a Copyright year
|
24
|
+
# author string ALL Book Author(s)
|
25
|
+
# lexile integer ALL Lexile Measure
|
26
|
+
# categories_display string n/a List of categories
|
27
|
+
# timestamp datetime gt,gte Date/time the book record was last modified
|
28
|
+
# min_age integer ALL Minimum age of book
|
29
|
+
# pages integer ALL Number of pages contained in the book
|
30
|
+
# categories related ALL List of URIs or individually nested resource data
|
31
|
+
# publisher string ALL Book publisher
|
32
|
+
# language string ALL Book's written language
|
33
|
+
# ISBN13 string ALL 13-digit ISBN
|
34
|
+
# lexile_spanish integer n/a Spanish Lexile Measure
|
35
|
+
# word_count_spanish integer n/a Number of words in Spanish version of book
|
36
|
+
# summary string n/a Summary of the book
|
37
|
+
# title string ALL Book Title
|
38
|
+
# resource_uri string n/a The resource url for accessing this resource directly
|
39
|
+
#
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Lexile
|
2
|
+
module Configuration
|
3
|
+
VALID_CONNECTION_KEYS = [:endpoint, :api_version, :user_agent, :testing, :timeout].freeze
|
4
|
+
VALID_OPTIONS_KEYS = [:username,:password].freeze
|
5
|
+
|
6
|
+
#@!visibility private
|
7
|
+
VALID_CONFIG_KEYS = VALID_CONNECTION_KEYS + VALID_OPTIONS_KEYS
|
8
|
+
|
9
|
+
DEFAULT_ENDPOINT = 'https://fabapi.lexile.com/api/fab'
|
10
|
+
DEFAULT_API_VERSION = 'v2'
|
11
|
+
DEFAULT_USER_AGENT = 'Lexile API Ruby Gem by Curriculet'.freeze
|
12
|
+
DEFAULT_TIMEOUT = nil
|
13
|
+
DEFAULT_TESTING = false
|
14
|
+
DEFAULT_USERNAME = nil
|
15
|
+
DEFAULT_PASSWORD = nil
|
16
|
+
|
17
|
+
# Build accessor methods for every config options so we can do this, for example:
|
18
|
+
attr_accessor *VALID_CONFIG_KEYS
|
19
|
+
|
20
|
+
# Make sure we have the default values set when we get 'extended'
|
21
|
+
def self.extended(base)
|
22
|
+
base.reset!
|
23
|
+
end
|
24
|
+
|
25
|
+
# reset!
|
26
|
+
# set all options to their defaults and free the client
|
27
|
+
def reset!
|
28
|
+
self.endpoint = DEFAULT_ENDPOINT
|
29
|
+
self.api_version = DEFAULT_API_VERSION
|
30
|
+
self.user_agent = DEFAULT_USER_AGENT
|
31
|
+
self.timeout = DEFAULT_TIMEOUT
|
32
|
+
self.testing = DEFAULT_TESTING
|
33
|
+
self.username = DEFAULT_USERNAME
|
34
|
+
self.password = DEFAULT_PASSWORD
|
35
|
+
|
36
|
+
@client = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
#@return [Hash] of all options
|
40
|
+
def options
|
41
|
+
Hash[ * VALID_CONFIG_KEYS.map { |key| [key, send(key)] }.flatten ]
|
42
|
+
end
|
43
|
+
|
44
|
+
# api_url
|
45
|
+
# Interpolate the base url for all calls
|
46
|
+
# return [String] the base url for all api calls
|
47
|
+
def api_url( path = nil)
|
48
|
+
[endpoint,api_version,path].compact.join('/')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Yields itself for use in the configuration block
|
52
|
+
# @example
|
53
|
+
# Lexile.configure do |c|
|
54
|
+
# c.api_key = <MY-API-KEY>
|
55
|
+
# c.api_verion = 'v2'
|
56
|
+
# c.endpoint = 'https://fabapi.lexile.com/api/fab/'
|
57
|
+
# c.timeout = '10' #seconds
|
58
|
+
# c.testing = true
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# @return Lexile::API::Client
|
62
|
+
#
|
63
|
+
def configure
|
64
|
+
yield self
|
65
|
+
@client = Lexile::API::Client.new( self )
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def client
|
70
|
+
@client ||= Lexile::API::Client.new( self )
|
71
|
+
end
|
72
|
+
end # Configuration
|
73
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Lexile
|
2
|
+
class InvalidCredentials < StandardError; end
|
3
|
+
class CannotProcessResponse < StandardError; end
|
4
|
+
|
5
|
+
class HTTPError < StandardError
|
6
|
+
attr_reader :response
|
7
|
+
attr_reader :params
|
8
|
+
|
9
|
+
def initialize(response, params = {})
|
10
|
+
@response = response
|
11
|
+
@params = params
|
12
|
+
super(response)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{self.class.to_s} : #{response.code} #{response.body}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class NotFound < HTTPError; end
|
21
|
+
class Unavailable < HTTPError; end
|
22
|
+
class BadRequest < HTTPError; end
|
23
|
+
class ServerError < HTTPError; end
|
24
|
+
class AuthenticationFailed < HTTPError; end
|
25
|
+
class RateLimited < HTTPError; end
|
26
|
+
class UnknownStatusCode < HTTPError; end
|
27
|
+
end
|
data/lib/lexile/model.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Lexile
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.send :extend, ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# api_path
|
9
|
+
# This sets the API path so the API collections can use them in an agnostic way
|
10
|
+
# @param path [String] the api path for the current model with a leading slash
|
11
|
+
# @return [void]
|
12
|
+
def api_path(path = nil)
|
13
|
+
@_api_path ||= path
|
14
|
+
end
|
15
|
+
|
16
|
+
# Understanding the Response
|
17
|
+
# The API will return a JSON response for each query. Lets take a look at a sample response.
|
18
|
+
#
|
19
|
+
# {"meta":
|
20
|
+
# {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1},
|
21
|
+
# "objects":
|
22
|
+
# [
|
23
|
+
# {
|
24
|
+
# "ISBN": "1234567890",
|
25
|
+
# "ISBN13": "9781234567890",
|
26
|
+
# "author": "Author, Fake",
|
27
|
+
# "booktype": "Fiction",
|
28
|
+
# "call_number": "",
|
29
|
+
# "categories":
|
30
|
+
# [{"id": 1, "name": "Action", "resource_uri": "/api/fab/2013-02-04/category/1/"}],
|
31
|
+
# "categories_display": "",
|
32
|
+
# "copyright": "2013",
|
33
|
+
# "id": 1,
|
34
|
+
# "keyword": "",
|
35
|
+
# "language": "",
|
36
|
+
# "lexile": 1000,
|
37
|
+
# "lexile_code": "",
|
38
|
+
# "lexile_code_spanish": "",
|
39
|
+
# "lexile_display": "1000L",
|
40
|
+
# "lexile_spanish": null,
|
41
|
+
# "max_age": null,
|
42
|
+
# "min_age": null,
|
43
|
+
# "pages": 5,
|
44
|
+
# "publisher":
|
45
|
+
# "Fake Publisher",
|
46
|
+
# "resource_uri": "/api/fab/2013-02-04/book/1/",
|
47
|
+
# "serial": 1,
|
48
|
+
# "series": "",
|
49
|
+
# "summary": "This is a sample summary",
|
50
|
+
# "timestamp": "2013-05-04T17:43:50",
|
51
|
+
# "title": "Test Book",
|
52
|
+
# "word_count": 400,
|
53
|
+
# "word_count_spanish": null
|
54
|
+
# }
|
55
|
+
# ]
|
56
|
+
# }
|
57
|
+
#
|
58
|
+
# We will mainly cover the meta data in the response in this section. For more details about the book object
|
59
|
+
# itself please refer to the resources documentation. Lets take a look at just the meta block.
|
60
|
+
#
|
61
|
+
# {"meta":{"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1}
|
62
|
+
#
|
63
|
+
# In the meta object you will find key pieces of information about your result.
|
64
|
+
# limit: By default only 20 objects are sent back in each response. You can increase or decrease this limit by
|
65
|
+
# supplying a limit filter on your query.
|
66
|
+
#
|
67
|
+
# next: The url for you to fetch the next set of results in your query. This url will not be a fully qualified
|
68
|
+
# url but instead relative to the base url we discussed earlier. If the value is none there is not another set
|
69
|
+
# of results and you have reached the last "page"
|
70
|
+
#
|
71
|
+
# offset: The number of records you are offsetting this result by. For example if you had a limit of 20 and
|
72
|
+
# there were 100 total records. If you had an offset of 60 that would mean you are looking at records 61-80.
|
73
|
+
#
|
74
|
+
# previous: Same as next but refers to the previous set of results. If this is null there are no previous sets
|
75
|
+
# available.
|
76
|
+
#
|
77
|
+
# total_count: The total number of results in the query. For example if you had a query that generated 100
|
78
|
+
# results this number would be 100.
|
79
|
+
#
|
80
|
+
# A field that is not explained in the resources documentation that should be noted here is resource_uri.
|
81
|
+
# You will notice in our sample response there is a resource_uri for the category and the book. This is the
|
82
|
+
# url you will use to obtain information about that particular object. Like the next and previous urls, this is
|
83
|
+
# not fully qualified and is relative to the base url discussed earlier.
|
84
|
+
|
85
|
+
# Parses a request.body response into a Lexile::Model objects
|
86
|
+
#
|
87
|
+
def parse( raw_json )
|
88
|
+
parsed_json = String === raw_json ? JSON.parse(raw_json) : json
|
89
|
+
if parsed_json.has_key?('objects')
|
90
|
+
#this is a multi record and data should contain an array
|
91
|
+
unless parsed_json['objects'].is_a? Array
|
92
|
+
raise Lexile::CannotProcessResponse.new('[:objects] is present in response but it does not contain an Array')
|
93
|
+
end
|
94
|
+
return parsed_json['objects'].map {|array_element| new( array_element ) }
|
95
|
+
else
|
96
|
+
return new( parsed_json )
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|