eval_in 0.1.6 → 0.2.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/Readme.md +132 -0
- data/eval_in.gemspec +1 -1
- data/lib/eval_in.rb +5 -168
- data/lib/eval_in/client.rb +77 -0
- data/lib/eval_in/constants.rb +37 -0
- data/lib/eval_in/http.rb +46 -0
- data/lib/eval_in/mock.rb +80 -0
- data/lib/eval_in/result.rb +38 -0
- data/spec/client_spec.rb +195 -0
- data/spec/eval_in_spec.rb +91 -371
- data/spec/http_spec.rb +27 -0
- data/spec/mock_spec.rb +168 -0
- data/spec/result_spec.rb +93 -0
- data/spec/spec_helper.rb +23 -0
- metadata +17 -3
- data/lib/eval_in/version.rb +0 -3
data/spec/http_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This code is mostly tested through tests on the higher level things it depends on
|
4
|
+
# But for a few small helpers, I wanted to just hit a lot of edge cases to really
|
5
|
+
# give myself confidence they did what I was expecting.
|
6
|
+
RSpec.describe EvalIn::HTTP do
|
7
|
+
describe '.jsonify_url' do
|
8
|
+
def assert_transforms(initial_url, expected_url)
|
9
|
+
actual_url = EvalIn::HTTP.jsonify_url initial_url
|
10
|
+
expect(actual_url).to eq expected_url
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'appends .json to a url that is missing it' do
|
14
|
+
assert_transforms 'http://eval.in/1', 'http://eval.in/1.json'
|
15
|
+
assert_transforms 'http://eval.in/1.json', 'http://eval.in/1.json'
|
16
|
+
|
17
|
+
assert_transforms 'http://eval.in/1?a=b', 'http://eval.in/1.json?a=b'
|
18
|
+
assert_transforms 'http://eval.in/1.json?a=b', 'http://eval.in/1.json?a=b'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'changes .not-json to .json' do
|
22
|
+
assert_transforms 'http://eval.in/1.xml', 'http://eval.in/1.json'
|
23
|
+
assert_transforms 'http://eval.in/1.html', 'http://eval.in/1.json'
|
24
|
+
assert_transforms 'http://eval.in/1.html?a=b', 'http://eval.in/1.json?a=b'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/mock_spec.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eval_in/mock'
|
3
|
+
|
4
|
+
|
5
|
+
# TODO: look at other reasons the thing could blow up, do we need to provide the mock a way to do these things?
|
6
|
+
RSpec.describe EvalIn::Mock do
|
7
|
+
def is_jruby?
|
8
|
+
defined? RUBY_ENGINE and RUBY_ENGINE == 'jruby'
|
9
|
+
end
|
10
|
+
|
11
|
+
specify 'instances provide the same methods with the same signatures as the EvalIn class' do
|
12
|
+
mock = described_class.new
|
13
|
+
EvalIn.methods(false).each do |method_name|
|
14
|
+
expected_signature = EvalIn.method(method_name).parameters
|
15
|
+
actual_signature = mock.method(method_name).parameters
|
16
|
+
expect(actual_signature).to eq expected_signature
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#call' do
|
21
|
+
context 'when initialized with a mock result' do
|
22
|
+
it 'returns the mock result' do
|
23
|
+
result = Object.new
|
24
|
+
mock = described_class.new result: result
|
25
|
+
expect(mock.call "code", language: '').to equal result
|
26
|
+
end
|
27
|
+
it 'raises an ArgumentError if no language is provided' do
|
28
|
+
mock = described_class.new result: Object.new
|
29
|
+
expect { mock.call "code" }.to raise_error ArgumentError
|
30
|
+
expect { mock.call "code", language: '' }.to_not raise_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when initialized with an on_call proc' do
|
35
|
+
it 'passes the code and options to the proc and returns the result' do
|
36
|
+
mock = described_class.new on_call: -> code, options {
|
37
|
+
expect(code).to eq 'on_call code'
|
38
|
+
expect(options).to eq language: 'l'
|
39
|
+
123
|
40
|
+
}
|
41
|
+
expect(mock.call "on_call code", language: "l").to eq 123
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'raises an ArgumentError if no language is provided' do
|
45
|
+
mock = described_class.new on_call: -> * {}
|
46
|
+
expect { mock.call "code" }.to raise_error ArgumentError
|
47
|
+
expect { mock.call "code", language: '' }.to_not raise_error
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when initialized without a a result or on_call proc' do
|
52
|
+
before do
|
53
|
+
if is_jruby?
|
54
|
+
pending "I can't figure out how to get JRuby to execute a fucking process"
|
55
|
+
raise "Why did RSpec remove the block form of pending?"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'executes the code with open3 against the list of language mappings' do
|
60
|
+
mock = described_class.new(languages: {
|
61
|
+
'correct-lang' => {program: 'echo', args: ['RIGHT LANGUAGE']},
|
62
|
+
'incorrect-lang' => {program: 'echo', args: ['WRONG LANGUAGE']},
|
63
|
+
})
|
64
|
+
result = mock.call 'some code', language: 'correct-lang'
|
65
|
+
expect(result.output).to start_with 'RIGHT LANGUAGE'
|
66
|
+
end
|
67
|
+
|
68
|
+
def result_from(options={})
|
69
|
+
language = options.fetch :language, 'dummy language'
|
70
|
+
code = options.fetch :code, 'dummy code'
|
71
|
+
program_code = options.fetch :program_code, '# noop'
|
72
|
+
|
73
|
+
described_class.new(languages: {
|
74
|
+
language => {
|
75
|
+
program: RbConfig.ruby,
|
76
|
+
args: ['-e', program_code]
|
77
|
+
}
|
78
|
+
}).call(code, language: language)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'records stderr and stdout' do
|
82
|
+
result = result_from program_code: '$stdout.print("STDOUT "); $stdout.flush; $stderr.print("STDERR")'
|
83
|
+
expect(result.output).to eq 'STDOUT STDERR'
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'records the exit status' do
|
87
|
+
expect(result_from(program_code: 'exit 12').exitstatus).to eq 12
|
88
|
+
expect(result_from(program_code: 'exit 99').exitstatus).to eq 99
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'sets the language and language_friendly to the provided language' do
|
92
|
+
result = result_from language: 'the-lang'
|
93
|
+
expect(result.language).to eq 'the-lang'
|
94
|
+
expect(result.language_friendly).to eq 'the-lang'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets the code to the provided code' do
|
98
|
+
expect(result_from(code: 'the-code').code).to eq 'the-code'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'sets the url to my mock result at https://eval.in/207744.json' do
|
102
|
+
expect(result_from().url).to eq 'https://eval.in/207744.json'
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'sets the status to something looking like a real status' do
|
106
|
+
# in this case, just the status from https://eval.in/207744.json
|
107
|
+
expect(result_from().status).to match success_status_regex
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'blows up if asked for a language it doesn\'t know how to evaluate' do
|
111
|
+
expect { described_class.new.call '', language: 'no-such-lang' }
|
112
|
+
.to raise_error KeyError, /no-such-lang/
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'raises an ArgumentError if no language is provided' do
|
116
|
+
mock = described_class.new languages: {'l' => {program: 'echo', args: []}}
|
117
|
+
expect { mock.call "code" }.to raise_error ArgumentError
|
118
|
+
expect { mock.call "code", language: 'l' }.to_not raise_error
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#fetch_result' do
|
124
|
+
context 'when initialized with a mock result' do
|
125
|
+
it 'returns the mock result' do
|
126
|
+
result = Object.new
|
127
|
+
mock = described_class.new(result: result)
|
128
|
+
expect(mock.fetch_result "code").to equal result
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'when initialized with an on_fetch_result proc' do
|
133
|
+
it 'passes the url and options to the proc and returns the result' do
|
134
|
+
mock = described_class.new on_fetch_result: -> url, options {
|
135
|
+
expect(url).to eq 'some url'
|
136
|
+
expect(options).to eq a: 'b'
|
137
|
+
123
|
138
|
+
}
|
139
|
+
expect(mock.fetch_result 'some url', a: 'b').to eq 123
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when initialized without a result or on_fetch_result proc' do
|
144
|
+
include WebMock::API
|
145
|
+
|
146
|
+
it 'delegates to the real implementation' do
|
147
|
+
url = "http://example.com/some-result.json"
|
148
|
+
result_hash = {'lang_friendly' => 'some lang friendly',
|
149
|
+
'lang' => 'some lang',
|
150
|
+
'code' => 'some code',
|
151
|
+
'output' => 'some output',
|
152
|
+
'status' => 'some status'}
|
153
|
+
stub_request(:get, url)
|
154
|
+
.with(headers: {'User-Agent' => 'http://rubygems.org/gems/eval_in (context)'})
|
155
|
+
.to_return(status: 200, body: JSON.dump(result_hash))
|
156
|
+
result = described_class.new.fetch_result("http://example.com/some-result.json", context: 'context')
|
157
|
+
assert_result result,
|
158
|
+
exitstatus: 0,
|
159
|
+
language: result_hash['lang'],
|
160
|
+
language_friendly: result_hash['lang_friendly'],
|
161
|
+
code: result_hash['code'],
|
162
|
+
output: result_hash['output'],
|
163
|
+
status: result_hash['status'],
|
164
|
+
url: url
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/spec/result_spec.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe EvalIn::Result do
|
4
|
+
it 'initializes with the provided attributes' do
|
5
|
+
result = EvalIn::Result.new exitstatus: 123,
|
6
|
+
language: 'the language',
|
7
|
+
language_friendly: 'the friendly language',
|
8
|
+
code: 'the code',
|
9
|
+
output: 'the output',
|
10
|
+
status: 'the status'
|
11
|
+
assert_result result,
|
12
|
+
exitstatus: 123,
|
13
|
+
language: 'the language',
|
14
|
+
language_friendly: 'the friendly language',
|
15
|
+
code: 'the code',
|
16
|
+
output: 'the output',
|
17
|
+
status: 'the status'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'uses sensible type-correct defaults for missing attributes' do
|
21
|
+
assert_result EvalIn::Result.new,
|
22
|
+
exitstatus: -1,
|
23
|
+
language: '',
|
24
|
+
language_friendly: '',
|
25
|
+
code: '',
|
26
|
+
output: '',
|
27
|
+
status: '',
|
28
|
+
url: ''
|
29
|
+
assert_result EvalIn::Result.new(language: nil,
|
30
|
+
language_friendly: nil,
|
31
|
+
code: nil,
|
32
|
+
output: nil,
|
33
|
+
status: nil,
|
34
|
+
url: nil),
|
35
|
+
exitstatus: -1,
|
36
|
+
language: '',
|
37
|
+
language_friendly: '',
|
38
|
+
code: '',
|
39
|
+
output: '',
|
40
|
+
status: '',
|
41
|
+
url: ''
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'doesn\'t mutate the input attributes' do
|
45
|
+
attributes = {status: 'OK'}
|
46
|
+
EvalIn::Result.new attributes
|
47
|
+
expect(attributes).to eq status: 'OK'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'logs extra attributes to stderr input' do
|
51
|
+
fake_error_stream = StringIO.new
|
52
|
+
EvalIn::Result.new a: 1, b: 2, stderr: fake_error_stream
|
53
|
+
expect(fake_error_stream.string).to eq "Unexpected attributes! [:a, :b]\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'defaults the error stream to $stderr' do
|
57
|
+
expect { EvalIn::Result.new a: 1, b: 2 }.to \
|
58
|
+
output("Unexpected attributes! [:a, :b]\n").to_stderr
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'has an as_json representation which dumps all its keys' do
|
62
|
+
result = EvalIn::Result.new(language: 'l',
|
63
|
+
language_friendly: 'lf',
|
64
|
+
code: 'c',
|
65
|
+
output: 'o',
|
66
|
+
status: 's',
|
67
|
+
url: 'u')
|
68
|
+
expect(result.as_json).to eq 'exitstatus' => -1,
|
69
|
+
'language' => 'l',
|
70
|
+
'language_friendly' => 'lf',
|
71
|
+
'code' => 'c',
|
72
|
+
'output' => 'o',
|
73
|
+
'status' => 's',
|
74
|
+
'url' => 'u'
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'has a to_json that works correctly' do
|
78
|
+
result = EvalIn::Result.new(language: 'l',
|
79
|
+
language_friendly: 'lf',
|
80
|
+
code: 'c',
|
81
|
+
output: 'o',
|
82
|
+
status: 's',
|
83
|
+
url: 'u')
|
84
|
+
after_json = JSON.parse(result.to_json)
|
85
|
+
expect(after_json).to eq 'exitstatus' => -1,
|
86
|
+
'language' => 'l',
|
87
|
+
'language_friendly' => 'lf',
|
88
|
+
'code' => 'c',
|
89
|
+
'output' => 'o',
|
90
|
+
'status' => 's',
|
91
|
+
'url' => 'u'
|
92
|
+
end
|
93
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'eval_in'
|
2
|
+
require 'webmock'
|
3
|
+
WebMock.disable_net_connect!
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.filter_run_excluding integration: true
|
7
|
+
|
8
|
+
config.include Module.new {
|
9
|
+
def assert_result(result, attributes)
|
10
|
+
attributes.each do |key, value|
|
11
|
+
expect(result.public_send key).to eq value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def success_status_regex
|
16
|
+
/OK \([\d.]+ sec real, [\d.]+ sec wall, \d MB, \d+ syscalls\)/
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
config.after do
|
21
|
+
WebMock.reset!
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eval_in
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Cheek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -79,8 +79,17 @@ files:
|
|
79
79
|
- Readme.md
|
80
80
|
- eval_in.gemspec
|
81
81
|
- lib/eval_in.rb
|
82
|
-
- lib/eval_in/
|
82
|
+
- lib/eval_in/client.rb
|
83
|
+
- lib/eval_in/constants.rb
|
84
|
+
- lib/eval_in/http.rb
|
85
|
+
- lib/eval_in/mock.rb
|
86
|
+
- lib/eval_in/result.rb
|
87
|
+
- spec/client_spec.rb
|
83
88
|
- spec/eval_in_spec.rb
|
89
|
+
- spec/http_spec.rb
|
90
|
+
- spec/mock_spec.rb
|
91
|
+
- spec/result_spec.rb
|
92
|
+
- spec/spec_helper.rb
|
84
93
|
homepage: https://github.com/JoshCheek/eval_in
|
85
94
|
licenses:
|
86
95
|
- WTFPL
|
@@ -106,5 +115,10 @@ signing_key:
|
|
106
115
|
specification_version: 4
|
107
116
|
summary: Evaluates code (Ruby and others) safely by sending it to https://eval.in
|
108
117
|
test_files:
|
118
|
+
- spec/client_spec.rb
|
109
119
|
- spec/eval_in_spec.rb
|
120
|
+
- spec/http_spec.rb
|
121
|
+
- spec/mock_spec.rb
|
122
|
+
- spec/result_spec.rb
|
123
|
+
- spec/spec_helper.rb
|
110
124
|
has_rdoc:
|
data/lib/eval_in/version.rb
DELETED