coursemology-evaluator 0.0.0 → 0.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.
- checksums.yaml +4 -4
- data/.env +3 -0
- data/.gitignore +22 -19
- data/.hound.yml +8 -0
- data/.idea/Coursemology Evaluator.iml +1 -76
- data/.rspec +1 -0
- data/.rubocop.unhound.yml +244 -0
- data/.rubocop.yml +26 -0
- data/.travis.yml +4 -2
- data/Gemfile.lock +114 -0
- data/Procfile +1 -0
- data/README.md +29 -2
- data/Rakefile +3 -3
- data/bin/evaluator +6 -0
- data/coursemology-evaluator.gemspec +18 -4
- data/lib/coursemology/evaluator.rb +32 -4
- data/lib/coursemology/evaluator/cli.rb +51 -0
- data/lib/coursemology/evaluator/client.rb +74 -0
- data/lib/coursemology/evaluator/docker_container.rb +58 -0
- data/lib/coursemology/evaluator/logging.rb +11 -0
- data/lib/coursemology/evaluator/logging/client_log_subscriber.rb +24 -0
- data/lib/coursemology/evaluator/logging/docker_log_subscriber.rb +17 -0
- data/lib/coursemology/evaluator/models.rb +6 -0
- data/lib/coursemology/evaluator/models/base.rb +49 -0
- data/lib/coursemology/evaluator/models/programming_evaluation.rb +66 -0
- data/lib/coursemology/evaluator/models/programming_evaluation/package.rb +11 -0
- data/lib/coursemology/evaluator/services.rb +5 -0
- data/lib/coursemology/evaluator/services/evaluate_programming_package_service.rb +150 -0
- data/lib/coursemology/evaluator/string_io.rb +13 -0
- data/lib/coursemology/evaluator/utils.rb +41 -0
- data/lib/coursemology/evaluator/version.rb +1 -1
- data/lib/coursemology/polyglot/extensions.rb +2 -0
- data/lib/coursemology/polyglot/extensions/language.rb +23 -0
- metadata +190 -12
@@ -0,0 +1,11 @@
|
|
1
|
+
class Coursemology::Evaluator::Models::ProgrammingEvaluation::Package
|
2
|
+
# The stream comprising the package.
|
3
|
+
attr_reader :stream
|
4
|
+
|
5
|
+
# Constructs a new Package.
|
6
|
+
#
|
7
|
+
# @param [IO] stream The stream comprising the package.
|
8
|
+
def initialize(stream)
|
9
|
+
@stream = stream
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
class Coursemology::Evaluator::Services::EvaluateProgrammingPackageService
|
2
|
+
Result = Struct.new(:stdout, :stderr, :test_report, :exit_code)
|
3
|
+
|
4
|
+
# The path to the Coursemology user home directory.
|
5
|
+
HOME_PATH = '/home/coursemology'.freeze
|
6
|
+
|
7
|
+
# The path to where the package will be extracted.
|
8
|
+
PACKAGE_PATH = File.join(HOME_PATH, 'package')
|
9
|
+
|
10
|
+
# The path to where the test report will be at.
|
11
|
+
REPORT_PATH = File.join(PACKAGE_PATH, 'report.xml')
|
12
|
+
|
13
|
+
# The ratio to multiply the memory limits from our evaluation to the container by.
|
14
|
+
MEMORY_LIMIT_RATIO = 1.megabyte / 1.kilobyte
|
15
|
+
|
16
|
+
# Executes the given package in a container.
|
17
|
+
#
|
18
|
+
# @param [Coursemology::Evaluator::Models::ProgrammingEvaluation] evaluation The evaluation
|
19
|
+
# from the server.
|
20
|
+
# @return [Coursemology::Evaluator::Services::EvaluateProgrammingPackageService::Result] The
|
21
|
+
# result of the evaluation.
|
22
|
+
def self.execute(evaluation)
|
23
|
+
new(evaluation).send(:execute)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates a new service object.
|
27
|
+
def initialize(evaluation)
|
28
|
+
@evaluation = evaluation
|
29
|
+
@package = evaluation.package
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Evaluates the package.
|
35
|
+
#
|
36
|
+
# @return [Coursemology::Evaluator::Services::EvaluateProgrammingPackageService::Result]
|
37
|
+
def execute
|
38
|
+
container = create_container(@evaluation.language.class.docker_image)
|
39
|
+
copy_package(container)
|
40
|
+
execute_package(container)
|
41
|
+
|
42
|
+
extract_result(container)
|
43
|
+
ensure
|
44
|
+
destroy_container(container) if container
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_container(image)
|
48
|
+
image_identifier = "coursemology/evaluator-image-#{image}"
|
49
|
+
Coursemology::Evaluator::DockerContainer.create(image_identifier, argv: container_arguments)
|
50
|
+
end
|
51
|
+
|
52
|
+
def container_arguments
|
53
|
+
result = []
|
54
|
+
result.push("-c#{@evaluation.time_limit}") if @evaluation.time_limit
|
55
|
+
result.push("-m#{@evaluation.memory_limit * MEMORY_LIMIT_RATIO}") if @evaluation.memory_limit
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
# Copies the contents of the package to the container.
|
61
|
+
#
|
62
|
+
# @param [Docker::Container] container The container to copy the package into.
|
63
|
+
def copy_package(container)
|
64
|
+
tar = tar_package(@package)
|
65
|
+
container.archive_in_stream(HOME_PATH) do
|
66
|
+
tar.read(Excon.defaults[:chunk_size]).to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Converts the zip package into a tar package for the container.
|
71
|
+
#
|
72
|
+
# This also adds an additional +package+ directory to the start of the path, following tar
|
73
|
+
# convention.
|
74
|
+
#
|
75
|
+
# @param [Coursemology::Evaluator::Models::ProgrammingEvaluation::Package] package The package
|
76
|
+
# to convert to a tar.
|
77
|
+
# @return [IO] A stream containing the tar.
|
78
|
+
def tar_package(package)
|
79
|
+
tar_file_stream = StringIO.new
|
80
|
+
tar_file = Gem::Package::TarWriter.new(tar_file_stream)
|
81
|
+
Zip::File.open_buffer(package.stream) do |zip_file|
|
82
|
+
copy_archive(zip_file, tar_file, File.basename(PACKAGE_PATH))
|
83
|
+
tar_file.close
|
84
|
+
end
|
85
|
+
|
86
|
+
tar_file_stream.seek(0)
|
87
|
+
tar_file_stream
|
88
|
+
end
|
89
|
+
|
90
|
+
# Copies every entry from the zip archive to the tar archive, adding the optional prefix to the
|
91
|
+
# start of each file name.
|
92
|
+
#
|
93
|
+
# @param [Zip::File] zip_file The zip file to read from.
|
94
|
+
# @param [Gem::Package::TarWriter] tar_file The tar file to write to.
|
95
|
+
# @param [String] prefix The prefix to add to every file name in the tar.
|
96
|
+
def copy_archive(zip_file, tar_file, prefix = nil)
|
97
|
+
zip_file.each do |entry|
|
98
|
+
next unless entry.file?
|
99
|
+
|
100
|
+
zip_entry_stream = entry.get_input_stream
|
101
|
+
new_entry_name = prefix ? File.join(prefix, entry.name) : entry.name
|
102
|
+
tar_file.add_file(new_entry_name, 0664) do |tar_entry_stream|
|
103
|
+
IO.copy_stream(zip_entry_stream, tar_entry_stream)
|
104
|
+
end
|
105
|
+
|
106
|
+
zip_entry_stream.close
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def execute_package(container)
|
111
|
+
container.start!
|
112
|
+
container.wait
|
113
|
+
end
|
114
|
+
|
115
|
+
def extract_result(container)
|
116
|
+
logs = container.logs(stdout: true, stderr: true)
|
117
|
+
|
118
|
+
_, stdout, stderr = Coursemology::Evaluator::Utils.parse_docker_stream(logs)
|
119
|
+
Result.new(stdout, stderr, extract_test_report(container), container.exit_code)
|
120
|
+
end
|
121
|
+
|
122
|
+
def extract_test_report(container)
|
123
|
+
stream = extract_test_report_archive(container)
|
124
|
+
|
125
|
+
tar_file = Gem::Package::TarReader.new(stream)
|
126
|
+
tar_file.each do |file|
|
127
|
+
return file.read
|
128
|
+
end
|
129
|
+
rescue Docker::Error::NotFoundError
|
130
|
+
return nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# Extracts the test report from the container.
|
134
|
+
#
|
135
|
+
# @return [StringIO] The stream containing the archive, the pointer is reset to the start of the
|
136
|
+
# stream.
|
137
|
+
def extract_test_report_archive(container)
|
138
|
+
stream = StringIO.new
|
139
|
+
container.archive_out(REPORT_PATH) do |bytes|
|
140
|
+
stream.write(bytes)
|
141
|
+
end
|
142
|
+
|
143
|
+
stream.seek(0)
|
144
|
+
stream
|
145
|
+
end
|
146
|
+
|
147
|
+
def destroy_container(container)
|
148
|
+
container.delete
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Adapter for StringIO for compatibility with RubyZip.
|
2
|
+
#
|
3
|
+
# StringIO does not inherit from IO, so RubyZip does not accept StringIO in place of IO.
|
4
|
+
class Coursemology::Evaluator::StringIO < ::StringIO
|
5
|
+
def is_a?(klass)
|
6
|
+
klass == IO || super
|
7
|
+
end
|
8
|
+
|
9
|
+
# RubyZip assumes all IO objects respond to path.
|
10
|
+
def path
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Coursemology::Evaluator::Utils
|
2
|
+
# Represents one block of the Docker Attach protocol.
|
3
|
+
DockerAttachBlock = Struct.new(:stream, :length, :bytes)
|
4
|
+
|
5
|
+
# Parses a Docker +attach+ protocol stream into its constituent protocols.
|
6
|
+
#
|
7
|
+
# See https://docs.docker.com/engine/reference/api/docker_remote_api_v1.19/#attach-to-a-container.
|
8
|
+
#
|
9
|
+
# This drops all blocks belonging to streams other than STDIN, STDOUT, or STDERR.
|
10
|
+
#
|
11
|
+
# @param [String] string The input stream to parse.
|
12
|
+
# @return [Array<(String, String, String)>] The stdin, stdout, and stderr output.
|
13
|
+
def self.parse_docker_stream(string)
|
14
|
+
result = ['', '', '']
|
15
|
+
stream = StringIO.new(string)
|
16
|
+
|
17
|
+
while (block = parse_docker_stream_read_block(stream))
|
18
|
+
next if block.stream >= result.length
|
19
|
+
result[block.stream] << block.bytes
|
20
|
+
end
|
21
|
+
|
22
|
+
stream.close
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
# Reads a block from the given stream, and parses it according to the Docker +attach+ protocol.
|
27
|
+
#
|
28
|
+
# @param [IO] stream The stream to read.
|
29
|
+
# @raise [IOError] If the stream is corrupt.
|
30
|
+
# @return [DockerAttachBlock] If there is data in the stream.
|
31
|
+
# @return [nil] If there is no data left in the stream.
|
32
|
+
def self.parse_docker_stream_read_block(stream)
|
33
|
+
header = stream.read(8)
|
34
|
+
return nil if header.blank?
|
35
|
+
fail IOError unless header.length == 8
|
36
|
+
|
37
|
+
console_stream, _, _, _, length = header.unpack('C4N')
|
38
|
+
DockerAttachBlock.new(console_stream, length, stream.read(length))
|
39
|
+
end
|
40
|
+
private_class_method :parse_docker_stream_read_block
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Coursemology::Polyglot::Language
|
2
|
+
# Finds the language class with the specified name.
|
3
|
+
#
|
4
|
+
# @param [String] type The name of the class.
|
5
|
+
# @return [nil] If the type is not defined.
|
6
|
+
# @return [Class] If the type was found.
|
7
|
+
def self.find_by(type:)
|
8
|
+
class_ = concrete_languages.find { |language| language.name == type }
|
9
|
+
class_.new if class_
|
10
|
+
end
|
11
|
+
|
12
|
+
# Finds the language class with the specified name.
|
13
|
+
#
|
14
|
+
# @param [String] type The name of the class.
|
15
|
+
# @return [Class] If the type was found.
|
16
|
+
# @raise [ArgumentError] When the type was not found.
|
17
|
+
def self.find_by!(type:)
|
18
|
+
language = find_by(type: type)
|
19
|
+
fail ArgumentError, "Cannot find the language #{type}" unless language
|
20
|
+
|
21
|
+
language
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coursemology-evaluator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Low
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,23 +52,201 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_girl
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: codeclimate-test-reporter
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: vcr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: activesupport
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 4.2.0
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 4.2.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: flexirest
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.2'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.2'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: faraday_middleware
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: coursemology-polyglot
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 0.0.3
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 0.0.3
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: docker-api
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 1.2.5
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.2.5
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rubyzip
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
55
209
|
description: Sets up a consistent environment for evaluating programming packages.
|
56
210
|
email:
|
57
211
|
- joel@joelsplace.sg
|
58
|
-
executables:
|
212
|
+
executables:
|
213
|
+
- evaluator
|
59
214
|
extensions: []
|
60
215
|
extra_rdoc_files: []
|
61
216
|
files:
|
217
|
+
- ".env"
|
62
218
|
- ".gitignore"
|
219
|
+
- ".hound.yml"
|
63
220
|
- ".idea/Coursemology Evaluator.iml"
|
64
221
|
- ".rspec"
|
222
|
+
- ".rubocop.unhound.yml"
|
223
|
+
- ".rubocop.yml"
|
65
224
|
- ".travis.yml"
|
66
225
|
- Gemfile
|
226
|
+
- Gemfile.lock
|
227
|
+
- Procfile
|
67
228
|
- README.md
|
68
229
|
- Rakefile
|
230
|
+
- bin/evaluator
|
69
231
|
- coursemology-evaluator.gemspec
|
70
232
|
- lib/coursemology/evaluator.rb
|
233
|
+
- lib/coursemology/evaluator/cli.rb
|
234
|
+
- lib/coursemology/evaluator/client.rb
|
235
|
+
- lib/coursemology/evaluator/docker_container.rb
|
236
|
+
- lib/coursemology/evaluator/logging.rb
|
237
|
+
- lib/coursemology/evaluator/logging/client_log_subscriber.rb
|
238
|
+
- lib/coursemology/evaluator/logging/docker_log_subscriber.rb
|
239
|
+
- lib/coursemology/evaluator/models.rb
|
240
|
+
- lib/coursemology/evaluator/models/base.rb
|
241
|
+
- lib/coursemology/evaluator/models/programming_evaluation.rb
|
242
|
+
- lib/coursemology/evaluator/models/programming_evaluation/package.rb
|
243
|
+
- lib/coursemology/evaluator/services.rb
|
244
|
+
- lib/coursemology/evaluator/services/evaluate_programming_package_service.rb
|
245
|
+
- lib/coursemology/evaluator/string_io.rb
|
246
|
+
- lib/coursemology/evaluator/utils.rb
|
71
247
|
- lib/coursemology/evaluator/version.rb
|
248
|
+
- lib/coursemology/polyglot/extensions.rb
|
249
|
+
- lib/coursemology/polyglot/extensions/language.rb
|
72
250
|
homepage: http://coursemology.org
|
73
251
|
licenses:
|
74
252
|
- MIT
|
@@ -89,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
267
|
version: '0'
|
90
268
|
requirements: []
|
91
269
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.4.
|
270
|
+
rubygems_version: 2.4.5.1
|
93
271
|
signing_key:
|
94
272
|
specification_version: 4
|
95
273
|
summary: Coursemology programming package evaluator
|