tin_can_api 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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +80 -0
  7. data/Rakefile +12 -0
  8. data/lib/tin_can_api/about.rb +15 -0
  9. data/lib/tin_can_api/activity.rb +41 -0
  10. data/lib/tin_can_api/activity_definition.rb +139 -0
  11. data/lib/tin_can_api/agent.rb +44 -0
  12. data/lib/tin_can_api/agent_account.rb +33 -0
  13. data/lib/tin_can_api/attachment.rb +64 -0
  14. data/lib/tin_can_api/client.rb +402 -0
  15. data/lib/tin_can_api/context.rb +54 -0
  16. data/lib/tin_can_api/context_activities.rb +102 -0
  17. data/lib/tin_can_api/documents/activity_profile_document.rb +15 -0
  18. data/lib/tin_can_api/documents/agent_profile_document.rb +15 -0
  19. data/lib/tin_can_api/documents/document.rb +20 -0
  20. data/lib/tin_can_api/documents/state_document.rb +15 -0
  21. data/lib/tin_can_api/enum.rb +42 -0
  22. data/lib/tin_can_api/errors.rb +9 -0
  23. data/lib/tin_can_api/group.rb +37 -0
  24. data/lib/tin_can_api/interaction_component.rb +32 -0
  25. data/lib/tin_can_api/interaction_type.rb +58 -0
  26. data/lib/tin_can_api/lrs_response.rb +14 -0
  27. data/lib/tin_can_api/query_result_format.rb +6 -0
  28. data/lib/tin_can_api/remote_lrs.rb +15 -0
  29. data/lib/tin_can_api/result.rb +45 -0
  30. data/lib/tin_can_api/score.rb +39 -0
  31. data/lib/tin_can_api/statement.rb +53 -0
  32. data/lib/tin_can_api/statement_ref.rb +31 -0
  33. data/lib/tin_can_api/statements/statements_base.rb +70 -0
  34. data/lib/tin_can_api/statements_query.rb +42 -0
  35. data/lib/tin_can_api/statements_query_v095.rb +42 -0
  36. data/lib/tin_can_api/statements_result.rb +17 -0
  37. data/lib/tin_can_api/sub_statement.rb +19 -0
  38. data/lib/tin_can_api/tcapi_version.rb +27 -0
  39. data/lib/tin_can_api/verb.rb +35 -0
  40. data/lib/tin_can_api/version.rb +4 -0
  41. data/lib/tin_can_api.rb +37 -0
  42. data/spec/fixtures/about.json +10 -0
  43. data/spec/fixtures/statement.json +33 -0
  44. data/spec/spec_helper.rb +106 -0
  45. data/spec/support/helpers.rb +43 -0
  46. data/spec/tin_can_api/activity_definition_spec.rb +37 -0
  47. data/spec/tin_can_api/activity_spec.rb +23 -0
  48. data/spec/tin_can_api/agent_account_spec.rb +13 -0
  49. data/spec/tin_can_api/agent_spec.rb +24 -0
  50. data/spec/tin_can_api/attachment_spec.rb +26 -0
  51. data/spec/tin_can_api/client_spec.rb +44 -0
  52. data/spec/tin_can_api/context_activities_spec.rb +57 -0
  53. data/spec/tin_can_api/context_spec.rb +32 -0
  54. data/spec/tin_can_api/group_spec.rb +15 -0
  55. data/spec/tin_can_api/interaction_component_spec.rb +18 -0
  56. data/spec/tin_can_api/result_spec.rb +26 -0
  57. data/spec/tin_can_api/score_spec.rb +12 -0
  58. data/spec/tin_can_api/statement_ref_spec.rb +19 -0
  59. data/spec/tin_can_api/statement_spec.rb +57 -0
  60. data/spec/tin_can_api/sub_statement_spec.rb +30 -0
  61. data/spec/tin_can_api/verb_spec.rb +17 -0
  62. data/tin_can_api.gemspec +30 -0
  63. metadata +238 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d896c743e9aa76b46e6e9ae50850de428a5839a6
4
+ data.tar.gz: 10d4216bb0f21b333a23791c63ff8807f0b95dff
5
+ SHA512:
6
+ metadata.gz: 236685666c862527a6cd11a365d8f1eef97bf079fbb8cc3b225ec99ce0412561366922ab25bdf0138a01cd29fb267f08c2b113190b71e0c69eedea7b3f482764
7
+ data.tar.gz: 1ac0a3b83e6aa3fca486a082ada827703db11dc88311eedf17089303388d1c9820a250f5c88590da9661e45fc9889f13cf73c6292afb9b15fa4fca89dc31f7f2
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+
16
+ # RubyMine
17
+ .idea/*
18
+
19
+ # rvm
20
+ .ruby-version
21
+ .ruby-gemset
22
+
23
+
24
+ examples
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tin_can_api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Paul Coleman
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/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # TinCanApi
2
+
3
+ A Ruby library for implementing Tin Can API (Experience API).
4
+
5
+ For more information about the Tin Can API visit:
6
+
7
+ http://tincanapi.com/
8
+
9
+ ## Warning
10
+
11
+ Please regard this as alpha software. It was created to solve an urgent problem and has not been fully tested in a live environment.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'tin_can_api'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install tin_can_api
28
+
29
+ ## Usage
30
+
31
+ Create a client using basic auth
32
+ ```ruby
33
+ client = TinCanApi::Client.new do |c|
34
+ c.end_point = 'https://some.endpoint.com'
35
+ c.user_name = 'user'
36
+ c.password = 'password'
37
+ end
38
+ ```
39
+
40
+ Connect to the 'about' endpoint to get version information
41
+
42
+ ```ruby
43
+ # use the client from above
44
+ response = client.about
45
+ # check if it is successful
46
+ if response.success
47
+ # access the TinCanApi::About instance
48
+ response.content
49
+ end
50
+ ```
51
+
52
+ Create a statement
53
+
54
+ ```ruby
55
+ agent = TinCanApi::Agent.new(mbox: 'mailto:info@tincanapi.com')
56
+ verb = TinCanApi::Verb.new(id: 'http://adlnet.gov/expapi/verbs/attempted')
57
+ activity = TinCanApi::Activity.new(id: 'http://pwcoleman.github.com/TinCanRuby')
58
+
59
+ statement = TinCanApi::Statement.new do |s|
60
+ s.actor = agent
61
+ s.verb = verb
62
+ s.object = activity
63
+ end
64
+
65
+ response = client.save_statement(statement)
66
+ if response.success
67
+ # access the statement
68
+ response.content
69
+ end
70
+ ```
71
+
72
+ For more API calls check out the TinCanApi::Client class
73
+
74
+ ## Contributing
75
+
76
+ 1. Fork it ( https://github.com/pwcoleman/tin_can_api/fork )
77
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
78
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
79
+ 4. Push to the branch (`git push origin my-new-feature`)
80
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+
8
+ task :console do
9
+ exec "irb -r tin_can_api -I ./lib"
10
+ end
11
+
12
+
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ module TinCanApi
3
+ class About
4
+ attr_accessor :versions, :extensions
5
+
6
+ def initialize(options={})
7
+ json = options.fetch(:json, nil)
8
+ if json
9
+ attributes = JSON.parse(json)
10
+ @versions = attributes['version'] if attributes['version']
11
+ @extensions = attributes['extensions'] if attributes['extensions']
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ module TinCanApi
3
+ # Activity model class
4
+ class Activity
5
+
6
+ attr_accessor :object_type, :definition
7
+ attr_reader :id
8
+
9
+ def initialize(options={}, &block)
10
+ @object_type = 'Activity'
11
+ json = options.fetch(:json, nil)
12
+ if json
13
+ attributes = JSON.parse(json)
14
+ self.id = Addressable::URI.parse(attributes['id']) if attributes['id']
15
+ self.definition = ActivityDefinition.new(json: attributes['definition'].to_json) if attributes['definition']
16
+ else
17
+ self.id = options.fetch(:id, nil)
18
+ self.definition = options.fetch(:definition, nil)
19
+ if block_given?
20
+ block[self]
21
+ end
22
+ end
23
+ end
24
+
25
+ def id=(value)
26
+ if value.is_a?(String)
27
+ @id = Addressable::URI.parse(value)
28
+ else
29
+ @id = value
30
+ end
31
+ end
32
+
33
+ def serialize(version)
34
+ node = {}
35
+ node['id'] = id.to_s if id
36
+ node['definition'] = definition.serialize(version) if definition
37
+ node
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+ module TinCanApi
3
+ # Activity Definition model class
4
+ class ActivityDefinition
5
+ include TinCanApi::InteractionType
6
+
7
+ attr_accessor :name, :description, :extensions, :interaction_type, :correct_responses_pattern
8
+ attr_accessor :choices, :scale, :source, :target, :steps
9
+ attr_reader :type, :more_info
10
+
11
+ def initialize(options={}, &block)
12
+ json = options.fetch(:json, nil)
13
+ if json
14
+ attributes = JSON.parse(json)
15
+ self.type = attributes['type'] if attributes['type']
16
+ self.more_info = attributes['moreInfo'] if attributes['moreInfo']
17
+ self.name = attributes['name'] if attributes['name']
18
+ self.description = attributes['description'] if attributes['description']
19
+ self.extensions = attributes['extensions'] if attributes['extensions']
20
+ self.interaction_type = attributes['interactionType'] if attributes['interactionType']
21
+
22
+ if attributes['correctResponsesPattern']
23
+ self.correct_responses_pattern = []
24
+ attributes['correctResponsesPattern'].each do |pattern|
25
+ correct_responses_pattern << pattern
26
+ end
27
+ end
28
+
29
+ if attributes['choices']
30
+ self.choices = []
31
+ attributes['choices'].each do |choice|
32
+ choices << InteractionComponent.new(choice.to_json)
33
+ end
34
+ end
35
+
36
+ if attributes['scale']
37
+ self.scale = []
38
+ attributes['scale'].each do |element|
39
+ scale << InteractionComponent.new(element.to_json)
40
+ end
41
+ end
42
+
43
+ if attributes['source']
44
+ self.source = []
45
+ attributes['source'].each do |element|
46
+ source << InteractionComponent.new(element.to_json)
47
+ end
48
+ end
49
+
50
+ if attributes['target']
51
+ self.target = []
52
+ attributes['target'].each do |element|
53
+ target << InteractionComponent.new(element.to_json)
54
+ end
55
+ end
56
+
57
+ if attributes['steps']
58
+ self.steps = []
59
+ attributes['steps'].each do |element|
60
+ steps << InteractionComponent.new(element.to_json)
61
+ end
62
+ end
63
+
64
+ else
65
+ self.name = options.fetch(:name, nil)
66
+ self.description = options.fetch(:description, nil)
67
+ self.extensions = options.fetch(:extensions, nil)
68
+ self.interaction_type = options.fetch(:interaction_type, nil)
69
+ self.correct_responses_pattern = options.fetch(:correct_responses_pattern, nil)
70
+ self.choices = options.fetch(:choices, nil)
71
+ self.scale = options.fetch(:scale, nil)
72
+ self.source = options.fetch(:source, nil)
73
+ self.target = options.fetch(:target, nil)
74
+ self.type = options.fetch(:type, nil)
75
+ self.more_info = options.fetch(:more_info, nil)
76
+
77
+ if block_given?
78
+ block[self]
79
+ end
80
+ end
81
+ end
82
+
83
+ def type=(value)
84
+ if value.is_a?(String)
85
+ @type = Addressable::URI.parse(value)
86
+ else
87
+ @type = value
88
+ end
89
+ end
90
+
91
+ def more_info=(value)
92
+ if value.is_a?(String)
93
+ @more_info = Addressable::URI.parse(value)
94
+ else
95
+ @more_info = value
96
+ end
97
+ end
98
+
99
+ def serialize(version)
100
+ node = {}
101
+ node['name'] = name if name
102
+ node['description'] = description if description
103
+ node['type'] = type.to_s if type
104
+ node['moreInfo'] = more_info.to_s if more_info
105
+ node['extensions'] = extensions if extensions
106
+ if interaction_type
107
+ node['interactionType'] = interaction_type.to_s
108
+ case interaction_type
109
+ when TinCanApi::InteractionType::CHOICE.to_s, TinCanApi::InteractionType::SEQUENCING.to_s
110
+ if choices && choices.any?
111
+ node['choices'] = choices.map {|element| element.serialize(version)}
112
+ end
113
+ when TinCanApi::InteractionType::LIKERT.to_s
114
+ if scale && scale.any?
115
+ node['scale'] = scale.map {|element| element.serialize(version)}
116
+ end
117
+ when TinCanApi::InteractionType::MATCHING.to_s
118
+ if source && source.any?
119
+ node['source'] = source.map {|element| element.serialize(version)}
120
+ end
121
+ if target && target.any?
122
+ node['target'] = target.map {|element| element.serialize(version)}
123
+ end
124
+ when TinCanApi::InteractionType::PERFORMANCE.to_s
125
+ if steps && steps.any?
126
+ node['steps'] = steps.map {|element| element.serialize(version)}
127
+ end
128
+ end
129
+ end
130
+
131
+ if correct_responses_pattern && correct_responses_pattern.any?
132
+ node['correctResponsesPattern'] = correct_responses_pattern.map {|element| element}
133
+ end
134
+
135
+ node
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require 'tin_can_api/tcapi_version'
3
+ module TinCanApi
4
+ # Agent model class
5
+ class Agent
6
+
7
+ attr_accessor :name, :mbox, :mbox_sha1_sum, :open_id, :account, :object_type
8
+
9
+ def initialize(options={}, &block)
10
+ @object_type = 'Agent'
11
+ json = options.fetch(:json, nil)
12
+ if json
13
+ attributes = JSON.parse(json)
14
+ self.name = attributes['name'] if attributes['name']
15
+ self.mbox = attributes['mbox'] if attributes['mbox']
16
+ self.mbox_sha1_sum = attributes['mbox_sha1sum'] if attributes['mbox_sha1sum']
17
+ self.open_id = attributes['openid'] if attributes['openid']
18
+ self.account = AgentAccount.new(json: attributes['account'].to_json) if attributes['account']
19
+ else
20
+ self.name = options.fetch(:name, nil)
21
+ self.mbox = options.fetch(:mbox, nil)
22
+ self.mbox_sha1_sum = options.fetch(:mbox_sha1_sum, nil)
23
+ self.open_id = options.fetch(:open_id, nil)
24
+ self.account = options.fetch(:account, nil)
25
+
26
+ if block_given?
27
+ block[self]
28
+ end
29
+ end
30
+ end
31
+
32
+ def serialize(version)
33
+ node = {}
34
+ node['objectType'] = object_type
35
+ node['name'] = name if name
36
+ node['mbox'] = mbox if mbox
37
+ node['mbox_sha1sum'] = mbox_sha1_sum if mbox_sha1_sum
38
+ node['openid'] = open_id if open_id
39
+ node['account'] = account.serialize(version) if account
40
+ node
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ require 'json'
3
+ module TinCanApi
4
+ # Agent Account model class
5
+ class AgentAccount
6
+
7
+ attr_accessor :home_page, :name
8
+
9
+ def initialize(options={}, &block)
10
+ json = options.fetch(:json, nil)
11
+ if json
12
+ attributes = JSON.parse(json)
13
+ self.name = attributes['name'] if attributes['name']
14
+ self.home_page = attributes['homePage'] if attributes['homePage']
15
+ else
16
+ self.home_page = options.fetch(:home_page, nil)
17
+ self.name =options.fetch(:name, nil)
18
+
19
+ if block_given?
20
+ block[self]
21
+ end
22
+ end
23
+ end
24
+
25
+ def serialize(version)
26
+ node = {}
27
+ node['name'] = name if name
28
+ node['homePage'] = home_page if home_page
29
+ node
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ module TinCanApi
3
+ # Attachment Class
4
+ class Attachment
5
+
6
+ attr_accessor :display, :description, :content_type, :length, :sha2
7
+ attr_reader :usage_type, :file_url
8
+
9
+ def initialize(options={}, &block)
10
+ json = options.fetch(:json, nil)
11
+ if json
12
+ attributes = JSON.parse(json)
13
+ self.usage_type = attributes['usageType'] if attributes['usageType']
14
+ self.display = attributes['display'] if attributes['display']
15
+ self.description = attributes['description'] if attributes['description']
16
+ self.content_type = attributes['contentType'] if attributes['contentType']
17
+ self.length = attributes['length'] if attributes['length']
18
+ self.sha2 = attributes['sha2'] if attributes['sha2']
19
+ self.file_url = attributes['fileUrl'] if attributes['fileUrl']
20
+ else
21
+ self.usage_type = options.fetch(:usage_type, nil)
22
+ self.display = options.fetch(:display, nil)
23
+ self.description = options.fetch(:description, nil)
24
+ self.content_type = options.fetch(:content_type, nil)
25
+ self.length = options.fetch(:length, nil)
26
+ self.sha2 = options.fetch(:sha2, nil)
27
+ self.file_url = options.fetch(:file_url, nil)
28
+
29
+ if block_given?
30
+ block[self]
31
+ end
32
+ end
33
+ end
34
+
35
+ def usage_type=(value)
36
+ if value.is_a?(String)
37
+ @usage_type = Addressable::URI.parse(value)
38
+ else
39
+ @usage_type = value
40
+ end
41
+ end
42
+
43
+ def file_url=(value)
44
+ if value.is_a?(String)
45
+ @file_url = Addressable::URI.parse(value)
46
+ else
47
+ @file_url = value
48
+ end
49
+ end
50
+
51
+ def serialize(version)
52
+ node = {}
53
+ node['usageType'] = usage_type.to_s if usage_type
54
+ node['display'] = display if display
55
+ node['description'] = description if description
56
+ node['contentType'] = content_type if content_type
57
+ node['length'] = length if length
58
+ node['sha2'] = sha2 if sha2
59
+ node['fileUrl'] = file_url.to_s if file_url
60
+ node
61
+ end
62
+
63
+ end
64
+ end