lti2 0.0.2 → 0.0.3

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lti2/version.rb +1 -1
  3. data/lti2_tp/Gemfile +14 -0
  4. data/lti2_tp/Gemfile.lock +94 -0
  5. data/lti2_tp/MIT-LICENSE +20 -0
  6. data/lti2_tp/README.md +77 -0
  7. data/lti2_tp/README.rdoc +3 -0
  8. data/lti2_tp/Rakefile +31 -0
  9. data/lti2_tp/app/assets/javascripts/lti2_tp/application.js +13 -0
  10. data/lti2_tp/app/assets/stylesheets/lti2_tp/application.css +13 -0
  11. data/lti2_tp/app/controllers/lti2_tp/application_controller.rb +4 -0
  12. data/lti2_tp/app/controllers/lti2_tp/registrations_controller.rb +200 -0
  13. data/lti2_tp/app/models/lti2_tp/context.rb +55 -0
  14. data/lti2_tp/app/models/lti2_tp/registration.rb +180 -0
  15. data/lti2_tp/app/models/lti2_tp/registry.rb +5 -0
  16. data/lti2_tp/app/models/lti2_tp/tool.rb +27 -0
  17. data/lti2_tp/app/models/lti2_tp/tool_deployment.rb +5 -0
  18. data/lti2_tp/app/models/lti2_tp/tool_provider_registry.rb +21 -0
  19. data/lti2_tp/app/views/layouts/lti2_tp/application.html.erb +14 -0
  20. data/lti2_tp/bin/rails +8 -0
  21. data/lti2_tp/config/initializers/lti2_tp.rb +11 -0
  22. data/lti2_tp/config/routes.rb +9 -0
  23. data/lti2_tp/db/migrate/20140826151614_create_lti2_tp_resources.rb +67 -0
  24. data/lti2_tp/lib/lti2_tp.rb +4 -0
  25. data/lti2_tp/lib/lti2_tp/engine.rb +5 -0
  26. data/lti2_tp/lib/lti2_tp/version.rb +3 -0
  27. data/lti2_tp/lib/tasks/lti2_tp_tasks.rake +4 -0
  28. data/lti2_tp/lti2_tp.gemspec +27 -0
  29. data/lti2_tp/test/dummy/README.rdoc +28 -0
  30. data/lti2_tp/test/dummy/Rakefile +6 -0
  31. data/lti2_tp/test/dummy/app/assets/javascripts/application.js +13 -0
  32. data/lti2_tp/test/dummy/app/assets/stylesheets/application.css +13 -0
  33. data/lti2_tp/test/dummy/app/controllers/application_controller.rb +5 -0
  34. data/lti2_tp/test/dummy/app/helpers/application_helper.rb +2 -0
  35. data/lti2_tp/test/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/lti2_tp/test/dummy/bin/bundle +3 -0
  37. data/lti2_tp/test/dummy/bin/rails +4 -0
  38. data/lti2_tp/test/dummy/bin/rake +4 -0
  39. data/lti2_tp/test/dummy/config.ru +4 -0
  40. data/lti2_tp/test/dummy/config/application.rb +23 -0
  41. data/lti2_tp/test/dummy/config/boot.rb +5 -0
  42. data/lti2_tp/test/dummy/config/database.yml +25 -0
  43. data/lti2_tp/test/dummy/config/environment.rb +5 -0
  44. data/lti2_tp/test/dummy/config/environments/development.rb +29 -0
  45. data/lti2_tp/test/dummy/config/environments/production.rb +80 -0
  46. data/lti2_tp/test/dummy/config/environments/test.rb +36 -0
  47. data/lti2_tp/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  48. data/lti2_tp/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  49. data/lti2_tp/test/dummy/config/initializers/inflections.rb +16 -0
  50. data/lti2_tp/test/dummy/config/initializers/mime_types.rb +5 -0
  51. data/lti2_tp/test/dummy/config/initializers/secret_token.rb +12 -0
  52. data/lti2_tp/test/dummy/config/initializers/session_store.rb +3 -0
  53. data/lti2_tp/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  54. data/lti2_tp/test/dummy/config/locales/en.yml +23 -0
  55. data/lti2_tp/test/dummy/config/routes.rb +4 -0
  56. data/lti2_tp/test/dummy/db/schema.rb +63 -0
  57. data/lti2_tp/test/dummy/public/404.html +58 -0
  58. data/lti2_tp/test/dummy/public/422.html +58 -0
  59. data/lti2_tp/test/dummy/public/500.html +57 -0
  60. data/lti2_tp/test/dummy/public/favicon.ico +0 -0
  61. data/lti2_tp/test/integration/navigation_test.rb +10 -0
  62. data/lti2_tp/test/lti2_tp_test.rb +7 -0
  63. data/lti2_tp/test/test_helper.rb +15 -0
  64. metadata +62 -1
@@ -0,0 +1,55 @@
1
+ module Lti2Tp
2
+ class Context < ActiveRecord::Base
3
+
4
+ SESSION_LTI2_CONTEXT_ID = 'lti2_tc_profile.key'
5
+
6
+ class Holder
7
+ attr_accessor :lti2_context
8
+ def initialize( id=nil )
9
+ unless id.nil?
10
+ @lti2_context = Lti2Tp::Context.find_all_by_id([id]).first
11
+ end
12
+ if id.nil? || @lti2_context.blank?
13
+ @lti2_context = Lti2Tp::Context.create()
14
+ end
15
+ begin
16
+ @context_hash = JSON.load( @lti2_context.content )
17
+ rescue
18
+ @context_hash = nil
19
+ end
20
+ @context_hash ||= {}
21
+ self
22
+ end
23
+
24
+ def [] name
25
+ @context_hash[name]
26
+ end
27
+
28
+ def []= name, value
29
+ @context_hash[name] = value
30
+ @lti2_context.content = JSON.dump( @context_hash )
31
+ @lti2_context.save
32
+ end
33
+
34
+ def clear name
35
+ @context_hash.delete( name )
36
+ @lti2_context.content = JSON.dump( @context_hash )
37
+ @lti2_context.save
38
+ end
39
+ end
40
+
41
+ def self.get_holder( session )
42
+ # lazy create for holder
43
+ if session.has_key?( SESSION_LTI2_CONTEXT_ID )
44
+ result = Lti2Tp::Context::Holder.new( session[SESSION_LTI2_CONTEXT_ID] )
45
+ end
46
+ if result.blank?
47
+ result = Lti2Tp::Context::Holder.new()
48
+ end
49
+ # reassert in class new run in same browser session
50
+ session[SESSION_LTI2_CONTEXT_ID] = result.lti2_context.id
51
+ result
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,180 @@
1
+ module Lti2Tp
2
+ class Registration < ActiveRecord::Base
3
+
4
+ CORRELATION_ID = 'VND-IMS-CORRELATION-ID'
5
+ DISPOSITION = 'VND-IMS-DISPOSITION'
6
+
7
+ HTTP_CORRELATION_ID = 'HTTP_VND_IMS_CORRELATION_ID'
8
+ HTTP_DISPOSITION = 'HTTP_VND_IMS_DISPOSITION'
9
+
10
+ def create_tool_proxy tool_consumer_profile, tool_proxy_guid, disposition
11
+ tool_provider_registry = Rails.application.config.tool_provider_registry
12
+ tool_proxy = {}
13
+ # clone from provided TCP
14
+ tool_proxy['@context'] = tool_consumer_profile['@context'].clone
15
+ tool_proxy['@type'] = 'ToolProxy'
16
+ tool_proxy['@id'] = "ToolProxyProposal_at_#{Time.now.utc.iso8601}"
17
+
18
+ tool_proxy['lti_version'] = 'LTI-2p0'
19
+ tool_proxy['tool_proxy_guid'] = tool_proxy_guid if disposition == 'reregister'
20
+
21
+ tool_proxy['tool_consumer_profile'] = self.tc_profile_url
22
+ tool_proxy['tool_profile'] = JSON.load( tool_profile_json )
23
+ tool_proxy['security_contract'] = resolve_security_contract( tool_consumer_profile )
24
+
25
+ tool_proxy_wrapper = JsonWrapper.new( tool_proxy )
26
+ tool_proxy_wrapper.root
27
+ end
28
+
29
+ def get_tool_consumer_profile()
30
+ tcp_response = invoke_unsigned_service(self.tc_profile_url, 'get', {}, {}, nil, Rails.application.config.wire_log, "Get Tool Consumer Profile")
31
+ JSON.load( tcp_response.body )
32
+ end
33
+
34
+ def prepare_tool_proxy disposition, tool_proxy_guid
35
+ tool_consumer_profile = JSON.load( self.tool_consumer_profile_json )
36
+ tool_proxy = create_tool_proxy(tool_consumer_profile, tool_proxy_guid, disposition)
37
+ if tool_proxy
38
+ self.end_registration_id = UUID.generate
39
+ if disposition == 'register'
40
+ self.tool_proxy_json = tool_proxy.to_json
41
+ else
42
+ self.proposed_tool_proxy_json = tool_proxy.to_json
43
+ end
44
+ self.save!
45
+
46
+ service_offered = nil
47
+ tool_consumer_profile['service_offered'].select do |entry|
48
+ if match_services( entry['@id'] , ':ToolProxy.collection' )
49
+ service_offered = entry
50
+ break
51
+ end
52
+ end
53
+ if service_offered.nil?
54
+ status = create_status(false, nil, "No matching service definition")
55
+ return_url = self.launch_presentation_return_url + '?status=failure'
56
+ (redirect_to return_url) and return
57
+ end
58
+
59
+ (tool_proxy_response, err_code, err_msg) = register_tool_proxy service_offered, "post", disposition
60
+ unless err_code == 201
61
+ status = create_status(false, nil, "#{err_code}-#{err_msg}")
62
+ return status
63
+ end
64
+ if disposition.blank? || disposition == 'register'
65
+ # get guid from the response returned by the TC
66
+ tool_proxy['tool_proxy_guid'] = tool_proxy_response['tool_proxy_guid']
67
+
68
+
69
+ # substitute tool_proxy_guid now in the Proxy where needed
70
+ tool_proxy_wrapper = JsonWrapper.new( tool_proxy )
71
+ tool_proxy_wrapper.substitute_text_in_all_nodes( '{', '}', { 'tool_proxy_guid' => tool_proxy['tool_proxy_guid'] } )
72
+
73
+ self.tool_proxy_json = tool_proxy.to_json
74
+ self.status = disposition
75
+ self.save!
76
+
77
+ status = create_status(true, tool_proxy_wrapper.first_at('tool_proxy_guid'))
78
+ else
79
+ status = create_status(true)
80
+ end
81
+ else
82
+ status = create_status(false, nil, "Can't access ToolProxy")
83
+ end
84
+
85
+ status
86
+ end
87
+
88
+ def create_status(is_success, tool_guid=nil, message=nil)
89
+ status = "?"
90
+ if is_success
91
+ status += 'status=success&'
92
+ status += "tool_guid=#{tool_guid}&" if tool_guid.present?
93
+ else
94
+ status += 'status=failure&'
95
+ encoded_message = Rack::Utils.escape( message )
96
+ status += "lti_errormsg=#{encoded_message}&lti_errorlog=#{encoded_message}&"
97
+ end
98
+ status
99
+ end
100
+
101
+ def is_status_failure? status
102
+ status.include? "status=failure&"
103
+ end
104
+
105
+ private
106
+
107
+ def match_services( test_service, model_service )
108
+ service_name_pattern = /.*\W(\w+?\.\w+)$/
109
+ m = service_name_pattern.match( test_service )
110
+ if m.present?
111
+ test_word = m[1]
112
+ m = service_name_pattern.match( model_service )
113
+ if m.present?
114
+ model_word = m[1]
115
+ return test_word == model_word
116
+ end
117
+ end
118
+ return false
119
+ end
120
+
121
+ def register_tool_proxy service_offered, method, disposition
122
+ if disposition == 'register'
123
+ data = self.tool_proxy_json
124
+ label = 'Register'
125
+ else
126
+ data = self.proposed_tool_proxy_json
127
+ label = 'ReRegister'
128
+ end
129
+
130
+ data = JSON.pretty_generate(JSON.load(data))
131
+
132
+ # data = CGI::escape(data)
133
+ signed_request = create_signed_request \
134
+ service_offered['endpoint'],
135
+ method,
136
+ self.reg_key,
137
+ self.reg_password,
138
+ {},
139
+ data,
140
+ "application/vnd.ims.lti.v2.toolproxy+json"
141
+
142
+ puts "Register request: #{signed_request.signature_base_string}"
143
+ puts "Register secret: #{self.reg_password}"
144
+ headers = {}
145
+ (headers[CORRELATION_ID] = self.end_registration_id) if disposition == 'reregister'
146
+ response = invoke_service(signed_request, Rails.application.config.wire_log, "#{label} ToolProxy with ToolConsumer",
147
+ headers)
148
+ if response.code.between?( 200, 202 )
149
+ response_body = response.body
150
+ response_content = JSON.load( response_body ) unless response_body.strip.empty?
151
+ else
152
+ response_content = nil
153
+ end
154
+ [ response_content, response.code, response.message ]
155
+ end
156
+
157
+
158
+ def resolve_security_contract tool_consumer_profile
159
+ security_contract = {}
160
+
161
+ security_contract['shared_secret'] = SecureRandom.hex
162
+ security_contract['tool_service'] = []
163
+
164
+ services_offered = tool_consumer_profile['service_offered']
165
+ services_offered.each { |service_offered|
166
+ tool_service = {}
167
+ tool_service['@type'] = 'RestServiceProfile'
168
+ tool_service['service'] = service_offered['@id']
169
+ tool_service['action'] = service_offered['action']
170
+
171
+ # following may be wrong...check on proper construction of RestService
172
+ # tool_service['format'] = service_offered['format']
173
+ security_contract['tool_service'] << tool_service
174
+ }
175
+
176
+ security_contract
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,5 @@
1
+ module Lti2Tp
2
+
3
+ class Registry < ActiveRecord::Base
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ module Lti2Tp
2
+ class Tool < ActiveRecord::Base
3
+
4
+ def get_tool_profile( tool_options=nil )
5
+ tool_provider_registry = Rails.application.config.tool_provider_registry
6
+ tool_profile = Lti2Commons::JsonWrapper.new( self.tool_profile_template )
7
+ tool_profile.substitute_text_in_all_nodes( '{', '}', tool_provider_registry.registry )
8
+
9
+ # only if tool_options, provided prune day available resource_handlers
10
+ if tool_options
11
+ resource_handlers = tool_profile.first_at('resource_handler')
12
+ resource_handlers_to_delete = []
13
+ resource_handlers.each { |resource_handler|
14
+ resource_handler_wrapper = Lti2Commons::JsonWrapper.new( resource_handler )
15
+ unless tool_options.include? resource_handler_wrapper.first_at('resource_name.default_value')
16
+ resource_handlers_to_delete << resource_handler
17
+ end
18
+ }
19
+ resource_handlers_to_delete.each { |rh|
20
+ resource_handlers.delete( rh )
21
+ }
22
+ end
23
+ tool_profile.root
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module Lti2Tp
2
+ class ToolDeployment < ActiveRecord::Base
3
+ belongs_to :tenant
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Lti2Tp
2
+
3
+ class ToolProviderRegistry
4
+
5
+ attr_reader :security_contract_template, :tp_deployment_url, :relaxed_oauth_check, :result_template,
6
+ :tool_provider_name, :registry
7
+
8
+ def initialize
9
+ registry_entries = Registry.all()
10
+ @registry = {}
11
+ registry_entries.each { |entry| @registry[entry.name] = entry.content unless entry.name == 'content' }
12
+
13
+ @tp_deployment_url = registry['tp_deployment_url']
14
+ @relaxed_oauth_check = registry['relaxed_oauth_check']
15
+ @result_template = registry['result_template']
16
+ @tool_provider_name = registry['tool_provider_name']
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Lti2Tp</title>
5
+ <%= stylesheet_link_tag "lti2_tp/application", media: "all" %>
6
+ <%= javascript_include_tag "lti2_tp/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/lti2_tp/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'
@@ -0,0 +1,11 @@
1
+ include Lti2Commons
2
+ include WireLogSupport
3
+
4
+ NONCE_TIME_TO_LIVE = 300 # seconds
5
+ Rails.application.config.nonce_cache = Cache.new :ttl => NONCE_TIME_TO_LIVE
6
+
7
+ if ActiveRecord::Base.connection.table_exists? 'lti2_tp_registries'
8
+ Rails.application.config.tool_provider_registry = Lti2Tp::ToolProviderRegistry.new
9
+ end
10
+
11
+ Rails.application.config.wire_log = WireLog.new "ToolProvider", File.expand_path("../tc_sample_app/public/wirelog.html")
@@ -0,0 +1,9 @@
1
+ Lti2Tp::Engine.routes.draw do
2
+
3
+ resources :registrations
4
+
5
+ match 'reregistrations' => 'registrations#reregister', :via => :post
6
+
7
+ match 'tool_proxies' => 'registrations#end_registration', :via => :post
8
+
9
+ end
@@ -0,0 +1,67 @@
1
+ class CreateLti2TpResources < ActiveRecord::Migration
2
+ def up
3
+ create_table :lti2_tp_registrations do |t|
4
+ t.integer :tool_id
5
+ t.integer :tenant_id
6
+ t.string :tenant_name
7
+ t.string :tenant_basename
8
+ t.string :user_id
9
+ t.string :reg_key
10
+ t.string :reg_password
11
+ t.string :tc_profile_url
12
+ t.string :launch_presentation_return_url
13
+ t.string :status
14
+ t.string :message_type
15
+ t.string :lti_version
16
+ t.string :end_registration_id
17
+ t.text :tool_consumer_profile_json
18
+ t.text :tool_profile_json
19
+ t.text :tool_proxy_json
20
+ t.text :proposed_tool_proxy_json
21
+
22
+ t.timestamps
23
+ end
24
+ execute('ALTER TABLE lti2_tp_registrations CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
25
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tenant_name` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
26
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tenant_basename` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
27
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `user_id` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
28
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `reg_key` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
29
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `reg_password` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
30
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tc_profile_url` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
31
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `launch_presentation_return_url` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
32
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `status` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
33
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `message_type` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
34
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `lti_version` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
35
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `end_registration_id` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
36
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tool_consumer_profile_json` TEXT CHARACTER SET utf8mb4 NULL;")
37
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tool_profile_json` TEXT CHARACTER SET utf8mb4 NULL;")
38
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `tool_proxy_json` TEXT CHARACTER SET utf8mb4 NULL;")
39
+ execute("ALTER TABLE lti2_tp_registrations MODIFY `proposed_tool_proxy_json` TEXT CHARACTER SET utf8mb4 NULL;")
40
+
41
+ create_table :lti2_tp_registries do |t|
42
+ t.string :name
43
+ t.text :content
44
+
45
+ t.timestamps
46
+ end
47
+ execute('ALTER TABLE lti2_tp_registries CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
48
+ execute("ALTER TABLE lti2_tp_registries MODIFY `name` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
49
+ execute("ALTER TABLE lti2_tp_registries MODIFY `content` TEXT CHARACTER SET utf8mb4 NULL;")
50
+
51
+ create_table :lti2_tp_tools do |t|
52
+ t.string :tool_name
53
+ t.text :tool_profile_template
54
+
55
+ t.timestamps
56
+ end
57
+ execute('ALTER TABLE lti2_tp_tools CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
58
+ execute("ALTER TABLE lti2_tp_tools MODIFY `tool_name` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;")
59
+ execute("ALTER TABLE lti2_tp_tools MODIFY `tool_profile_template` TEXT CHARACTER SET utf8mb4 NULL;")
60
+ end
61
+
62
+ def down
63
+ drop_table :lti2_tp_registrations
64
+ drop_table :lti2_tp_registries
65
+ drop_table :lti2_tp_tools
66
+ end
67
+ end
@@ -0,0 +1,4 @@
1
+ require 'lti2_tp/engine'
2
+
3
+ module Lti2Tp
4
+ end
@@ -0,0 +1,5 @@
1
+ module Lti2Tp
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Lti2Tp
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Lti2Tp
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :lti2_tp do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path( '../lib', __FILE__ )
2
+
3
+ # Maintain your gem's version:
4
+ require 'lti2_tp/version'
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+
9
+ s.name = 'lti2_tp'
10
+ s.version = Lti2Tp::VERSION
11
+ s.authors = ['John Tibbetts']
12
+ s.email = ['john.tibbetts@ingramcontent.com']
13
+ s.homepage = 'http://vitalsource.com'
14
+ s.summary = 'LTI2 Tool Provider'
15
+ s.description = 'LTI2 Tool Provider'
16
+
17
+ s.files = Dir[ '{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc' ]
18
+ s.test_files = Dir[ 'test/**/*' ]
19
+
20
+ s.required_ruby_version = '~> 2.0.0'
21
+
22
+ s.add_dependency 'rails', '~> 4.1.1'
23
+ s.add_dependency 'mysql2', '~> 0.3.13'
24
+
25
+ s.add_development_dependency 'sqlite3'
26
+
27
+ end