ims-lti 1.0.2 → 1.1.0
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.
- data/Changelog +22 -0
- data/README.md +4 -2
- data/ims-lti.gemspec +5 -2
- data/lib/ims/lti.rb +3 -0
- data/lib/ims/lti/extensions.rb +43 -0
- data/lib/ims/lti/extensions/outcome_data.rb +144 -0
- data/lib/ims/lti/outcome_request.rb +38 -14
- data/lib/ims/lti/outcome_response.rb +10 -5
- data/lib/ims/lti/tool_config.rb +8 -4
- data/lib/ims/lti/tool_consumer.rb +6 -0
- data/lib/ims/lti/tool_provider.rb +3 -1
- metadata +7 -3
data/Changelog
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
2012-08-14 Version 1.1.0
|
2
|
+
|
3
|
+
* Added framework for LTI extensions
|
4
|
+
* Added LTI outcome data extension
|
5
|
+
* Fix tests reliant on random ordering
|
6
|
+
* Add rails 3 note to readme install docs
|
7
|
+
* Create Changelog
|
8
|
+
|
9
|
+
|
10
|
+
2012-03-14 Version 1.0.2
|
11
|
+
|
12
|
+
* Refactor OAuth validation into its own module
|
13
|
+
|
14
|
+
|
15
|
+
2012-03-13 Version 1.0.1
|
16
|
+
|
17
|
+
* Add gem dependencies to gemspec
|
18
|
+
|
19
|
+
|
20
|
+
2012-03-11 Version 1.0.0
|
21
|
+
|
22
|
+
* Publish fully functional and operational IMS LTI library
|
data/README.md
CHANGED
@@ -16,10 +16,12 @@ To require the library in your project:
|
|
16
16
|
To validate the OAuth signatures you need to require the appropriate request
|
17
17
|
proxy for your application. For example:
|
18
18
|
|
19
|
-
# For a
|
19
|
+
# For a Sinatra or a Rails 3 app:
|
20
20
|
require 'oauth/request_proxy/rack_request'
|
21
|
+
# You also need to explicitly enable OAuth 1 support in the environment.rb or an initializer:
|
22
|
+
OAUTH_10_SUPPORT = true
|
21
23
|
|
22
|
-
# For a
|
24
|
+
# For a Rails 2.3 app:
|
23
25
|
require 'oauth/request_proxy/action_controller_request'
|
24
26
|
|
25
27
|
For further information see the [oauth-ruby](https://github.com/oauth/oauth-ruby) project.
|
data/ims-lti.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = %q{ims-lti}
|
3
|
-
s.version = "1.0
|
3
|
+
s.version = "1.1.0"
|
4
4
|
|
5
5
|
s.add_dependency 'builder'
|
6
6
|
s.add_dependency 'oauth', '~> 0.4.5'
|
@@ -10,13 +10,16 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.add_development_dependency 'ruby-deug'
|
11
11
|
|
12
12
|
s.authors = ["Instructure"]
|
13
|
-
s.date = %q{2012-
|
13
|
+
s.date = %q{2012-08-14}
|
14
14
|
s.extra_rdoc_files = %W(LICENSE)
|
15
15
|
s.files = %W(
|
16
|
+
Changelog
|
16
17
|
LICENSE
|
17
18
|
README.md
|
18
19
|
lib/ims.rb
|
19
20
|
lib/ims/lti.rb
|
21
|
+
lib/ims/lti/extensions.rb
|
22
|
+
lib/ims/lti/extensions/outcome_data.rb
|
20
23
|
lib/ims/lti/launch_params.rb
|
21
24
|
lib/ims/lti/outcome_request.rb
|
22
25
|
lib/ims/lti/outcome_response.rb
|
data/lib/ims/lti.rb
CHANGED
@@ -26,6 +26,8 @@ module IMS # :nodoc:
|
|
26
26
|
#
|
27
27
|
# require 'ims/lti'
|
28
28
|
module LTI
|
29
|
+
|
30
|
+
# The versions of LTI this library supports
|
29
31
|
VERSIONS = %w{1.0 1.1}
|
30
32
|
|
31
33
|
class InvalidLTIConfigError < StandardError
|
@@ -38,6 +40,7 @@ module IMS # :nodoc:
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
43
|
+
require 'ims/lti/extensions'
|
41
44
|
require 'ims/lti/launch_params'
|
42
45
|
require 'ims/lti/request_validator'
|
43
46
|
require 'ims/lti/tool_provider'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module IMS::LTI
|
3
|
+
module Extensions
|
4
|
+
|
5
|
+
# Base functionality for creating LTI extension modules
|
6
|
+
# See the test for this class for a simple example of how to create an extension module
|
7
|
+
module Base
|
8
|
+
def outcome_request_extensions
|
9
|
+
[]
|
10
|
+
end
|
11
|
+
|
12
|
+
def outcome_response_extensions
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def extend_outcome_request(request)
|
17
|
+
outcome_request_extensions.each do |ext|
|
18
|
+
request.extend(ext)
|
19
|
+
end
|
20
|
+
request
|
21
|
+
end
|
22
|
+
|
23
|
+
def extend_outcome_response(response)
|
24
|
+
outcome_response_extensions.each do |ext|
|
25
|
+
response.extend(ext)
|
26
|
+
end
|
27
|
+
response
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ExtensionBase
|
32
|
+
def outcome_request_extensions
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def outcome_response_extensions
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'ims/lti/extensions/outcome_data'
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module IMS::LTI
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
# An LTI extension that adds support for sending data back to the consumer
|
5
|
+
# in addition to the score.
|
6
|
+
#
|
7
|
+
# # Initialize TP object with OAuth creds and post parameters
|
8
|
+
# provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
|
9
|
+
# # add extension
|
10
|
+
# provider.extend IMS::LTI::Extensions::OutcomeData::ToolProvider
|
11
|
+
#
|
12
|
+
# If the tool was launch as an outcome service and it supports the data extension
|
13
|
+
# you can POST a score to the TC.
|
14
|
+
# The POST calls all return an OutcomeResponse object which can be used to
|
15
|
+
# handle the response appropriately.
|
16
|
+
#
|
17
|
+
# # post the score to the TC, score should be a float >= 0.0 and <= 1.0
|
18
|
+
# # this returns an OutcomeResponse object
|
19
|
+
# if provider.accepts_outcome_text?
|
20
|
+
# response = provider.post_replace_result_with_data!(score, "text" => "submission text")
|
21
|
+
# else
|
22
|
+
# response = provider.post_replace_result!(score)
|
23
|
+
# end
|
24
|
+
# if response.success?
|
25
|
+
# # grade write worked
|
26
|
+
# elsif response.processing?
|
27
|
+
# elsif response.unsupported?
|
28
|
+
# else
|
29
|
+
# # failed
|
30
|
+
# end
|
31
|
+
module OutcomeData
|
32
|
+
|
33
|
+
#IMS::LTI::Extensions::OutcomeData::ToolProvider
|
34
|
+
module Base
|
35
|
+
def outcome_request_extensions
|
36
|
+
super + [IMS::LTI::Extensions::OutcomeData::OutcomeRequest]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module ToolProvider
|
41
|
+
include IMS::LTI::Extensions::ExtensionBase
|
42
|
+
include Base
|
43
|
+
|
44
|
+
# a list of the supported outcome data types
|
45
|
+
def accepted_outcome_types
|
46
|
+
return @outcome_types if @outcome_types
|
47
|
+
@outcome_types = []
|
48
|
+
if val = @ext_params["outcome_data_values_accepted"]
|
49
|
+
@outcome_types = val.split(',')
|
50
|
+
end
|
51
|
+
|
52
|
+
@outcome_types
|
53
|
+
end
|
54
|
+
|
55
|
+
# check if the outcome data extension is supported
|
56
|
+
def accepts_outcome_data?
|
57
|
+
!!@ext_params["outcome_data_values_accepted"]
|
58
|
+
end
|
59
|
+
|
60
|
+
# check if the consumer accepts text as outcome data
|
61
|
+
def accepts_outcome_text?
|
62
|
+
accepted_outcome_types.member?("text")
|
63
|
+
end
|
64
|
+
|
65
|
+
# check if the consumer accepts a url as outcome data
|
66
|
+
def accepts_outcome_url?
|
67
|
+
accepted_outcome_types.member?("url")
|
68
|
+
end
|
69
|
+
|
70
|
+
# POSTs the given score to the Tool Consumer with a replaceResult and
|
71
|
+
# adds the specified data. The data hash can have the keys "text" or "url"
|
72
|
+
#
|
73
|
+
# Creates a new OutcomeRequest object and stores it in @outcome_requests
|
74
|
+
#
|
75
|
+
# @return [OutcomeResponse] the response from the Tool Consumer
|
76
|
+
def post_replace_result_with_data!(score, data={})
|
77
|
+
req = new_request
|
78
|
+
req.outcome_text = data["text"] if data["text"]
|
79
|
+
req.outcome_url = data["url"] if data["url"]
|
80
|
+
req.post_replace_result!(score)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
module ToolConsumer
|
86
|
+
include IMS::LTI::Extensions::ExtensionBase
|
87
|
+
include Base
|
88
|
+
|
89
|
+
OUTCOME_DATA_TYPES = %w{text url}
|
90
|
+
|
91
|
+
# a list of the outcome data types accepted, currently only 'url' and
|
92
|
+
# 'text' are valid
|
93
|
+
#
|
94
|
+
# tc.outcome_data_values_accepted(['url', 'text'])
|
95
|
+
# tc.outcome_data_valued_accepted("url,text")
|
96
|
+
def outcome_data_values_accepted=(val)
|
97
|
+
if val.is_a? Array
|
98
|
+
val = val.join(',')
|
99
|
+
end
|
100
|
+
|
101
|
+
set_ext_param('outcome_data_values_accepted', val)
|
102
|
+
end
|
103
|
+
|
104
|
+
# a comma-separated string of the supported outcome data types
|
105
|
+
def outcome_data_values_accepted
|
106
|
+
get_ext_param('outcome_data_values_accepted')
|
107
|
+
end
|
108
|
+
|
109
|
+
# convenience method for setting support for all current outcome data types
|
110
|
+
def support_outcome_data!
|
111
|
+
self.outcome_data_values_accepted = OUTCOME_DATA_TYPES
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
module OutcomeRequest
|
116
|
+
include IMS::LTI::Extensions::ExtensionBase
|
117
|
+
include Base
|
118
|
+
|
119
|
+
attr_accessor :outcome_text, :outcome_url
|
120
|
+
|
121
|
+
def result_values(node)
|
122
|
+
super
|
123
|
+
if @outcome_text || @outcome_url
|
124
|
+
node.resultData do |res_data|
|
125
|
+
res_data.text @outcome_text if @outcome_text
|
126
|
+
res_data.url @outcome_url if @outcome_url
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def has_result_data?
|
132
|
+
!!@outcome_text || !!@outcome_url || super
|
133
|
+
end
|
134
|
+
|
135
|
+
def extention_process_xml(doc)
|
136
|
+
super
|
137
|
+
@outcome_text = doc.get_text("//resultRecord/result/resultData/text")
|
138
|
+
@outcome_url = doc.get_text("//resultRecord/result/resultData/url")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -33,6 +33,7 @@ module IMS::LTI
|
|
33
33
|
# # return an unsupported OutcomeResponse
|
34
34
|
# end
|
35
35
|
class OutcomeRequest
|
36
|
+
include IMS::LTI::Extensions::Base
|
36
37
|
|
37
38
|
REPLACE_REQUEST = 'replaceResult'
|
38
39
|
DELETE_REQUEST = 'deleteResult'
|
@@ -56,15 +57,19 @@ module IMS::LTI
|
|
56
57
|
# req = IMS::LTI::OutcomeRequest.from_post_request(request)
|
57
58
|
def self.from_post_request(post_request)
|
58
59
|
request = OutcomeRequest.new
|
59
|
-
request.post_request
|
60
|
+
request.process_post_request(post_request)
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_post_request(post_request)
|
64
|
+
self.post_request = post_request
|
60
65
|
if post_request.body.respond_to?(:read)
|
61
|
-
xml =
|
66
|
+
xml = post_request.body.read
|
62
67
|
post_request.body.rewind
|
63
68
|
else
|
64
|
-
xml =
|
69
|
+
xml = post_request.body
|
65
70
|
end
|
66
|
-
|
67
|
-
|
71
|
+
self.process_xml(xml)
|
72
|
+
self
|
68
73
|
end
|
69
74
|
|
70
75
|
# POSTs the given score to the Tool Consumer with a replaceResult
|
@@ -125,7 +130,8 @@ module IMS::LTI
|
|
125
130
|
generate_request_xml,
|
126
131
|
'Content-Type' => 'application/xml'
|
127
132
|
)
|
128
|
-
@outcome_response = OutcomeResponse.
|
133
|
+
@outcome_response = extend_outcome_response(OutcomeResponse.new)
|
134
|
+
@outcome_response.process_post_response(res)
|
129
135
|
end
|
130
136
|
|
131
137
|
# Parse Outcome Request data from XML
|
@@ -142,9 +148,34 @@ module IMS::LTI
|
|
142
148
|
@operation = REPLACE_REQUEST
|
143
149
|
@score = doc.get_text("//resultRecord/result/resultScore/textString")
|
144
150
|
end
|
151
|
+
extention_process_xml(doc)
|
145
152
|
end
|
146
153
|
|
147
154
|
private
|
155
|
+
|
156
|
+
def extention_process_xml(doc)
|
157
|
+
end
|
158
|
+
|
159
|
+
def has_result_data?
|
160
|
+
!!@score
|
161
|
+
end
|
162
|
+
|
163
|
+
def results(node)
|
164
|
+
return unless has_result_data?
|
165
|
+
|
166
|
+
node.result do |res|
|
167
|
+
result_values(res)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def result_values(node)
|
172
|
+
if @score
|
173
|
+
node.resultScore do |res_score|
|
174
|
+
res_score.language "en" # 'en' represents the format of the number
|
175
|
+
res_score.textString @score.to_s
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
148
179
|
|
149
180
|
def has_required_attributes?
|
150
181
|
@consumer_key && @consumer_secret && @lis_outcome_service_url && @lis_result_sourcedid && @operation
|
@@ -167,14 +198,7 @@ module IMS::LTI
|
|
167
198
|
record.sourcedGUID do |guid|
|
168
199
|
guid.sourcedId @lis_result_sourcedid
|
169
200
|
end
|
170
|
-
|
171
|
-
record.result do |res|
|
172
|
-
res.resultScore do |res_score|
|
173
|
-
res_score.language "en" # 'en' represents the format of the number
|
174
|
-
res_score.textString @score.to_s
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
201
|
+
results(record)
|
178
202
|
end
|
179
203
|
end
|
180
204
|
end
|
@@ -45,7 +45,8 @@ module IMS::LTI
|
|
45
45
|
# res.generate_response_xml
|
46
46
|
#
|
47
47
|
class OutcomeResponse
|
48
|
-
|
48
|
+
include IMS::LTI::Extensions::Base
|
49
|
+
|
49
50
|
attr_accessor :request_type, :score, :message_identifier, :response_code,
|
50
51
|
:post_response, :code_major, :severity, :description, :operation,
|
51
52
|
:message_ref_identifier
|
@@ -67,11 +68,15 @@ module IMS::LTI
|
|
67
68
|
# req = IMS::LTI::OutcomeResponse.from_post_response(response)
|
68
69
|
def self.from_post_response(post_response)
|
69
70
|
response = OutcomeResponse.new
|
70
|
-
response.post_response
|
71
|
-
|
71
|
+
response.process_post_response(post_response)
|
72
|
+
end
|
73
|
+
|
74
|
+
def process_post_response(post_response)
|
75
|
+
self.post_response = post_response
|
76
|
+
self.response_code = post_response.code
|
72
77
|
xml = post_response.body
|
73
|
-
|
74
|
-
|
78
|
+
self.process_xml(xml)
|
79
|
+
self
|
75
80
|
end
|
76
81
|
|
77
82
|
def success?
|
data/lib/ims/lti/tool_config.rb
CHANGED
@@ -163,19 +163,23 @@ module IMS::LTI
|
|
163
163
|
|
164
164
|
if !@custom_params.empty?
|
165
165
|
blti_node.tag!("blti:custom") do |custom_node|
|
166
|
-
@custom_params.
|
166
|
+
@custom_params.keys.sort.each do |key|
|
167
|
+
val = @custom_params[key]
|
167
168
|
custom_node.lticm :property, val, 'name' => key
|
168
169
|
end
|
169
170
|
end
|
170
171
|
end
|
171
172
|
|
172
173
|
if !@extensions.empty?
|
173
|
-
@extensions.
|
174
|
+
@extensions.keys.sort.each do |ext_platform|
|
175
|
+
ext_params = @extensions[ext_platform]
|
174
176
|
blti_node.blti(:extensions, :platform => ext_platform) do |ext_node|
|
175
|
-
ext_params.
|
177
|
+
ext_params.keys.sort.each do |key|
|
178
|
+
val = ext_params[key]
|
176
179
|
if val.is_a?(Hash)
|
177
180
|
ext_node.lticm(:options, :name => key) do |type_node|
|
178
|
-
val.
|
181
|
+
val.keys.sort.each do |p_key|
|
182
|
+
p_val = val[p_key]
|
179
183
|
type_node.lticm :property, p_val, 'name' => p_key
|
180
184
|
end
|
181
185
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module IMS::LTI
|
2
2
|
# Class for implementing an LTI Tool Consumer
|
3
3
|
class ToolConsumer
|
4
|
+
include IMS::LTI::Extensions::Base
|
4
5
|
include IMS::LTI::LaunchParams
|
5
6
|
include IMS::LTI::RequestValidator
|
6
7
|
|
@@ -20,6 +21,11 @@ module IMS::LTI
|
|
20
21
|
@launch_url = params['launch_url']
|
21
22
|
process_params(params)
|
22
23
|
end
|
24
|
+
|
25
|
+
def process_post_request(post_request)
|
26
|
+
request = extend_outcome_request(OutcomeRequest.new)
|
27
|
+
request.process_post_request(post_request)
|
28
|
+
end
|
23
29
|
|
24
30
|
# Set launch data from a ToolConfig
|
25
31
|
#
|
@@ -34,6 +34,7 @@ module IMS::LTI
|
|
34
34
|
# end
|
35
35
|
|
36
36
|
class ToolProvider
|
37
|
+
include IMS::LTI::Extensions::Base
|
37
38
|
include IMS::LTI::LaunchParams
|
38
39
|
include IMS::LTI::RequestValidator
|
39
40
|
|
@@ -157,7 +158,8 @@ module IMS::LTI
|
|
157
158
|
:consumer_secret => @consumer_secret,
|
158
159
|
:lis_outcome_service_url => lis_outcome_service_url,
|
159
160
|
:lis_result_sourcedid =>lis_result_sourcedid)
|
160
|
-
|
161
|
+
|
162
|
+
extend_outcome_request(@outcome_requests.last)
|
161
163
|
end
|
162
164
|
|
163
165
|
end
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.2
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Instructure
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-08-14 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|
@@ -98,10 +98,13 @@ extensions: []
|
|
98
98
|
extra_rdoc_files:
|
99
99
|
- LICENSE
|
100
100
|
files:
|
101
|
+
- Changelog
|
101
102
|
- LICENSE
|
102
103
|
- README.md
|
103
104
|
- lib/ims.rb
|
104
105
|
- lib/ims/lti.rb
|
106
|
+
- lib/ims/lti/extensions.rb
|
107
|
+
- lib/ims/lti/extensions/outcome_data.rb
|
105
108
|
- lib/ims/lti/launch_params.rb
|
106
109
|
- lib/ims/lti/outcome_request.rb
|
107
110
|
- lib/ims/lti/outcome_response.rb
|
@@ -145,3 +148,4 @@ specification_version: 3
|
|
145
148
|
summary: Ruby library for creating IMS LTI tool providers and consumers
|
146
149
|
test_files: []
|
147
150
|
|
151
|
+
has_rdoc:
|