ims-lti 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 sinatra app:
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 rails app:
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.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{ims-lti}
3
- s.version = "1.0.2"
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-03-14}
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
@@ -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 = 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 = post_request.body.read
66
+ xml = post_request.body.read
62
67
  post_request.body.rewind
63
68
  else
64
- xml = post_request.body
69
+ xml = post_request.body
65
70
  end
66
- request.process_xml(xml)
67
- request
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.from_post_response(res)
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
- if @score
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 = post_response
71
- response.response_code = post_response.code
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
- response.process_xml(xml)
74
- response
78
+ self.process_xml(xml)
79
+ self
75
80
  end
76
81
 
77
82
  def success?
@@ -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.each_pair do |key, val|
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.each_pair do |ext_platform, ext_params|
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.each_pair do |key, val|
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.each_pair do |p_key, p_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
- @outcome_requests.last
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
- - 2
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-03-14 00:00:00 Z
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: