lessonly-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +2 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/Procfile +1 -0
- data/README.md +102 -0
- data/Rakefile +4 -0
- data/lessonly-ruby.gemspec +33 -0
- data/lib/lessonly.rb +23 -0
- data/lib/lessonly/client.rb +62 -0
- data/lib/lessonly/defaults.rb +34 -0
- data/lib/lessonly/exceptions.rb +31 -0
- data/lib/lessonly/resource.rb +102 -0
- data/lib/lessonly/resource/assignment.rb +5 -0
- data/lib/lessonly/resource/course.rb +33 -0
- data/lib/lessonly/resource/group.rb +18 -0
- data/lib/lessonly/resource/lesson.rb +5 -0
- data/lib/lessonly/resource/user.rb +4 -0
- data/lib/lessonly/response.rb +70 -0
- data/lib/lessonly/serializer.rb +19 -0
- data/lib/lessonly/version.rb +3 -0
- data/spec/course_spec.rb +68 -0
- data/spec/group_spec.rb +41 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/fake_lessonly.rb +35 -0
- data/spec/support/fixtures/get_courses.json +17 -0
- data/spec/support/fixtures/get_courses_1.json +22 -0
- data/spec/support/fixtures/get_courses_1_assignments.json +14 -0
- data/spec/support/fixtures/get_groups.json +9 -0
- data/spec/support/fixtures/get_groups_1.json +18 -0
- data/spec/support/fixtures/get_users.json +27 -0
- data/spec/support/fixtures/get_users_1.json +8 -0
- data/spec/support/fixtures/get_users_3.json +8 -0
- data/spec/support/fixtures/put_courses_1_assignments.json +14 -0
- data/spec/support/fixtures/put_groups_1.json +22 -0
- data/spec/user_spec.rb +17 -0
- metadata +236 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 05e500d1cfe48a8a50cb16f50f921c652887474b
|
4
|
+
data.tar.gz: 5e7e36a9db0d0040d82048302004b9abde2438ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 757b43c94fe0649d9a81130e8ec366a1ae537235493458ae4b6356f359b4fc389f617b98895e129823b826823b85928adfd9bc487f06cb503069a95edd6c8e5f
|
7
|
+
data.tar.gz: 3fff06a332fa0c36678b048253a1dbdbbd8d8d17ec018b38584a953a141f27f77c6d1cb3a61dc3d153a869ee5191fd45d9f753eb222799a70ddb5dc132e08737
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Aptible, Inc.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
console: bundle exec pry -r lessonly
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
#  Lessonly Ruby Client
|
2
|
+
|
3
|
+
|
4
|
+
Ruby client for [Lesson.ly](http://lesson.ly) API.
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add the following line to your application's Gemfile
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'lessonly-ruby'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then run `bundle install`.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
First, configure your client:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'lessonly'
|
23
|
+
|
24
|
+
Lessonly.configure do |config|
|
25
|
+
config.root_url = 'https://api.lesson.ly/api/v1'
|
26
|
+
config.api_key = `LESSONLY API KEY`
|
27
|
+
config.domain = `LESSONLY SUBDOMAIN`
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
This is best done in an application initializer inside `config/initializers`.
|
32
|
+
|
33
|
+
From here, you can interact with the Lessonly API resources however you wish:
|
34
|
+
|
35
|
+
### Users
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
all_users = Lessonly::User.all
|
39
|
+
|
40
|
+
skylar = Lessonly::User.find(user_id)
|
41
|
+
skylar.name # "Skylar Anderson"
|
42
|
+
skylar.email # "skylar@aptible.com"
|
43
|
+
|
44
|
+
frank = Lessonly::User.create({ name: 'Frank Macreery', email: 'frank@aptible.com', role: 'learner' })
|
45
|
+
frank.name # "Frank Macreery"
|
46
|
+
```
|
47
|
+
|
48
|
+
### Groups
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
all_groups = Lessonly::Group.all
|
52
|
+
developers = Lessonly::Group.find(group_id)
|
53
|
+
```
|
54
|
+
|
55
|
+
### Add a user to a group
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
developers = Lessonly::Group.find(group_id)
|
59
|
+
|
60
|
+
developers.create_membership(skylar) # Add Skylar to developers group
|
61
|
+
developers.create_membership(frank) # Add Frank to developers group
|
62
|
+
```
|
63
|
+
|
64
|
+
### Courses
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
all_courses = Lessonly::Course.all
|
68
|
+
|
69
|
+
course = Lessonly::Course.find(course_id)
|
70
|
+
course.create_assignment(skylar, 1.year.from_now) # Skylar assigned to course, due in 1 year
|
71
|
+
|
72
|
+
course.assignments.count # 1
|
73
|
+
course.assignments.first.user.name # "Skylar"
|
74
|
+
course.assignments.first.user.email # "skylar@aptible.com"
|
75
|
+
|
76
|
+
course.lessons.count # 1
|
77
|
+
course.lessons.first.title # "Developer Lesson"
|
78
|
+
```
|
79
|
+
|
80
|
+
### Assign a course to a user
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
course = Lessonly::Course.find(course_id)
|
84
|
+
user = Lessonly::User.find(user_id)
|
85
|
+
|
86
|
+
course.create_assignment(skylar, 1.month.from_now) # User assigned to course, due in a month
|
87
|
+
```
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
1. Fork the project.
|
92
|
+
1. Commit your changes, with specs.
|
93
|
+
1. Ensure that your code passes specs (`rake spec`) and meets Aptible's Ruby style guide (`rake rubocop`).
|
94
|
+
1. Create a new pull request on GitHub.
|
95
|
+
|
96
|
+
## Copyright and License
|
97
|
+
|
98
|
+
MIT License, see [LICENSE](LICENSE.md) for details.
|
99
|
+
|
100
|
+
Copyright (c) 2014 [Aptible](https://www.aptible.com) and contributors.
|
101
|
+
|
102
|
+
[<img src="https://s.gravatar.com/avatar/9b58236204e844e3181e43e05ddb0809?s=60" style="border-radius: 50%;" alt="@sandersonet" />](https://github.com/sandersonet)
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'English'
|
6
|
+
require 'lessonly/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'lessonly-ruby'
|
10
|
+
spec.version = Lessonly::VERSION
|
11
|
+
spec.authors = ['Skylar Anderson']
|
12
|
+
spec.email = ['skylar@aptible.com']
|
13
|
+
spec.description = 'Ruby client for Lesson.ly API'
|
14
|
+
spec.summary = 'Ruby client for Lesson.ly API'
|
15
|
+
spec.homepage = 'https://github.com/aptible/lessonly-ruby'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($RS)
|
19
|
+
spec.test_files = spec.files.grep(%r{^spec\/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'gem_config'
|
23
|
+
spec.add_dependency 'sawyer', '~> 0.5.3'
|
24
|
+
spec.add_dependency 'activesupport'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
27
|
+
spec.add_development_dependency 'aptible-tasks'
|
28
|
+
spec.add_development_dependency 'rake'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'webmock'
|
31
|
+
spec.add_development_dependency 'pry'
|
32
|
+
spec.add_development_dependency 'sinatra'
|
33
|
+
end
|
data/lib/lessonly.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'gem_config'
|
2
|
+
require 'lessonly/defaults'
|
3
|
+
|
4
|
+
module Lessonly
|
5
|
+
include GemConfig::Base
|
6
|
+
|
7
|
+
with_configuration do
|
8
|
+
has :root_url,
|
9
|
+
classes: String,
|
10
|
+
default: ENV['LESSONLY_ROOT_URL'] || 'https://api.lesson.ly/api/v1'
|
11
|
+
|
12
|
+
has :api_key,
|
13
|
+
classes: String,
|
14
|
+
default: ENV['LESSONLY_API_KEY'] || ''
|
15
|
+
|
16
|
+
has :domain,
|
17
|
+
classes: String,
|
18
|
+
default: ENV['LESSONLY_DOMAIN'] || ''
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'lessonly/client'
|
23
|
+
require 'lessonly/resource'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'sawyer'
|
2
|
+
require 'lessonly/serializer'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Lessonly
|
6
|
+
class Client
|
7
|
+
include Lessonly::Defaults
|
8
|
+
attr_reader :agent
|
9
|
+
attr_reader :last_response
|
10
|
+
|
11
|
+
def get(url, query = {})
|
12
|
+
request :get, url, nil, query: query
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(url, resource, query = {})
|
16
|
+
request(:post, url, resource, query: query)
|
17
|
+
end
|
18
|
+
|
19
|
+
def put(url, resource, query = {})
|
20
|
+
request :put, url, resource, query: query
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(url, resource)
|
24
|
+
request :delete, url, resource
|
25
|
+
end
|
26
|
+
|
27
|
+
def patch(url, resource, query = {})
|
28
|
+
request :patch, url, resource, query: query
|
29
|
+
end
|
30
|
+
|
31
|
+
def agent
|
32
|
+
@agent ||= Sawyer::Agent.new(api_endpoint, sawyer_options) do |http|
|
33
|
+
http.headers[:accept] = media_type
|
34
|
+
http.headers[:user_agent] = user_agent
|
35
|
+
http.headers[:authorization] = "Basic #{auth_token}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def auth_token
|
42
|
+
Base64.strict_encode64("#{domain}:#{api_key}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def sawyer_options
|
46
|
+
{
|
47
|
+
faraday: Faraday.new(connection_options),
|
48
|
+
serializer: Lessonly::Serializer.any_json
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def request(method, path, data, options = {})
|
53
|
+
options[:headers] ||= {}
|
54
|
+
unless method == :get
|
55
|
+
options[:headers][:content_type] = 'application/json'
|
56
|
+
end
|
57
|
+
|
58
|
+
@last_response = agent.call(method, path, data, options)
|
59
|
+
@last_response.data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'lessonly/version'
|
2
|
+
|
3
|
+
module Lessonly
|
4
|
+
module Defaults
|
5
|
+
def api_key
|
6
|
+
Lessonly.configuration.api_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def api_endpoint
|
10
|
+
Lessonly.configuration.root_url
|
11
|
+
end
|
12
|
+
|
13
|
+
def domain
|
14
|
+
Lessonly.configuration.domain
|
15
|
+
end
|
16
|
+
|
17
|
+
def user_agent
|
18
|
+
"Lessonly Ruby Gem #{Lessonly::VERSION} made by Aptible"
|
19
|
+
end
|
20
|
+
|
21
|
+
def media_type
|
22
|
+
'application/json'
|
23
|
+
end
|
24
|
+
|
25
|
+
def connection_options
|
26
|
+
{
|
27
|
+
headers: {
|
28
|
+
accept: media_type,
|
29
|
+
user_agent: user_agent
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lessonly
|
2
|
+
class Exception < ::StandardError
|
3
|
+
attr_accessor :cause
|
4
|
+
|
5
|
+
def initialize(message, attrs = {})
|
6
|
+
self.cause = attrs[:cause]
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ResponseError < Exception
|
12
|
+
attr_accessor :response
|
13
|
+
attr_accessor :body
|
14
|
+
|
15
|
+
def initialize(message, attrs = {})
|
16
|
+
self.response = attrs[:response]
|
17
|
+
self.body = attrs[:body]
|
18
|
+
|
19
|
+
if body.present? && body.key?(:error)
|
20
|
+
error = body[:error]
|
21
|
+
message = "#{message} (#{error})"
|
22
|
+
elsif response
|
23
|
+
message = "#{message} (\"#{response.inspect}\")"
|
24
|
+
end
|
25
|
+
|
26
|
+
super(message, attrs)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ResourceNotFoundError < ResponseError; end
|
31
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
require 'lessonly/response'
|
5
|
+
|
6
|
+
module Lessonly
|
7
|
+
class Resource < Sawyer::Resource
|
8
|
+
def self.all(query = {})
|
9
|
+
client.get collection_path, query: query
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find(id)
|
13
|
+
find_by_href("#{collection_path}/#{id}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find_by_href(href)
|
17
|
+
client.get(href)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create(params, query = {})
|
21
|
+
client.post collection_path, new(client.agent, params), query: query
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.collection_path
|
25
|
+
basename
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:disable PredicateName
|
29
|
+
def self.has_many(relation)
|
30
|
+
define_has_many_getter(relation)
|
31
|
+
# TODO: define_has_many_setter(relation)
|
32
|
+
end
|
33
|
+
# rubocop:enable PredicateName
|
34
|
+
|
35
|
+
def self.define_has_many_getter(relation)
|
36
|
+
define_method relation do
|
37
|
+
if (memoized = instance_variable_get("@#{relation}"))
|
38
|
+
memoized
|
39
|
+
else
|
40
|
+
entries = client.get("#{href}/#{relation}")
|
41
|
+
instance_variable_set("@#{relation}", entries)
|
42
|
+
entries
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# rubocop:disable PredicateName
|
48
|
+
def self.has_one(relation, options = {})
|
49
|
+
define_method relation do
|
50
|
+
if (memoized = instance_variable_get("@#{relation}"))
|
51
|
+
memoized
|
52
|
+
else
|
53
|
+
klass = options[:klass] || "Lessonly::#{relation.classify}"
|
54
|
+
|
55
|
+
id_attr = options[:using] ? options[:using] : "#{relation}_id"
|
56
|
+
relation_id = instance_variable_get('@attrs')[id_attr]
|
57
|
+
|
58
|
+
if relation_id
|
59
|
+
item = klass.constantize.find(relation_id)
|
60
|
+
instance_variable_set("@#{relation}", item)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# rubocop:enable PredicateName
|
66
|
+
|
67
|
+
def self.basename
|
68
|
+
name.split('::').last.underscore.dasherize.pluralize
|
69
|
+
end
|
70
|
+
|
71
|
+
def update(params)
|
72
|
+
self.attrs = attrs.merge(params)
|
73
|
+
client.put href, self
|
74
|
+
end
|
75
|
+
|
76
|
+
def destroy
|
77
|
+
client.delete href, self
|
78
|
+
end
|
79
|
+
|
80
|
+
def reload
|
81
|
+
self.class.find_by_href(href)
|
82
|
+
end
|
83
|
+
|
84
|
+
def href
|
85
|
+
"#{self.class.collection_path}/#{id}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.client
|
89
|
+
@client ||= Lessonly::Client.new
|
90
|
+
end
|
91
|
+
|
92
|
+
def client
|
93
|
+
self.class.client
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'lessonly/resource/assignment'
|
99
|
+
require 'lessonly/resource/course'
|
100
|
+
require 'lessonly/resource/group'
|
101
|
+
require 'lessonly/resource/lesson'
|
102
|
+
require 'lessonly/resource/user'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Lessonly
|
2
|
+
class Course < Resource
|
3
|
+
has_many :assignments
|
4
|
+
|
5
|
+
def initialize(agent, data = {})
|
6
|
+
super(agent, data)
|
7
|
+
|
8
|
+
define_has_many_lessons if @attrs[:lessons]
|
9
|
+
end
|
10
|
+
|
11
|
+
def define_has_many_lessons
|
12
|
+
self.lessons = @attrs[:lessons].map do |l|
|
13
|
+
Lessonly::Lesson.new(agent, l.attrs)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_assignment(user, due_by = 1.year.from_now)
|
18
|
+
self.assignments = assignments.push(
|
19
|
+
Lessonly::Assignment.new(agent, assignee_id: user.id, due_by: due_by)
|
20
|
+
)
|
21
|
+
|
22
|
+
client.put "#{href}/assignments",
|
23
|
+
Resource.new(agent, assignments: assignments)
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy_assignment(user)
|
27
|
+
self.assignments = assignments.select { |a| a.assignee_id != user.id }
|
28
|
+
|
29
|
+
client.put "#{href}/assignments",
|
30
|
+
Resource.new(agent, assignments: assignments)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Lessonly
|
2
|
+
class Group < Resource
|
3
|
+
def destroy_membership(user)
|
4
|
+
return unless members.any?
|
5
|
+
|
6
|
+
new_members = members.map do |m|
|
7
|
+
m.remove = true if m.id == user.id
|
8
|
+
end
|
9
|
+
|
10
|
+
update(members: new_members)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_membership(user)
|
14
|
+
self.members = members.push(user)
|
15
|
+
update(members: members)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'lessonly/exceptions'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
module Sawyer
|
6
|
+
class Response
|
7
|
+
# rubocop:disable MethodLength
|
8
|
+
def initialize(agent, res, options = {})
|
9
|
+
if res.status >= 400
|
10
|
+
fail Lessonly::ResponseError.new(
|
11
|
+
res.status.to_s,
|
12
|
+
response: res,
|
13
|
+
body: agent.decode_body(res.body),
|
14
|
+
cause: res.status
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
@res = res
|
19
|
+
@agent = agent
|
20
|
+
@status = res.status
|
21
|
+
@headers = res.headers
|
22
|
+
@env = res.env
|
23
|
+
@rels = process_rels
|
24
|
+
@started = options[:sawyer_started]
|
25
|
+
@ended = options[:sawyer_ended]
|
26
|
+
@data = if @headers[:content_type] =~ /json|msgpack/
|
27
|
+
process_data(@agent.decode_body(res.body))
|
28
|
+
else
|
29
|
+
res.body
|
30
|
+
end
|
31
|
+
|
32
|
+
log_response
|
33
|
+
end
|
34
|
+
# rubocop:enable MethodLength
|
35
|
+
|
36
|
+
def log_response
|
37
|
+
# puts @env
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_data(data)
|
41
|
+
@type = type_from_data(data) || @type
|
42
|
+
fail 'No type!' unless @type
|
43
|
+
|
44
|
+
data = data[@type.to_sym] if data.key?(@type.to_sym)
|
45
|
+
|
46
|
+
case data
|
47
|
+
when Hash then klass_from_type(data).new(agent, data)
|
48
|
+
when Array then data.map { |hash| process_data(hash) }
|
49
|
+
when nil then nil
|
50
|
+
else data
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def klass_from_type(result)
|
55
|
+
# Get sub object type if exists, otherwise
|
56
|
+
# defer to parent type
|
57
|
+
type = type_from_data(result) || @type
|
58
|
+
"Lessonly::#{type.classify}".constantize
|
59
|
+
end
|
60
|
+
|
61
|
+
def type_from_data(data)
|
62
|
+
# Create user type: `create_user`
|
63
|
+
# Delete user type: `delete_user`
|
64
|
+
# Course assignment type: `course_assignments`
|
65
|
+
# Use the last segment of type to determine resource class
|
66
|
+
return nil unless data.key? :type
|
67
|
+
data[:type].split('_').last
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Lessonly
|
2
|
+
class Serializer < Sawyer::Serializer
|
3
|
+
def encode_object(resource)
|
4
|
+
resource
|
5
|
+
# return resource.serialize if resource.respond_to? :serialize
|
6
|
+
# case resource
|
7
|
+
# when Hash then encode_hash(resource)
|
8
|
+
# when Array then resource.map { |o| encode_object(o) }
|
9
|
+
# else resource
|
10
|
+
# end
|
11
|
+
end
|
12
|
+
|
13
|
+
def time_field?(key, value)
|
14
|
+
time_fields = %w(created updated start stop initalPeriodStart
|
15
|
+
currentPeriodStart currentPeriodEnd)
|
16
|
+
value && time_fields.include?(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/course_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lessonly::Course do
|
4
|
+
context '#all' do
|
5
|
+
it 'should return all courses' do
|
6
|
+
courses = Lessonly::Course.all
|
7
|
+
expect(courses.count).to eq 3
|
8
|
+
expect(courses.first.title).to eq 'HIPAA Basic'
|
9
|
+
expect(courses.first.id).to eq 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context '#find' do
|
14
|
+
it 'should find a single course' do
|
15
|
+
course = Lessonly::Course.find(1)
|
16
|
+
expect(course.title).to eq 'HIPAA Advanced'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#lessons' do
|
21
|
+
it 'should have many lessons' do
|
22
|
+
course = Lessonly::Course.find(1)
|
23
|
+
|
24
|
+
expect(course.lessons.first).to be_a Lessonly::Lesson
|
25
|
+
expect(course.lessons.first.title).to eq 'Basic HIPAA Lesson'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#assignments' do
|
30
|
+
it 'should have many assignments' do
|
31
|
+
course = Lessonly::Course.find(1)
|
32
|
+
assignments = course.assignments
|
33
|
+
|
34
|
+
expect(assignments.count).to eq 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should serialize assignment assignees as user' do
|
38
|
+
course = Lessonly::Course.find(1)
|
39
|
+
assignee = course.assignments.first.user
|
40
|
+
|
41
|
+
expect(assignee).to be_a Lessonly::User
|
42
|
+
expect(assignee.name).to eq 'Chas Ballew'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context '#create_assignment' do
|
47
|
+
it 'should create a user assignment' do
|
48
|
+
course = Lessonly::Course.find(1)
|
49
|
+
user = Lessonly::User.find(3)
|
50
|
+
|
51
|
+
course.create_assignment(user)
|
52
|
+
|
53
|
+
expect(course.assignments.map(&:assignee_id)).to include(user.id)
|
54
|
+
expect(course.assignments.count).to eq 2
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context '#destroy_assignment' do
|
59
|
+
it 'should remove a user assignment' do
|
60
|
+
course = Lessonly::Course.find(1)
|
61
|
+
user = Lessonly::User.find(1)
|
62
|
+
|
63
|
+
course.destroy_assignment(user)
|
64
|
+
|
65
|
+
expect(course.assignments.count).to eq 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/group_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lessonly::Group do
|
4
|
+
context '#all' do
|
5
|
+
it 'should return all groups' do
|
6
|
+
groups = Lessonly::Group.all
|
7
|
+
expect(groups.count).to eq 1
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#find' do
|
12
|
+
it 'should find a single group' do
|
13
|
+
group = Lessonly::Group.find(1)
|
14
|
+
expect(group.name).to eq 'Developers'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context '#create_membership' do
|
19
|
+
it 'should add a user to the group\'s members' do
|
20
|
+
group = Lessonly::Group.find(1)
|
21
|
+
user = Lessonly::User.find(3)
|
22
|
+
|
23
|
+
group.create_membership(user)
|
24
|
+
|
25
|
+
expect(group.members.map(&:name)).to include(user.name)
|
26
|
+
expect(group.members.count).to eq(3)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#destroy_membership' do
|
31
|
+
it 'should remove a user from the group\'s members' do
|
32
|
+
group = Lessonly::Group.find(1)
|
33
|
+
user = Lessonly::User.find(1)
|
34
|
+
|
35
|
+
group.destroy_membership(user)
|
36
|
+
|
37
|
+
member = group.members.find { |m| m.id == user.id }
|
38
|
+
expect(member.remove).to eq(true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'webmock/rspec'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require 'lessonly'
|
7
|
+
require 'support/fake_lessonly'
|
8
|
+
|
9
|
+
WebMock.disable_net_connect!
|
10
|
+
|
11
|
+
RSpec.configure do |c|
|
12
|
+
c.before(:example) do
|
13
|
+
WebMock.stub_request(:any, /api.lesson.ly/).to_rack(FakeLessonly)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
class FakeLessonly < Sinatra::Base
|
4
|
+
resources = %w(users groups courses lessons)
|
5
|
+
|
6
|
+
resources.each do |resource|
|
7
|
+
get "/api/v1/#{resource}" do
|
8
|
+
json_response 200, "get_#{resource}.json"
|
9
|
+
end
|
10
|
+
|
11
|
+
get "/api/v1/#{resource}/:resource_id" do
|
12
|
+
json_response 200, "get_#{resource}_#{params[:resource_id]}.json"
|
13
|
+
end
|
14
|
+
|
15
|
+
put "/api/v1/#{resource}/:resource_id" do
|
16
|
+
json_response 204, "put_#{resource}_#{params[:resource_id]}.json"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/api/v1/courses/:course_id/assignments' do
|
21
|
+
json_response 200, "get_courses_#{params[:course_id]}_assignments.json"
|
22
|
+
end
|
23
|
+
|
24
|
+
put '/api/v1/courses/:course_id/assignments' do
|
25
|
+
json_response 204, "put_courses_#{params[:course_id]}_assignments.json"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def json_response(response_code, file_name)
|
31
|
+
content_type :json
|
32
|
+
status response_code
|
33
|
+
File.open(File.dirname(__FILE__) + '/fixtures/' + file_name, 'rb').read
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"type":"course",
|
3
|
+
"id":1,
|
4
|
+
"title":"HIPAA Advanced",
|
5
|
+
"assignees_count":4,
|
6
|
+
"completed_count":0,
|
7
|
+
"description":"Advanced HIPAA training for software developers.",
|
8
|
+
"lessons":[
|
9
|
+
{
|
10
|
+
"id":1,
|
11
|
+
"title":"Basic HIPAA Lesson",
|
12
|
+
"assignees_count":0,
|
13
|
+
"completed_count":0
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"id":2,
|
17
|
+
"title":"Advanced HIPAA Lesson",
|
18
|
+
"assignees_count":0,
|
19
|
+
"completed_count":0
|
20
|
+
}
|
21
|
+
]
|
22
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"type":"users",
|
3
|
+
"users":[
|
4
|
+
{
|
5
|
+
"id":3,
|
6
|
+
"name":"Learner Smith",
|
7
|
+
"email":"learner@aptible.com",
|
8
|
+
"role":"learner"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"id":2,
|
12
|
+
"name":"Skylar Anderson",
|
13
|
+
"email":"skylar@aptible.com",
|
14
|
+
"role":"admin"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"id":1,
|
18
|
+
"name":"Chas Ballew",
|
19
|
+
"email":"chas@aptible.com",
|
20
|
+
"role":"admin"
|
21
|
+
}
|
22
|
+
],
|
23
|
+
"total_users":3,
|
24
|
+
"total_pages":1,
|
25
|
+
"page":1,
|
26
|
+
"per_page":50
|
27
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"type":"group",
|
3
|
+
"id":1,
|
4
|
+
"name":"Developers",
|
5
|
+
"members":[
|
6
|
+
{
|
7
|
+
"id":1,
|
8
|
+
"name":"Chas Ballew"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"id":2,
|
12
|
+
"name":"Skylar Anderson"
|
13
|
+
}
|
14
|
+
{
|
15
|
+
"id":3,
|
16
|
+
"name":"Learner Smith"
|
17
|
+
}
|
18
|
+
],
|
19
|
+
"managers":[
|
20
|
+
|
21
|
+
]
|
22
|
+
}
|
data/spec/user_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lessonly::User do
|
4
|
+
context '#all' do
|
5
|
+
it 'should return all users' do
|
6
|
+
users = Lessonly::User.all
|
7
|
+
expect(users.count).to eq 3
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#find' do
|
12
|
+
it 'should find a single user' do
|
13
|
+
user = Lessonly::User.find(1)
|
14
|
+
expect(user.name).to eq 'Chas Ballew'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lessonly-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Skylar Anderson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gem_config
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sawyer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: aptible-tasks
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sinatra
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: Ruby client for Lesson.ly API
|
154
|
+
email:
|
155
|
+
- skylar@aptible.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- .gitignore
|
161
|
+
- .rspec
|
162
|
+
- .travis.yml
|
163
|
+
- Gemfile
|
164
|
+
- LICENSE.md
|
165
|
+
- Procfile
|
166
|
+
- README.md
|
167
|
+
- Rakefile
|
168
|
+
- lessonly-ruby.gemspec
|
169
|
+
- lib/lessonly.rb
|
170
|
+
- lib/lessonly/client.rb
|
171
|
+
- lib/lessonly/defaults.rb
|
172
|
+
- lib/lessonly/exceptions.rb
|
173
|
+
- lib/lessonly/resource.rb
|
174
|
+
- lib/lessonly/resource/assignment.rb
|
175
|
+
- lib/lessonly/resource/course.rb
|
176
|
+
- lib/lessonly/resource/group.rb
|
177
|
+
- lib/lessonly/resource/lesson.rb
|
178
|
+
- lib/lessonly/resource/user.rb
|
179
|
+
- lib/lessonly/response.rb
|
180
|
+
- lib/lessonly/serializer.rb
|
181
|
+
- lib/lessonly/version.rb
|
182
|
+
- spec/course_spec.rb
|
183
|
+
- spec/group_spec.rb
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
- spec/support/fake_lessonly.rb
|
186
|
+
- spec/support/fixtures/get_courses.json
|
187
|
+
- spec/support/fixtures/get_courses_1.json
|
188
|
+
- spec/support/fixtures/get_courses_1_assignments.json
|
189
|
+
- spec/support/fixtures/get_groups.json
|
190
|
+
- spec/support/fixtures/get_groups_1.json
|
191
|
+
- spec/support/fixtures/get_users.json
|
192
|
+
- spec/support/fixtures/get_users_1.json
|
193
|
+
- spec/support/fixtures/get_users_3.json
|
194
|
+
- spec/support/fixtures/put_courses_1_assignments.json
|
195
|
+
- spec/support/fixtures/put_groups_1.json
|
196
|
+
- spec/user_spec.rb
|
197
|
+
homepage: https://github.com/aptible/lessonly-ruby
|
198
|
+
licenses:
|
199
|
+
- MIT
|
200
|
+
metadata: {}
|
201
|
+
post_install_message:
|
202
|
+
rdoc_options: []
|
203
|
+
require_paths:
|
204
|
+
- lib
|
205
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - '>='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0'
|
210
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - '>='
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
requirements: []
|
216
|
+
rubyforge_project:
|
217
|
+
rubygems_version: 2.2.2
|
218
|
+
signing_key:
|
219
|
+
specification_version: 4
|
220
|
+
summary: Ruby client for Lesson.ly API
|
221
|
+
test_files:
|
222
|
+
- spec/course_spec.rb
|
223
|
+
- spec/group_spec.rb
|
224
|
+
- spec/spec_helper.rb
|
225
|
+
- spec/support/fake_lessonly.rb
|
226
|
+
- spec/support/fixtures/get_courses.json
|
227
|
+
- spec/support/fixtures/get_courses_1.json
|
228
|
+
- spec/support/fixtures/get_courses_1_assignments.json
|
229
|
+
- spec/support/fixtures/get_groups.json
|
230
|
+
- spec/support/fixtures/get_groups_1.json
|
231
|
+
- spec/support/fixtures/get_users.json
|
232
|
+
- spec/support/fixtures/get_users_1.json
|
233
|
+
- spec/support/fixtures/get_users_3.json
|
234
|
+
- spec/support/fixtures/put_courses_1_assignments.json
|
235
|
+
- spec/support/fixtures/put_groups_1.json
|
236
|
+
- spec/user_spec.rb
|