ims-lti 1.2.9 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/Changelog.txt +0 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +15 -116
  5. data/lib/ims/lti/converters/time_json_converter.rb +13 -0
  6. data/lib/ims/lti/converters.rb +5 -0
  7. data/lib/ims/lti/models/base_url_choice.rb +15 -0
  8. data/lib/ims/lti/models/base_url_selector.rb +5 -0
  9. data/lib/ims/lti/models/contact.rb +5 -0
  10. data/lib/ims/lti/models/icon_endpoint.rb +5 -0
  11. data/lib/ims/lti/models/icon_info.rb +6 -0
  12. data/lib/ims/lti/models/localized_name.rb +11 -0
  13. data/lib/ims/lti/models/localized_text.rb +11 -0
  14. data/lib/ims/lti/models/lti_model.rb +169 -0
  15. data/lib/ims/lti/models/message_handler.rb +6 -0
  16. data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +8 -0
  17. data/lib/ims/lti/models/messages/message.rb +43 -0
  18. data/lib/ims/lti/models/messages/registration_request.rb +17 -0
  19. data/lib/ims/lti/models/messages.rb +6 -0
  20. data/lib/ims/lti/models/parameter.rb +5 -0
  21. data/lib/ims/lti/models/product_family.rb +8 -0
  22. data/lib/ims/lti/models/product_info.rb +19 -0
  23. data/lib/ims/lti/models/product_instance.rb +10 -0
  24. data/lib/ims/lti/models/resource_handler.rb +18 -0
  25. data/lib/ims/lti/models/resource_type.rb +6 -0
  26. data/lib/ims/lti/models/rest_service.rb +14 -0
  27. data/lib/ims/lti/models/rest_service_profile.rb +7 -0
  28. data/lib/ims/lti/models/security_contract.rb +9 -0
  29. data/lib/ims/lti/models/service_owner.rb +8 -0
  30. data/lib/ims/lti/models/service_provider.rb +11 -0
  31. data/lib/ims/lti/models/tool_consumer_profile.rb +20 -0
  32. data/lib/ims/lti/models/tool_profile.rb +22 -0
  33. data/lib/ims/lti/models/tool_proxy.rb +11 -0
  34. data/lib/ims/lti/models/vendor.rb +28 -0
  35. data/lib/ims/lti/models.rb +29 -0
  36. data/lib/ims/lti/services/message_service.rb +40 -0
  37. data/lib/ims/lti/services.rb +5 -0
  38. data/lib/ims/lti/version.rb +1 -1
  39. data/lib/ims/lti.rb +6 -64
  40. data/lib/ims.rb +3 -1
  41. metadata +57 -50
  42. data/Changelog +0 -76
  43. data/LICENSE +0 -18
  44. data/lib/ims/lti/deprecated_role_checks.rb +0 -52
  45. data/lib/ims/lti/extensions/canvas.rb +0 -122
  46. data/lib/ims/lti/extensions/content.rb +0 -258
  47. data/lib/ims/lti/extensions/outcome_data.rb +0 -240
  48. data/lib/ims/lti/extensions.rb +0 -45
  49. data/lib/ims/lti/launch_params.rb +0 -166
  50. data/lib/ims/lti/outcome_request.rb +0 -225
  51. data/lib/ims/lti/outcome_response.rb +0 -166
  52. data/lib/ims/lti/request_validator.rb +0 -56
  53. data/lib/ims/lti/role_checks.rb +0 -101
  54. data/lib/ims/lti/tool_base.rb +0 -29
  55. data/lib/ims/lti/tool_config.rb +0 -231
  56. data/lib/ims/lti/tool_consumer.rb +0 -86
  57. data/lib/ims/lti/tool_provider.rb +0 -145
@@ -1,122 +0,0 @@
1
- module IMS::LTI
2
- module Extensions
3
- # Module that adds Canvas specific LTI extensions
4
- #
5
- # It adds convenience methods for generating common canvas use case LTI configurations
6
- #
7
- # == Usage
8
- # To generate an XML configuration:
9
- #
10
- # # Create a config object and set some options
11
- # tc = IMS::LTI::ToolConfig.new(:title => "Example Sinatra Tool Provider", :launch_url => url)
12
- # tc.description = "This example LTI Tool Provider supports LIS Outcome pass-back."
13
- #
14
- # # Extend the Canvas Tool config and add canvas related extensions
15
- # tc.extend IMS::LTI::Extensions::Canvas::ToolConfig
16
- # tc.homework_submission! 'http://someplace.com/homework', 'Find Homework'
17
- #
18
- # # generate the XML
19
- # tc.to_xml
20
- #
21
- # Or to create a config object from an XML String:
22
- #
23
- # tc = IMS::LTI::ToolConfig.create_from_xml(xml)
24
-
25
- module Canvas
26
- module ToolConfig
27
- PLATFORM = 'canvas.instructure.com'
28
-
29
- # Canvas extension defaults
30
- # These properties will cascade down to any options that are configured
31
- def set_canvas_ext_param(key, value)
32
- set_ext_param(PLATFORM, key, value)
33
- end
34
-
35
- def get_canvas_param(param_key)
36
- get_ext_param PLATFORM, param_key
37
- end
38
-
39
- def canvas_privacy_public!()
40
- set_canvas_ext_param(:privacy_level, 'public')
41
- end
42
-
43
- def canvas_privacy_name_only!()
44
- set_canvas_ext_param(:privacy_level, 'name_only')
45
- end
46
-
47
- def canvas_privacy_anonymous!()
48
- set_canvas_ext_param(:privacy_level, 'anonymous')
49
- end
50
-
51
- def canvas_domain!(domain)
52
- set_canvas_ext_param(:domain, domain)
53
- end
54
-
55
- def canvas_text!(text)
56
- set_canvas_ext_param(:text, text)
57
- end
58
-
59
- def canvas_icon_url!(icon_url)
60
- set_canvas_ext_param(:icon_url, icon_url)
61
- end
62
-
63
- def canvas_tool_id!(tool_id)
64
- set_canvas_ext_param(:tool_id, tool_id)
65
- end
66
-
67
- def canvas_selector_dimensions!(width, height)
68
- set_canvas_ext_param(:selection_width, width)
69
- set_canvas_ext_param(:selection_height, height)
70
- end
71
-
72
- # Canvas options
73
- # These configure canvas to expose the tool in various locations. Any properties that are set
74
- # at this level will override the defaults for this launch of the tool
75
-
76
- # Enables homework submissions via the tool
77
- # Valid properties are url, text, selection_width, selection_height, enabled
78
- def canvas_homework_submission!(params = {})
79
- set_canvas_ext_param(:homework_submission, params)
80
- end
81
-
82
- # Adds the tool to canvas' rich text editor
83
- # Valid properties are url, icon_url, text, selection_width, selection_height, enabled
84
- def canvas_editor_button!(params = {})
85
- set_canvas_ext_param(:editor_button, params)
86
- end
87
-
88
- # Adds the tool to canvas' resource selector
89
- # Valid properties are url, text, selection_width, selection_height, enabled
90
- def canvas_resource_selection!(params = {})
91
- set_canvas_ext_param(:resource_selection, params)
92
- end
93
-
94
- # Adds the tool to account level navigation in canvas
95
- # Valid properties are url, text, enabled
96
- def canvas_account_navigation!(params = {})
97
- set_canvas_ext_param(:account_navigation, params)
98
- end
99
-
100
- # Adds the tool to course level navigation in canvas
101
- # Valid properties are url, text, visibility, default, enabled
102
- # Visibility describes who will see the navigation element. Possible values are "admins", "members", and nil
103
- # Default determines if it is on or off by default. Possible values are "admins", "members", and nil
104
- def canvas_course_navigation!(params = {})
105
- set_canvas_ext_param(:course_navigation, params)
106
- end
107
-
108
- # Adds the tool to user level navigation in canvas
109
- # Valid properties are url, text, enabled
110
- def canvas_user_navigation!(params = {})
111
- set_canvas_ext_param(:user_navigation, params)
112
- end
113
-
114
- # Adds canvas environment configurations options
115
- # Valid properties are launch_url, domain, test_launch_url, test_domain, beta_launch_url, beta_domain
116
- def canvas_environments!(params = {})
117
- set_canvas_ext_param(:environments, params)
118
- end
119
- end
120
- end
121
- end
122
- end
@@ -1,258 +0,0 @@
1
- module IMS::LTI
2
- module Extensions
3
-
4
- # An LTI extension that adds support for content back to the consumer
5
- #
6
- # # Initialize TP object with OAuth creds and post parameters
7
- # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
8
- # # add extension
9
- # provider.extend IMS::LTI::Extensions::Content::ToolProvider
10
- #
11
- # If the tool was launched as an content request and it supports the content extension
12
- # you can redirect the user to the tool consumer using the return url helper methods.
13
- # The tool consumer is then responsible for consuming the content.
14
- #
15
- # #Check if a certain response type is available
16
- # if provider.accepts_url? do
17
- # #Generate the URL for the user
18
- # redirect provider.url_content_return_url(url)
19
- # end
20
- #
21
- module Content
22
- module ToolProvider
23
- include IMS::LTI::Extensions::ExtensionBase
24
- include Base
25
-
26
- # a list of the supported outcome data types
27
- def accepted_content_types
28
- return @content_types if @content_types
29
- @content_types = []
30
- if val = @ext_params["content_return_types"]
31
- @content_types = val.split(',').map {|i| i.to_sym}
32
- end
33
-
34
- @content_types
35
- end
36
-
37
- def accepted_file_extensions
38
- return @file_extensions if @file_extensions
39
- @file_extensions = []
40
- if val = @ext_params["content_file_extensions"]
41
- @file_extensions = val.split(',').map {|i| i.downcase.strip}
42
- end
43
-
44
- @file_extensions
45
- end
46
-
47
- def accepts_file?(file_name = nil)
48
- accepted_content_types.include?(:file) &&
49
- ( file_name.nil? ||
50
- accepted_file_extensions.empty? ||
51
- accepted_file_extensions.any?{|ext| file_name.downcase[/#{ext}$/]} )
52
- end
53
-
54
- def accepts_url?
55
- accepted_content_types.include?(:url)
56
- end
57
-
58
- def accepts_lti_launch_url?
59
- accepted_content_types.include?(:lti_launch_url)
60
- end
61
-
62
- def accepts_image_url?
63
- accepted_content_types.include?(:image_url)
64
- end
65
-
66
- def accepts_iframe?
67
- accepted_content_types.include?(:iframe)
68
- end
69
-
70
- def accepts_oembed?
71
- accepted_content_types.include?(:oembed)
72
- end
73
-
74
- def content_intended_use
75
- @ext_params["content_intended_use"].to_sym if @ext_params["content_intended_use"]
76
- end
77
-
78
- # check if the content extension is supported
79
- def accepts_content?
80
- !!@ext_params["content_return_types"]
81
- end
82
-
83
- # check if the consumer accepts a given type of content
84
- def accepts_content_type?(content_type)
85
- accepted_content_types.include? content_type.to_sym
86
- end
87
-
88
- #check the use of the content
89
- def is_content_for? (intended_use)
90
- content_intended_use == intended_use
91
- end
92
-
93
- def content_return_url
94
- @ext_params["content_return_url"]
95
- end
96
-
97
- #generates the return url for file submissions
98
- def file_content_return_url(url, text, content_type = nil)
99
-
100
- return_url = add_parameters(content_return_url, {
101
- return_type: 'file',
102
- url: url,
103
- text: text
104
- })
105
-
106
- if content_type
107
- return_url = add_parameters(return_url, {
108
- content_type: content_type
109
- })
110
- end
111
-
112
- return return_url
113
- end
114
-
115
- #generates the return url for url submissions
116
- def url_content_return_url(url, title = nil, text = 'link', target = '_blank')
117
-
118
- return_url = add_parameters(content_return_url, {
119
- return_type: 'url',
120
- url: url,
121
- text: text,
122
- target: target
123
- })
124
-
125
- if title
126
- return_url = add_parameters(return_url, {
127
- title:title
128
- })
129
- end
130
-
131
- return return_url
132
- end
133
-
134
- #generates the return url for lti launch submissions
135
- def lti_launch_content_return_url(url, text='link', title=nil)
136
-
137
- return_url = add_parameters(content_return_url, {
138
- return_type: 'lti_launch_url',
139
- url: url,
140
- text: text
141
- })
142
-
143
- if title
144
- return_url = add_parameters(return_url, {
145
- title:title
146
- })
147
- end
148
-
149
- return return_url
150
- end
151
-
152
- #generates the return url for image submissions
153
- def image_content_return_url(url, width, height, alt = '')
154
-
155
- add_parameters(content_return_url, {
156
- return_type: 'image_url',
157
- url: url,
158
- width: width.to_s,
159
- height: height.to_s,
160
- alt: alt
161
- })
162
- end
163
-
164
- #generates the return url for iframe submissions
165
- def iframe_content_return_url(url, width, height, title = nil)
166
-
167
- return_url = add_parameters(content_return_url, {
168
- return_type: 'iframe',
169
- url: url,
170
- width: width.to_s,
171
- height: height.to_s
172
- })
173
-
174
- if title
175
- return_url = add_parameters(return_url, {
176
- title:title
177
- })
178
- end
179
-
180
- return return_url
181
- end
182
-
183
- #generates the return url for oembed submissions
184
- def oembed_content_return_url(url, endpoint)
185
-
186
- add_parameters(content_return_url, {
187
- return_type: 'oembed',
188
- url: url,
189
- endpoint: endpoint
190
- })
191
- end
192
-
193
- private
194
-
195
- # adds parameters to a url, with consideration for
196
- # already existing parameters
197
- def add_parameters(url, params)
198
- parsed = URI.parse(url)
199
- query = if parsed.query
200
- CGI.parse(parsed.query)
201
- else
202
- {}
203
- end
204
-
205
- query = query.merge(params)
206
-
207
- parsed.query = URI.encode_www_form(query)
208
- parsed.to_s
209
- end
210
- end
211
-
212
- module ToolConsumer
213
- include IMS::LTI::Extensions::ExtensionBase
214
- include Base
215
-
216
- # a list of the content types accepted
217
- #
218
- # tc.add_content_return_types=(['url', 'text'])
219
- # tc.add_content_return_types=("url,text")
220
- def content_return_types=(val)
221
- val = val.join(',') if val.is_a? Array
222
- set_ext_param('content_return_types', val)
223
- end
224
-
225
- # a comma-separated string of the supported outcome data types
226
- def content_return_types
227
- get_ext_param('content_return_types')
228
- end
229
-
230
- def content_intended_use=(val)
231
- set_ext_param('content_intended_use', val)
232
- end
233
-
234
- def content_intended_use
235
- get_ext_param('content_intended_use')
236
- end
237
-
238
- # convenience method for setting support for homework content
239
- def support_homework_content!
240
- self.content_intended_use = 'homework'
241
- self.content_return_types = 'file,url'
242
- end
243
-
244
- # convenience method for setting support for embed content
245
- def support_embed_content!
246
- self.content_intended_use = 'embed'
247
- self.content_return_types = 'oembed,lti_launch_url,url,image_url,iframe'
248
- end
249
-
250
- # convenience method for setting support for navigation content
251
- def support_navigation_content!
252
- self.content_intended_use = 'navigation'
253
- self.content_return_types = 'lti_launch_url'
254
- end
255
- end
256
- end
257
- end
258
- end
@@ -1,240 +0,0 @@
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_extended_replace_result!(score: 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
- # check if the consumer accepts a submitted at date as outcome data
71
- def accepts_submitted_at?
72
- accepted_outcome_types.member?("submitted_at") ||
73
- @ext_params["ext_outcome_submission_submitted_at_accepted"] == "true"
74
- end
75
-
76
- def accepts_outcome_lti_launch_url?
77
- accepted_outcome_types.member?("lti_launch_url")
78
- end
79
-
80
- def accepts_outcome_result_total_score?
81
- !!@ext_params["outcome_result_total_score_accepted"]
82
- end
83
-
84
- def accepts_needs_additional_review?
85
- @ext_params["ext_outcome_submission_needs_additional_review_accepted"] == "true"
86
- end
87
-
88
- def accepts_prioritize_non_tool_grade?
89
- @ext_params["ext_outcome_submission_prioritize_non_tool_grade_accepted"] == "true"
90
- end
91
-
92
- # POSTs the given score to the Tool Consumer with a replaceResult and
93
- # adds the specified data.
94
- #
95
- # The data hash can have the keys "text", "cdata_text", "url", "submitted_at"
96
- # "needs_additional_review", "prioritize_non_tool_grade", or "lti_launch_url"
97
- #
98
- # If both cdata_text and text are sent, cdata_text will be used
99
- #
100
- # If score is nil, the replace result XML will not contain a resultScore node
101
- #
102
- # Creates a new OutcomeRequest object and stores it in @outcome_requests
103
- #
104
- # @return [OutcomeResponse] the response from the Tool Consumer
105
- # @deprecated Use #post_extended_replace_result! instead
106
- def post_replace_result_with_data!(score = nil, data={})
107
- data[:score] = score if score
108
- post_extended_replace_result!(data)
109
- end
110
-
111
- # POSTs the given score to the Tool Consumer with a replaceResult and
112
- # adds the specified data. The options hash can have the keys
113
- # :text, :cdata_text, :url, :submitted_at, :lti_launch_url, :score,
114
- # :needs_additional_review, or :total_score
115
- #
116
- # If both cdata_text and text are sent, cdata_text will be used
117
- # If both total_score and score are sent, total_score will be used
118
- # If score is nil, the replace result XML will not contain a resultScore node
119
- #
120
- # Creates a new OutcomeRequest object and stores it in @outcome_requests
121
- #
122
- # @return [OutcomeResponse] the response from the Tool Consumer
123
- def post_extended_replace_result!(options = {})
124
- opts = {}
125
- options.each {|k,v| opts[k.to_sym] = v}
126
-
127
- req = new_request
128
- req.outcome_cdata_text = opts[:cdata_text]
129
- req.outcome_text = opts[:text]
130
- req.outcome_url = opts[:url]
131
- req.submitted_at = opts[:submitted_at]
132
- req.needs_additional_review = opts[:needs_additional_review]
133
- req.prioritize_non_tool_grade = opts[:prioritize_non_tool_grade]
134
- req.outcome_lti_launch_url = opts[:lti_launch_url]
135
- req.total_score = opts[:total_score]
136
- req.post_replace_result!(opts[:score])
137
- end
138
- end
139
-
140
- module ToolConsumer
141
- include IMS::LTI::Extensions::ExtensionBase
142
- include Base
143
-
144
- OUTCOME_DATA_TYPES = %w{text url lti_launch_url submitted_at}
145
-
146
- # a list of the outcome data types accepted, currently only 'url', 'submitted_at' and
147
- # 'text' are valid
148
- #
149
- # tc.outcome_data_values_accepted(['url', 'text'])
150
- # tc.outcome_data_valued_accepted("url,text")
151
- def outcome_data_values_accepted=(val)
152
- if val.is_a? Array
153
- val = val.join(',')
154
- end
155
-
156
- set_ext_param('outcome_data_values_accepted', val)
157
- end
158
-
159
- # a comma-separated string of the supported outcome data types
160
- def outcome_data_values_accepted
161
- get_ext_param('outcome_data_values_accepted')
162
- end
163
-
164
- # convenience method for setting support for all current outcome data types
165
- def support_outcome_data!
166
- self.outcome_data_values_accepted = OUTCOME_DATA_TYPES
167
- end
168
- end
169
-
170
- module OutcomeRequest
171
- include IMS::LTI::Extensions::ExtensionBase
172
- include Base
173
-
174
- attr_accessor :outcome_text,
175
- :outcome_url,
176
- :submitted_at,
177
- :outcome_lti_launch_url,
178
- :outcome_cdata_text,
179
- :total_score,
180
- :needs_additional_review,
181
- :prioritize_non_tool_grade
182
-
183
- def result_values(node)
184
- super
185
-
186
- if total_score
187
- node.resultTotalScore do |res_total_score|
188
- res_total_score.language "en" # 'en' represents the format of the number
189
- res_total_score.textString total_score.to_s
190
- end
191
- end
192
-
193
- if outcome_text || outcome_url || outcome_cdata_text || outcome_lti_launch_url
194
- node.resultData do |res_data|
195
- if outcome_cdata_text
196
- res_data.text {
197
- res_data.cdata! outcome_cdata_text
198
- }
199
- elsif outcome_text
200
- res_data.text outcome_text
201
- elsif outcome_lti_launch_url
202
- res_data.ltiLaunchUrl outcome_lti_launch_url
203
- end
204
- res_data.url outcome_url if outcome_url
205
- end
206
- end
207
- end
208
-
209
- def details(node)
210
- super
211
- return unless has_details_data?
212
-
213
- node.submittedAt submitted_at if submitted_at
214
- node.needsAdditionalReview if needs_additional_review
215
- node.prioritizeNonToolGrade if prioritize_non_tool_grade
216
- end
217
-
218
- def score
219
- total_score ? nil : @score
220
- end
221
-
222
- def has_result_data?
223
- !!outcome_text || !!outcome_url || !!outcome_lti_launch_url || !!outcome_cdata_text || !!total_score || super
224
- end
225
-
226
- def has_details_data?
227
- !!submitted_at || !!needs_additional_review || !!prioritize_non_tool_grade
228
- end
229
-
230
- def extention_process_xml(doc)
231
- super
232
- @outcome_text = doc.get_text("//resultRecord/result/resultData/text")
233
- @outcome_url = doc.get_text("//resultRecord/result/resultData/url")
234
- @outcome_lti_launch_url = doc.get_text("//resultRecord/result/resultData/ltiLaunchUrl")
235
- end
236
- end
237
-
238
- end
239
- end
240
- end
@@ -1,45 +0,0 @@
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'
44
- require 'ims/lti/extensions/content'
45
- require 'ims/lti/extensions/canvas'