arbor 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 +0 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +34 -0
- data/MIT-LICENSE +20 -0
- data/README.md +48 -0
- data/Rakefile +0 -0
- data/arbor.gemspec +25 -0
- data/doctor.rb +87 -0
- data/lib/arbor/api.rb +42 -0
- data/lib/arbor/client.rb +37 -0
- data/lib/arbor/errors.rb +6 -0
- data/lib/arbor/filter.rb +19 -0
- data/lib/arbor/model/abstract.rb +69 -0
- data/lib/arbor/model/factory.rb +18 -0
- data/lib/arbor/model/no_op_serialiser.rb +9 -0
- data/lib/arbor/model/serialiser.rb +41 -0
- data/lib/arbor/query.rb +24 -0
- data/lib/arbor/serialiser_registry.rb +25 -0
- data/lib/arbor/utils.rb +29 -0
- data/lib/arbor/version.rb +3 -0
- data/lib/arbor.rb +149 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ef88472480a661ea860c484d64e75f9884a2d171
|
4
|
+
data.tar.gz: 13885de2ac13295ab06788ece47856b6502cf1af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a19f952f616fb95da9c16d36bf6a4b67d87bf1051096fedd5bf3c11714bdea34611f2277cab870c369276ecaa2f0a4d9aecd466c1eb8fa8b566ad9635c8adbcd
|
7
|
+
data.tar.gz: ec4b7ba338c051f2d8020bdc4b40e8fc705dc14a3d088aba5724f58fe3eff1e5063c1fa3cfe06fee7218020cf7ce6ad38471794bf2458f9deccc4f6f1a60e00d
|
data/.gitignore
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
arbor (0.0.1)
|
5
|
+
activesupport (>= 3.0.0)
|
6
|
+
httpi
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.1.5)
|
12
|
+
i18n (~> 0.6, >= 0.6.9)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
httpi (2.0.2)
|
18
|
+
rack
|
19
|
+
i18n (0.6.11)
|
20
|
+
json (1.8.1)
|
21
|
+
minitest (5.4.1)
|
22
|
+
rack (1.5.2)
|
23
|
+
rake (10.3.2)
|
24
|
+
thread_safe (0.3.4)
|
25
|
+
tzinfo (1.2.2)
|
26
|
+
thread_safe (~> 0.1)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
arbor!
|
33
|
+
bundler (~> 1.3)
|
34
|
+
rake
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Mike Campbell, Meritec Ltd
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Ruby Interface for Arbor API
|
2
|
+
|
3
|
+
A Ruby interface to [Arbor Education API](http://www.arbor-education.com/developers.html).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
gem install arbor
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'arbor'
|
13
|
+
|
14
|
+
client = Arbor::Client.new("some-school", "user@email.net", "abc123")
|
15
|
+
|
16
|
+
# retrieve all students
|
17
|
+
students = client.query(:students)
|
18
|
+
|
19
|
+
# resources loaded by `query` (listings) are lazily loaded, and will
|
20
|
+
# generate another request for that specific resource when an attribute is
|
21
|
+
# requested which isn't already loaded
|
22
|
+
students.each do |s|
|
23
|
+
# extra requests to /rest-v2/students/<id> (s.href) occur here for each item
|
24
|
+
puts "#{s.person.legal_first_name} #{s.person.legal_last_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# use `retrieve` to get a single resource
|
28
|
+
student = client.retrieve(:student, 1)
|
29
|
+
|
30
|
+
# by default, it serialises the API responses in to classes defined for
|
31
|
+
# each resource. you can set your own serialiser for a resource type:
|
32
|
+
Arbor.serialisers.register(:students, MyStudentSerialiser)
|
33
|
+
|
34
|
+
# and/or modify the default serialiser. serialiser classes must respond to
|
35
|
+
# `deserialise` and return a single resource
|
36
|
+
Arbor.serialisers.default_serialiser = MyDefaultSerialiser
|
37
|
+
```
|
38
|
+
|
39
|
+
## Still to do
|
40
|
+
|
41
|
+
- Write tests
|
42
|
+
- Support the other stuff like ordering, paging, changelog etc.
|
43
|
+
- Include an API mixin for the resource classes
|
44
|
+
- RDoc it up
|
45
|
+
|
46
|
+
## Licence
|
47
|
+
|
48
|
+
[MIT License](http://opensource.org/licenses/MIT)
|
data/Rakefile
ADDED
File without changes
|
data/arbor.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 'arbor/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "arbor"
|
8
|
+
spec.version = Arbor::VERSION
|
9
|
+
spec.authors = ["Mike Campbell"]
|
10
|
+
spec.email = ["mike.campbell@meritec.co.uk"]
|
11
|
+
spec.description = %q{Interface for the Arbor Education REST API.}
|
12
|
+
spec.summary = %q{Interface for the Arbor Education REST API.}
|
13
|
+
spec.homepage = "http://github.com/meritec/arbor-rb"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport", ">= 3.0.0"
|
22
|
+
spec.add_dependency "httpi", ">= 2.0.5"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake", "~> 0"
|
25
|
+
end
|
data/doctor.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Usage: ruby doctor.rb [HOST=status.github.com[:PORT=443]]
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
if ARGV[0] =~ /^[^-]/
|
6
|
+
host, port = ARGV[0].split(':', 2)
|
7
|
+
else
|
8
|
+
host = 'status.github.com'
|
9
|
+
end
|
10
|
+
port ||= 443
|
11
|
+
|
12
|
+
ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
|
13
|
+
ruby_version = RUBY_VERSION
|
14
|
+
if patch = RbConfig::CONFIG['PATCHLEVEL']
|
15
|
+
ruby_version += "-p#{patch}"
|
16
|
+
end
|
17
|
+
puts "%s (%s)" % [ruby, ruby_version]
|
18
|
+
|
19
|
+
openssl_dir = OpenSSL::X509::DEFAULT_CERT_AREA
|
20
|
+
mac_openssl = '/System/Library/OpenSSL' == openssl_dir
|
21
|
+
puts "%s: %s" % [OpenSSL::OPENSSL_VERSION, openssl_dir]
|
22
|
+
[OpenSSL::X509::DEFAULT_CERT_DIR_ENV, OpenSSL::X509::DEFAULT_CERT_FILE_ENV].each do |key|
|
23
|
+
puts "%s=%s" % [key, ENV[key].to_s.inspect]
|
24
|
+
end
|
25
|
+
|
26
|
+
ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
|
27
|
+
ca_path = (ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR).chomp('/')
|
28
|
+
|
29
|
+
puts "\nHEAD https://#{host}:#{port}"
|
30
|
+
http = Net::HTTP.new(host, port)
|
31
|
+
http.use_ssl = true
|
32
|
+
|
33
|
+
# Explicitly setting cert_store like this is not needed in most cases but it
|
34
|
+
# seems necessary in edge cases such as when using `verify_callback` in some
|
35
|
+
# combination of Ruby + OpenSSL versions.
|
36
|
+
http.cert_store = OpenSSL::X509::Store.new
|
37
|
+
http.cert_store.set_default_paths
|
38
|
+
|
39
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
40
|
+
failed_cert = failed_cert_reason = nil
|
41
|
+
|
42
|
+
if mac_openssl
|
43
|
+
warn "warning: will not be able show failed certificate info on OS X's OpenSSL"
|
44
|
+
# This drives me absolutely nuts. It seems that on Rubies compiled against OS X's
|
45
|
+
# system OpenSSL, the mere fact of defining a `verify_callback` makes the
|
46
|
+
# cert verification fail for requests that would otherwise be successful.
|
47
|
+
else
|
48
|
+
http.verify_callback = lambda { |verify_ok, store_context|
|
49
|
+
if !verify_ok
|
50
|
+
failed_cert = store_context.current_cert
|
51
|
+
failed_cert_reason = "%d: %s" % [ store_context.error, store_context.error_string ]
|
52
|
+
end
|
53
|
+
verify_ok
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
user_agent = "net/http #{ruby_version}"
|
58
|
+
req = Net::HTTP::Head.new('/', 'user-agent' => user_agent)
|
59
|
+
|
60
|
+
begin
|
61
|
+
res = http.start { http.request(req) }
|
62
|
+
abort res.inspect if res.code.to_i >= 500
|
63
|
+
puts "OK"
|
64
|
+
rescue Errno::ECONNREFUSED
|
65
|
+
puts "Error: connection refused"
|
66
|
+
exit 1
|
67
|
+
rescue OpenSSL::SSL::SSLError => e
|
68
|
+
puts "#{e.class}: #{e.message}"
|
69
|
+
|
70
|
+
if failed_cert
|
71
|
+
puts "\nThe server presented a certificate that could not be verified:"
|
72
|
+
puts " subject: #{failed_cert.subject}"
|
73
|
+
puts " issuer: #{failed_cert.issuer}"
|
74
|
+
puts " error code %s" % failed_cert_reason
|
75
|
+
end
|
76
|
+
|
77
|
+
ca_file_missing = !File.exist?(ca_file) && !mac_openssl
|
78
|
+
ca_path_empty = Dir["#{ca_path}/*"].empty?
|
79
|
+
|
80
|
+
if ca_file_missing || ca_path_empty
|
81
|
+
puts "\nPossible causes:"
|
82
|
+
puts " `%s' does not exist" % ca_file if ca_file_missing
|
83
|
+
puts " `%s/' is empty" % ca_path if ca_path_empty
|
84
|
+
end
|
85
|
+
|
86
|
+
exit 1
|
87
|
+
end
|
data/lib/arbor/api.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'arbor/model/serialiser'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
module API
|
5
|
+
def retrieve(type, id)
|
6
|
+
resource = parse_resource(type)
|
7
|
+
data = get("/rest-v2/#{resource}/#{id}")
|
8
|
+
|
9
|
+
unmarshall_data(data, resource)
|
10
|
+
end
|
11
|
+
|
12
|
+
def query(type, query = nil)
|
13
|
+
resource = parse_resource(type)
|
14
|
+
query_string = query.build_query_string if query
|
15
|
+
|
16
|
+
data = get("/rest-v2/#{resource}?#{query_string}")
|
17
|
+
|
18
|
+
unmarshall_data(data, resource)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def unmarshall_data(data, resource)
|
23
|
+
singular_resource = resource.to_s.singularize
|
24
|
+
plural_resource = resource.to_s
|
25
|
+
|
26
|
+
if (res = data[singular_resource])
|
27
|
+
Model::Serialiser.parse_resource(res).tap { |obj|
|
28
|
+
obj.attach_client(self) if obj.respond_to?(:attach_client)
|
29
|
+
obj.lock_attributes = true
|
30
|
+
}
|
31
|
+
elsif data[plural_resource]
|
32
|
+
data[plural_resource].map do |res|
|
33
|
+
Model::Serialiser.parse_resource(res).tap { |obj|
|
34
|
+
obj.attach_client(self) if obj.respond_to?(:attach_client)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise Errors::SerialisationError, "Unexpected root key in API data."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/arbor/client.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'arbor/api'
|
2
|
+
require 'arbor/utils'
|
3
|
+
require 'httpi'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Arbor
|
7
|
+
class Client
|
8
|
+
include Arbor::API
|
9
|
+
include Arbor::Utils
|
10
|
+
attr_accessor :host, :username, :password, :settings
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
@settings = args.last.is_a?(Hash) ? args.pop : {}
|
14
|
+
raise ArgumentError, "must supply a subdomain, username and password" if args.length < 3
|
15
|
+
subdomain, @username, @password = args
|
16
|
+
@host = "https://#{subdomain}.uk.arbor.sc"
|
17
|
+
end
|
18
|
+
|
19
|
+
[:get, :post].each do |verb|
|
20
|
+
define_method(verb) do |*args|
|
21
|
+
request = configure_request(*args)
|
22
|
+
response = HTTPI.request(verb, request, settings[:adapter])
|
23
|
+
JSON.parse(response.body.presence || "{}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def configure_request(path, *args)
|
29
|
+
url = "#{host}#{path}"
|
30
|
+
request = HTTPI::Request.new(url, *args)
|
31
|
+
request.headers["Accept"] = "application/json"
|
32
|
+
auth_type = settings[:adapter] == :net_http ? :basic : :digest
|
33
|
+
request.auth.send(auth_type, username, password)
|
34
|
+
request
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/arbor/errors.rb
ADDED
data/lib/arbor/filter.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'arbor/utils'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
class Filter
|
5
|
+
include Arbor::Utils
|
6
|
+
attr_accessor :attribute, :operator, :value
|
7
|
+
OPERATORS = [:equals, :from, :to, :after, :before, :search, :in]
|
8
|
+
|
9
|
+
def initialize(attribute, operator, value)
|
10
|
+
@attribute = attribute
|
11
|
+
@operator = validate(operator, OPERATORS)
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"filters.#{attribute}.#{operator}=#{value}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'arbor/model/serialiser'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
module Model
|
5
|
+
class Abstract
|
6
|
+
attr_accessor :api_client, :entity_type, :attribute_names, :attribute_lock
|
7
|
+
|
8
|
+
def initialize(attributes)
|
9
|
+
@attribute_names = []
|
10
|
+
load_attributes(attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(meth, *args, &block)
|
14
|
+
attribute_lock ? super : begin
|
15
|
+
refresh_data
|
16
|
+
send(meth, *args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def refresh_data
|
21
|
+
raise "No API client configured for this resource" if api_client.nil?
|
22
|
+
raise "No entity_type set for this resource" if entity_type.nil?
|
23
|
+
raise "No known endpoint for this resource" if href.nil?
|
24
|
+
|
25
|
+
data = api_client.get(href)
|
26
|
+
|
27
|
+
parsed_attributes = Serialiser.parse_attributes(data[entity_type.downcase])
|
28
|
+
load_attributes(parsed_attributes)
|
29
|
+
attach_client(self.api_client)
|
30
|
+
|
31
|
+
@attribute_lock = true
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_attributes(attributes)
|
35
|
+
attributes.each do |name, value|
|
36
|
+
unless self.respond_to?("#{name}=")
|
37
|
+
self.class.instance_eval { define_method(name) { attribute(name) } }
|
38
|
+
self.class.send(:attr_writer, name)
|
39
|
+
end
|
40
|
+
@attribute_names << name
|
41
|
+
self.send("#{name}=", value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def attribute(name)
|
46
|
+
if instance_variable_defined?("@#{name}")
|
47
|
+
instance_variable_get("@#{name}")
|
48
|
+
else
|
49
|
+
raise Errors::UnknownAttributeError if attribute_lock
|
50
|
+
refresh_data
|
51
|
+
attribute(name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def attach_client(client)
|
56
|
+
self.api_client = client
|
57
|
+
self.attribute_names.each do |name|
|
58
|
+
if send(name).respond_to?("api_client=")
|
59
|
+
send(name).attach_client(client)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def unlock_attributes
|
65
|
+
@attribute_lock = false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'arbor/model/abstract'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
module Model
|
5
|
+
class Factory
|
6
|
+
def self.create(type = :unknown)
|
7
|
+
class_name = type.to_s.classify
|
8
|
+
|
9
|
+
unless Arbor::Model.const_defined?(class_name)
|
10
|
+
type_class = Class.new(Arbor::Model::Abstract)
|
11
|
+
Arbor::Model.const_set(class_name, type_class)
|
12
|
+
end
|
13
|
+
|
14
|
+
Arbor::Model.const_get(class_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'arbor/model/factory'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
module Model
|
5
|
+
class Serialiser
|
6
|
+
class << self
|
7
|
+
def deserialise(data)
|
8
|
+
attributes = parse_attributes(data)
|
9
|
+
|
10
|
+
resource = data["entityType"].underscore.to_sym
|
11
|
+
Factory.create(resource).new(attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_attributes(attributes)
|
15
|
+
attr_array = attributes.map do |key, value|
|
16
|
+
value = case value
|
17
|
+
when Hash
|
18
|
+
parse_resource(value)
|
19
|
+
when Array
|
20
|
+
value.map { |nr| parse_resource(nr) }
|
21
|
+
else
|
22
|
+
value
|
23
|
+
end
|
24
|
+
[key, value]
|
25
|
+
end
|
26
|
+
|
27
|
+
transform_keys(Hash[attr_array])
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_resource(r)
|
31
|
+
nested_type = r["entityType"]
|
32
|
+
nested_type ? Arbor.serialisers[nested_type].deserialise(r) : r
|
33
|
+
end
|
34
|
+
|
35
|
+
def transform_keys(hash)
|
36
|
+
Hash[hash.map { |k, v| [k.underscore, v] }]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/arbor/query.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'arbor/utils'
|
3
|
+
require 'arbor/filter'
|
4
|
+
|
5
|
+
module Arbor
|
6
|
+
class Query
|
7
|
+
include Arbor::Utils
|
8
|
+
|
9
|
+
attr_accessor :filters, :resource
|
10
|
+
|
11
|
+
def initialize(type, filters = [])
|
12
|
+
@resource = parse_resource(type)
|
13
|
+
@filters = filters
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_query_string
|
17
|
+
URI.escape(filters.map(&:to_s).join("&"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_filter(*args)
|
21
|
+
@filters << Arbor::Filter.new(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'arbor/model/serialiser'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
class SerialiserRegistry
|
5
|
+
attr_accessor :registry, :default_serialiser
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@registry = {}
|
9
|
+
@default_serialiser = Model::Serialiser
|
10
|
+
end
|
11
|
+
|
12
|
+
def register(resource, serialiser)
|
13
|
+
registry[resource] = serialiser
|
14
|
+
end
|
15
|
+
|
16
|
+
def unregister(resource)
|
17
|
+
registry[resource] = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_serialiser(resource)
|
21
|
+
registry[resource] || default_serialiser
|
22
|
+
end
|
23
|
+
alias_method :[], :get_serialiser
|
24
|
+
end
|
25
|
+
end
|
data/lib/arbor/utils.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module Arbor
|
4
|
+
module Utils
|
5
|
+
def parse_resource(type)
|
6
|
+
validate(get_resource(type), Arbor::RESOURCES)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_resource(type)
|
10
|
+
case type
|
11
|
+
when Class
|
12
|
+
type.name.pluralize.underscore.to_sym
|
13
|
+
else
|
14
|
+
type.to_s.pluralize.to_sym
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate(choice, options)
|
19
|
+
unless options.include?(choice)
|
20
|
+
raise ArgumentError, "#{choice} is not a valid option: #{options}"
|
21
|
+
end
|
22
|
+
choice
|
23
|
+
end
|
24
|
+
|
25
|
+
def left_pad(array, num)
|
26
|
+
num.times { array.unshift(nil) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/arbor.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'arbor/client'
|
2
|
+
require 'arbor/query'
|
3
|
+
require 'arbor/serialiser_registry'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'arbor/errors'
|
6
|
+
|
7
|
+
module Arbor
|
8
|
+
class << self
|
9
|
+
def serialisers
|
10
|
+
@serialisers ||= SerialiserRegistry.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RESOURCES = [:academic_calendar_dates, :academic_holidays, :academic_levels,
|
15
|
+
:academic_level_memberships, :academic_level_registration_form_mappings,
|
16
|
+
:academic_level_tutors, :academic_programmes, :academic_programme_instances,
|
17
|
+
:academic_teaching_hours, :academic_units,
|
18
|
+
:academic_unit_automatic_enrolments, :academic_unit_cohorts,
|
19
|
+
:academic_unit_cohort_memberships, :academic_unit_curriculums,
|
20
|
+
:academic_unit_enrolments, :academic_unit_leads, :academic_unit_tags,
|
21
|
+
:academic_working_days, :academic_years, :achievement_levels,
|
22
|
+
:achievement_level_sets, :additional_payment_reasons, :alerts, :allowances,
|
23
|
+
:allowance_amounts, :applications, :application_settings,
|
24
|
+
:application_withdrawal_reasons, :aspects, :aspect_sets, :assessments,
|
25
|
+
:assessment_batches, :assessment_batch_targets, :assessment_derivations,
|
26
|
+
:assessment_derivation_dependants, :assessment_templates,
|
27
|
+
:assessment_template_assessments, :attendance_follow_ups, :attendance_marks,
|
28
|
+
:attendance_patterns, :attendance_pattern_periods, :attendance_records,
|
29
|
+
:attendance_register_types, :attendance_register_type_marks,
|
30
|
+
:awarding_bodies, :awarding_body_staffs, :bank_details, :behaviours,
|
31
|
+
:behavioural_contracts, :behavioural_contract_goals,
|
32
|
+
:behavioural_contract_parties, :behavioural_contract_periods,
|
33
|
+
:behavioural_incidents, :behavioural_incident_actions,
|
34
|
+
:behavioural_incident_behaviours, :behavioural_incident_staff_involvements,
|
35
|
+
:behavioural_incident_student_involvements, :behavioural_notes,
|
36
|
+
:boarding_patterns, :boarding_statuses, :bookable_resource_periods,
|
37
|
+
:business_roles, :business_role_assignments,
|
38
|
+
:business_role_default_user_roles, :calendars, :calendar_entries,
|
39
|
+
:calendar_entry_dates, :calendar_entry_mappings, :calendar_types, :cases,
|
40
|
+
:center_numbers, :check_clearance_levels, :check_types,
|
41
|
+
:child_protection_statuses, :child_protection_status_assignments,
|
42
|
+
:collections, :communication_actions, :communication_tags, :companies,
|
43
|
+
:company_staffs, :consents, :consent_types, :contents, :content_tags,
|
44
|
+
:countries, :court_orders, :court_order_parties, :cover_arrangements,
|
45
|
+
:cover_requirements, :curriculums, :curriculum_evidences, :curriculum_goals,
|
46
|
+
:curriculum_grades, :curriculum_judgements, :curriculum_sections,
|
47
|
+
:curriculum_stages, :curriculum_tiers, :custom_groups,
|
48
|
+
:custom_group_memberships, :dashboards, :dashboard_kpis,
|
49
|
+
:dashboard_sections, :data_check_errors, :data_check_error_related_entities,
|
50
|
+
:data_export_errors, :data_export_error_tags, :data_item_subscriptions,
|
51
|
+
:data_set_subscriptions, :detentions, :detention_sessions,
|
52
|
+
:dietary_requirements, :dietary_requirement_types, :document_html_templates,
|
53
|
+
:document_image_templates, :document_layouts, :education_plans,
|
54
|
+
:education_plan_contributors, :eligibilities, :eligibility_records, :emails,
|
55
|
+
:email_accounts, :email_account_access_permissions, :email_addresses,
|
56
|
+
:email_drafts, :email_mailboxes, :email_parts, :email_recipients,
|
57
|
+
:email_references, :email_senders, :email_statuses, :enrolment_modes,
|
58
|
+
:ethnicities, :event_participants, :event_participant_invitations,
|
59
|
+
:exceptions, :exclusion_reasons, :faculties, :faculty_members, :files,
|
60
|
+
:genders, :grades, :grade_point_scales, :grade_point_scale_level_grade_sets,
|
61
|
+
:grade_sets, :guardians, :guardian_consultations,
|
62
|
+
:guardian_relationship_types, :hearing_tests, :houses, :house_memberships,
|
63
|
+
:house_tutors, :hydrators, :identification_document_types, :identity_cards,
|
64
|
+
:identity_card_readers, :immunizations, :immunization_types,
|
65
|
+
:impairment_categories, :import_records, :in_care_statuses,
|
66
|
+
:in_care_status_assignments, :intake_seasons,
|
67
|
+
:intake_season_applicant_buckets, :intake_season_groups, :item_units, :kpis,
|
68
|
+
:kpi_ranges, :kpi_targets, :kpi_values, :languages, :language_abilities,
|
69
|
+
:layout_object_types, :letters, :letter_drafts, :meals, :meal_patterns,
|
70
|
+
:meal_prices, :meal_provisions, :meal_sessions,
|
71
|
+
:meal_session_register_records, :meal_sittings,
|
72
|
+
:meal_sitting_automatic_attendee_targets, :medical_conditions,
|
73
|
+
:medical_condition_medications, :medical_condition_types, :medical_events,
|
74
|
+
:medical_event_types, :medical_institutions, :medical_institution_staffs,
|
75
|
+
:medical_plans, :medical_relationship_types, :medications, :meetings,
|
76
|
+
:meeting_notes, :meeting_topics, :message_draft_recipients, :model_bases,
|
77
|
+
:motor_vehicles, :motor_vehicle_makes, :nationality_statuses,
|
78
|
+
:next_of_kin_relationships, :notes, :notices, :operations, :pastoral_notes,
|
79
|
+
:pay_scales, :pay_scale_grades, :pay_scale_grade_spinal_points,
|
80
|
+
:pay_scale_spinal_points, :pay_scale_spinal_point_values,
|
81
|
+
:permanent_exclusions, :people, :person_checks, :person_former_names,
|
82
|
+
:person_identification_documents, :person_impairments,
|
83
|
+
:person_nationalities, :person_next_of_kins,
|
84
|
+
:person_professional_qualifications, :person_training_courses,
|
85
|
+
:person_transportation_methods, :point_awards, :point_award_categories,
|
86
|
+
:point_award_scales, :point_award_totals, :positions,
|
87
|
+
:position_business_roles, :postal_addresses, :postal_address_occupancies,
|
88
|
+
:professional_qualification_classes, :professional_qualification_subjects,
|
89
|
+
:professional_qualification_types, :progress_assessment_batches,
|
90
|
+
:progress_assessment_batch_measurement_periods,
|
91
|
+
:progress_assessment_batch_targets, :progress_measurement_periods,
|
92
|
+
:qualification_aspects, :qualification_assessables,
|
93
|
+
:qualification_assessable_instances, :qualification_awards,
|
94
|
+
:qualification_award_level_types, :qualification_award_types,
|
95
|
+
:qualification_centre_assessed_marks,
|
96
|
+
:qualification_element_disallowed_combinations,
|
97
|
+
:qualification_element_relationships, :qualification_element_series_links,
|
98
|
+
:qualification_grades, :qualification_grade_sets,
|
99
|
+
:qualification_incoming_messages, :qualification_learning_units,
|
100
|
+
:qualification_predictions, :qualification_registrations,
|
101
|
+
:qualification_registration_requests, :qualification_request_batches,
|
102
|
+
:qualification_results, :qualification_schemes, :qualification_series,
|
103
|
+
:qualification_special_arrangements,
|
104
|
+
:qualification_special_arrangement_types, :qualification_timetable_sessions,
|
105
|
+
:qualification_timetable_session_types, :qualification_time_windows,
|
106
|
+
:qualification_time_window_collections, :registration_forms,
|
107
|
+
:registration_form_memberships, :registration_form_tutors, :religions,
|
108
|
+
:report_card_batches, :report_card_batch_targets,
|
109
|
+
:report_card_batch_template_variables, :report_card_templates,
|
110
|
+
:report_card_template_sections, :report_subscriptions, :rooms, :room_assets,
|
111
|
+
:room_features, :room_room_assets, :room_room_features, :schools,
|
112
|
+
:school_enrolments, :school_events, :school_event_types, :school_staffs,
|
113
|
+
:school_trips, :schoolworks, :schoolwork_curriculum_goals,
|
114
|
+
:schoolwork_types, :sending_profiles, :sen_events, :sen_event_outcomes,
|
115
|
+
:sen_event_outcome_types, :sen_event_types, :sen_needs, :sen_need_types,
|
116
|
+
:sen_notes, :sen_provisions, :sen_provision_linked_relationships,
|
117
|
+
:sen_provision_types, :sen_statements, :sen_statuses,
|
118
|
+
:sen_status_assignments, :sessions, :session_curriculum_goals,
|
119
|
+
:session_room_asset_requirements, :session_room_feature_requirements,
|
120
|
+
:session_streams, :session_stream_automatic_enrolments,
|
121
|
+
:session_stream_enrolments, :session_stream_leads, :sites,
|
122
|
+
:site_operating_periods, :site_operating_period_exceptions,
|
123
|
+
:site_travel_times, :sms_messages, :sms_message_drafts, :sms_message_parts,
|
124
|
+
:sms_message_statuses, :snippets, :snippet_availabilities, :staffs,
|
125
|
+
:staff_absences, :staff_absence_categories, :staff_attendance_marks,
|
126
|
+
:staff_attendance_records, :staff_contracts, :staff_contract_posts,
|
127
|
+
:staff_contract_post_additional_payments, :staff_contract_post_allowances,
|
128
|
+
:staff_contract_post_end_reasons, :staff_contract_post_rates,
|
129
|
+
:staff_contract_post_salaries, :staff_contract_post_start_reasons,
|
130
|
+
:staff_destinations, :staff_employment_types, :staff_origins,
|
131
|
+
:staff_room_feature_requirements, :staff_working_periods, :students,
|
132
|
+
:student_absence_notes, :student_assessments, :student_assessment_marks,
|
133
|
+
:student_attendance_patterns, :student_guardian_relationships,
|
134
|
+
:student_linked_relationships, :student_linked_relationship_types,
|
135
|
+
:student_medical_relationships, :student_other_school_attendance_records,
|
136
|
+
:student_progress_assessments, :student_progress_assessment_marks,
|
137
|
+
:student_progress_baselines, :student_progress_goals, :student_report_cards,
|
138
|
+
:student_report_card_marks, :student_report_card_sections,
|
139
|
+
:student_room_feature_requirements, :student_schools, :student_schoolworks,
|
140
|
+
:student_schoolwork_marks, :subjects, :superannuation_schemes,
|
141
|
+
:superannuation_scheme_values, :tags, :taggings,
|
142
|
+
:teaching_assistant_allocations, :telephone_calls, :telephone_numbers,
|
143
|
+
:temporary_exclusions, :terms, :timetable_slots, :timetable_slot_staffs,
|
144
|
+
:titles, :training_courses, :training_events, :transportation_methods,
|
145
|
+
:triggers, :unenrolment_reasons, :universities, :university_staffs,
|
146
|
+
:usage_log_entries, :users, :user_defined_fields, :user_defined_records,
|
147
|
+
:user_notifications, :user_roles, :user_role_assignments, :vision_tests,
|
148
|
+
:widgets, :widget_types, :workitems, :workitem_assignments]
|
149
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: arbor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Campbell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httpi
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.0.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
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
|
+
description: Interface for the Arbor Education REST API.
|
70
|
+
email:
|
71
|
+
- mike.campbell@meritec.co.uk
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- Gemfile.lock
|
79
|
+
- MIT-LICENSE
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- arbor.gemspec
|
83
|
+
- doctor.rb
|
84
|
+
- lib/arbor.rb
|
85
|
+
- lib/arbor/api.rb
|
86
|
+
- lib/arbor/client.rb
|
87
|
+
- lib/arbor/errors.rb
|
88
|
+
- lib/arbor/filter.rb
|
89
|
+
- lib/arbor/model/abstract.rb
|
90
|
+
- lib/arbor/model/factory.rb
|
91
|
+
- lib/arbor/model/no_op_serialiser.rb
|
92
|
+
- lib/arbor/model/serialiser.rb
|
93
|
+
- lib/arbor/query.rb
|
94
|
+
- lib/arbor/serialiser_registry.rb
|
95
|
+
- lib/arbor/utils.rb
|
96
|
+
- lib/arbor/version.rb
|
97
|
+
homepage: http://github.com/meritec/arbor-rb
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.3.0
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Interface for the Arbor Education REST API.
|
121
|
+
test_files: []
|
122
|
+
has_rdoc:
|