seeing_is_believing 3.6.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -5
- data/Rakefile +2 -0
- data/Readme.md +1 -1
- data/appveyor.yml +3 -5
- data/features/regression.feature +7 -0
- data/features/xmpfilter-style.feature +1 -0
- data/lib/seeing_is_believing.rb +2 -1
- data/lib/seeing_is_believing/binary.rb +1 -1
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +1 -0
- data/lib/seeing_is_believing/binary/comment_lines.rb +1 -1
- data/lib/seeing_is_believing/binary/commentable_lines.rb +7 -7
- data/lib/seeing_is_believing/binary/config.rb +1 -3
- data/lib/seeing_is_believing/binary/remove_annotations.rb +1 -1
- data/lib/seeing_is_believing/code.rb +3 -3
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +30 -32
- data/lib/seeing_is_believing/event_stream/consumer.rb +2 -0
- data/lib/seeing_is_believing/event_stream/events.rb +7 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +3 -3
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +2 -1
- data/lib/seeing_is_believing/event_stream/producer.rb +14 -0
- data/lib/seeing_is_believing/safe.rb +1 -7
- data/lib/seeing_is_believing/swap_files.rb +90 -0
- data/lib/seeing_is_believing/the_matrix.rb +17 -4
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +22 -8
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +3 -0
- data/seeing_is_believing.gemspec +4 -5
- data/spec/binary/config_spec.rb +2 -2
- data/spec/evaluate_by_moving_files_spec.rb +18 -17
- data/spec/event_stream_spec.rb +1 -0
- data/spec/seeing_is_believing_spec.rb +99 -24
- data/spec/sib_spec_helpers/version.rb +17 -0
- data/spec/spec_helper.rb +2 -18
- data/spec/wrap_expressions_spec.rb +9 -3
- metadata +26 -12
- data/lib/seeing_is_believing/backup_file.rb +0 -50
- data/lib/seeing_is_believing/customize_pp.rb +0 -5
@@ -48,23 +48,32 @@ Kernel.module_eval do
|
|
48
48
|
private
|
49
49
|
|
50
50
|
define_method :warn do |*args, &block|
|
51
|
-
$stderr.puts
|
51
|
+
$stderr.puts(*args)
|
52
52
|
end
|
53
53
|
|
54
|
+
alias :exec :exec # disable warning
|
54
55
|
define_method :exec do |*args, &block|
|
55
56
|
$SiB.record_exec(args)
|
56
57
|
finish.call
|
57
58
|
real_exec.call(*args, &block)
|
58
59
|
end
|
59
60
|
|
61
|
+
alias :exit! :exit! # disable warning
|
60
62
|
define_method :exit! do |status=false|
|
61
63
|
finish.call
|
62
64
|
real_exit_bang.call(status)
|
63
65
|
end
|
64
66
|
|
67
|
+
alias :fork :fork
|
65
68
|
define_method :fork, &fork_defn
|
66
69
|
end
|
67
70
|
|
71
|
+
module Process
|
72
|
+
class << self
|
73
|
+
alias :fork :fork
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
68
77
|
Kernel.define_singleton_method :fork, &fork_defn
|
69
78
|
Process.define_singleton_method :fork, &fork_defn
|
70
79
|
|
@@ -74,10 +83,14 @@ symbol_to_s = Symbol.instance_method(:to_s)
|
|
74
83
|
exception_message = Exception.instance_method(:message)
|
75
84
|
exception_backtrace = Exception.instance_method(:backtrace)
|
76
85
|
|
86
|
+
# Guarding against hostile users (e.g. me) that do ridiculous things like blowing away these constants
|
87
|
+
exception_class = Exception
|
88
|
+
symbol_class = Symbol
|
89
|
+
|
77
90
|
at_exit do
|
78
|
-
|
79
|
-
|
80
|
-
|
91
|
+
exception_class.class_eval { define_method :message, exception_message }
|
92
|
+
exception_class.class_eval { define_method :backtrace, exception_backtrace }
|
93
|
+
symbol_class.class_eval { define_method :to_s, symbol_to_s }
|
81
94
|
exitstatus = ($! ? $SiB.record_exception(nil, $!) : 0)
|
82
95
|
finish.call
|
83
96
|
real_exit_bang.call(exitstatus) # clears exceptions so they don't print to stderr and change the processes actual exit status (we recorded what it should be)
|
@@ -25,7 +25,7 @@ class SeeingIsBelieving
|
|
25
25
|
|
26
26
|
wrappings = wrappings().sort_by(&:first)
|
27
27
|
|
28
|
-
wrappings.each do |line_num, (range,
|
28
|
+
wrappings.each do |line_num, (range, _last_col, meta)|
|
29
29
|
case meta
|
30
30
|
when :total_fucking_failure
|
31
31
|
rewriter.replace range, '.....TOTAL FUCKING FAILURE!.....'
|
@@ -34,11 +34,11 @@ class SeeingIsBelieving
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
wrappings.each do |line_num, (range,
|
37
|
+
wrappings.each do |line_num, (range, _last_col, _meta)|
|
38
38
|
rewriter.insert_before range, before_each.call(line_num)
|
39
39
|
end
|
40
40
|
|
41
|
-
wrappings.each do |line_num, (range,
|
41
|
+
wrappings.each do |line_num, (range, _last_col, _meta)|
|
42
42
|
rewriter.insert_after range, after_each.call(line_num)
|
43
43
|
end
|
44
44
|
|
@@ -53,8 +53,13 @@ class SeeingIsBelieving
|
|
53
53
|
attr_accessor :before_all, :after_all, :before_each, :after_each
|
54
54
|
attr_accessor :code, :wrappings
|
55
55
|
|
56
|
-
def buffer
|
57
|
-
|
56
|
+
def buffer
|
57
|
+
code.buffer
|
58
|
+
end
|
59
|
+
|
60
|
+
def rewriter
|
61
|
+
code.rewriter
|
62
|
+
end
|
58
63
|
|
59
64
|
def root_range
|
60
65
|
code.root.location.expression
|
@@ -107,7 +112,10 @@ class SeeingIsBelieving
|
|
107
112
|
when :defs, :module
|
108
113
|
add_to_wrappings ast
|
109
114
|
add_children ast, true
|
110
|
-
when :
|
115
|
+
when :ensure, :return, :break, :next, :splat, :kwsplat
|
116
|
+
add_children ast
|
117
|
+
when :rescue
|
118
|
+
add_to_wrappings ast if inline_rescue? ast
|
111
119
|
add_children ast
|
112
120
|
when :class
|
113
121
|
name, * = ast.children
|
@@ -135,8 +143,8 @@ class SeeingIsBelieving
|
|
135
143
|
add_to_wrappings ast
|
136
144
|
the_begin = ast.location.begin
|
137
145
|
if !the_begin
|
138
|
-
#
|
139
|
-
|
146
|
+
# a = 1,2,3
|
147
|
+
add_children ast
|
140
148
|
elsif the_begin.source !~ /\A%/
|
141
149
|
# normal array
|
142
150
|
add_children ast
|
@@ -247,5 +255,11 @@ class SeeingIsBelieving
|
|
247
255
|
add_children ast
|
248
256
|
end
|
249
257
|
end
|
258
|
+
|
259
|
+
def inline_rescue?(ast)
|
260
|
+
primary_code, rescue_body, _else_body = ast.children
|
261
|
+
return false unless primary_code
|
262
|
+
primary_code.loc.expression.last_line == rescue_body.loc.expression.first_line
|
263
|
+
end
|
250
264
|
end
|
251
265
|
end
|
@@ -5,6 +5,9 @@ class SeeingIsBelieving
|
|
5
5
|
# NOTE: if it received the AST, it could figure out if it needs
|
6
6
|
# to always wrap the expression in parentheses
|
7
7
|
WrapExpressions.call program,
|
8
|
+
before_all: -> {
|
9
|
+
"BEGIN { $SiB.file_loaded };"
|
10
|
+
},
|
8
11
|
before_each: -> line_number {
|
9
12
|
"$SiB.record_result(:inspect, #{line_number}, ("
|
10
13
|
},
|
data/seeing_is_believing.gemspec
CHANGED
@@ -12,19 +12,18 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = %q{Records the results of every line of code in your file (intended to be like xmpfilter), inspired by Bret Victor's JavaScript example in his talk "Inventing on Principle"}
|
13
13
|
s.license = "WTFPL"
|
14
14
|
|
15
|
-
s.rubyforge_project = "seeing_is_believing"
|
16
|
-
|
17
15
|
s.files = `git ls-files`.split("\n") - ['docs/seeing is believing.psd'] # remove psd b/c it boosts the gem size from 50kb to 20mb O.o
|
18
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
18
|
s.require_paths = ["lib"]
|
21
19
|
|
22
|
-
s.add_dependency "parser", "~> 2.
|
23
|
-
s.add_dependency "childprocess","~> 0.
|
20
|
+
s.add_dependency "parser", "~> 2.7.0"
|
21
|
+
s.add_dependency "childprocess","~> 3.0.0"
|
22
|
+
s.add_dependency "ffi", "~> 1.11.1"
|
24
23
|
|
25
24
|
s.add_development_dependency "pry"
|
26
25
|
s.add_development_dependency "haiti", ">= 0.1", "< 0.3"
|
27
|
-
s.add_development_dependency "rake", "~>
|
26
|
+
s.add_development_dependency "rake", "~> 13.0.0"
|
28
27
|
s.add_development_dependency "mrspec", "~> 0.3.1"
|
29
28
|
s.add_development_dependency "rspec", "~> 3.6.0"
|
30
29
|
s.add_development_dependency "cucumber", "~> 2.4"
|
data/spec/binary/config_spec.rb
CHANGED
@@ -257,8 +257,8 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
257
257
|
expect(parse([]).lib_options.require_files).to eq [matrix_file]
|
258
258
|
end
|
259
259
|
|
260
|
-
it 'appends pp
|
261
|
-
expect(parse(['-x']).lib_options.require_files).to eq [matrix_file, 'pp'
|
260
|
+
it 'appends pp for xmpfilter style' do
|
261
|
+
expect(parse(['-x']).lib_options.require_files).to eq [matrix_file, 'pp']
|
262
262
|
end
|
263
263
|
|
264
264
|
specify '-r and --require set an error if not provided with a filename' do
|
@@ -23,32 +23,32 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
23
23
|
def invoke(program, options={})
|
24
24
|
result = SeeingIsBelieving::Result.new
|
25
25
|
options[:event_handler] ||= SeeingIsBelieving::EventStream::Handlers::UpdateResult.new(result)
|
26
|
-
evaluator = described_class.new(program,
|
27
|
-
FileUtils.rm_f evaluator.
|
26
|
+
evaluator = described_class.new(filename, program, program, options)
|
27
|
+
FileUtils.rm_f evaluator.backup_path
|
28
28
|
evaluator.call
|
29
29
|
result
|
30
30
|
end
|
31
31
|
|
32
|
-
it 'evaluates the code
|
32
|
+
it 'evaluates the code as the given file' do
|
33
|
+
expect(invoke('print __FILE__').stdout).to eq filename
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'evaluates the code when the given file DNE' do
|
33
37
|
FileUtils.rm_f filename
|
34
38
|
expect(invoke('print 1').stdout).to eq '1'
|
35
39
|
end
|
36
40
|
|
37
|
-
it 'evaluates the code when the file Exists' do
|
41
|
+
it 'evaluates the code when the given file Exists' do
|
38
42
|
FileUtils.touch filename
|
39
43
|
expect(invoke('print 1').stdout).to eq '1'
|
40
44
|
end
|
41
45
|
|
42
46
|
it 'raises an error when the temp file already exists' do
|
43
|
-
evaluator = described_class.new('',
|
44
|
-
FileUtils.touch evaluator.
|
47
|
+
evaluator = described_class.new(filename, '', '', null_options)
|
48
|
+
FileUtils.touch evaluator.backup_path
|
45
49
|
expect { evaluator.call }.to raise_error SeeingIsBelieving::TempFileAlreadyExists
|
46
50
|
end
|
47
51
|
|
48
|
-
it 'evaluates the code as the given file' do
|
49
|
-
expect(invoke('print __FILE__').stdout).to eq filename
|
50
|
-
end
|
51
|
-
|
52
52
|
it 'does not change the original file' do
|
53
53
|
File.open(filename, 'w') { |f| f.write "ORIGINAL" }
|
54
54
|
invoke '1 + 1'
|
@@ -56,29 +56,29 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'uses HardCoreEnsure to move the file back' do
|
59
|
-
evaluator = described_class.new 'PROGRAM',
|
59
|
+
evaluator = described_class.new filename, 'PROGRAM', 'PROGRAM', null_options
|
60
60
|
File.open(filename, 'w') { |f| f.write 'ORIGINAL' }
|
61
|
-
FileUtils.rm_rf evaluator.
|
61
|
+
FileUtils.rm_rf evaluator.backup_path
|
62
62
|
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
63
63
|
# initial state
|
64
|
-
expect(File.exist? evaluator.
|
64
|
+
expect(File.exist? evaluator.backup_path).to eq false
|
65
65
|
expect(File.read filename).to eq 'ORIGINAL'
|
66
66
|
|
67
67
|
# after code
|
68
68
|
options[:code].call rescue nil
|
69
|
-
expect(File.read evaluator.
|
69
|
+
expect(File.read evaluator.backup_path).to eq 'ORIGINAL'
|
70
70
|
expect(File.read filename).to eq 'PROGRAM'
|
71
71
|
|
72
72
|
# after ensure
|
73
73
|
options[:ensure].call
|
74
74
|
expect(File.read filename).to eq 'ORIGINAL'
|
75
|
-
expect(File.exist? evaluator.
|
75
|
+
expect(File.exist? evaluator.backup_path).to eq false
|
76
76
|
end
|
77
77
|
evaluator.call
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'uses HardCoreEnsure to delete the file if it wrote it where one did not previously exist' do
|
81
|
-
evaluator = described_class.new 'PROGRAM',
|
81
|
+
evaluator = described_class.new filename, 'PROGRAM', 'PROGRAM', null_options
|
82
82
|
FileUtils.rm_rf filename
|
83
83
|
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
84
84
|
# initial state
|
@@ -149,7 +149,7 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
149
149
|
|
150
150
|
it 'must provide an event handler, which receives the process\'s events' do
|
151
151
|
# raises error
|
152
|
-
expect { described_class.new("",
|
152
|
+
expect { described_class.new(filename, "", "", {}) }
|
153
153
|
.to raise_error ArgumentError, /event_handler/
|
154
154
|
|
155
155
|
# sees all the events
|
@@ -164,6 +164,7 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
164
164
|
result = invoke <<-RUBY, timeout_seconds: 0.5
|
165
165
|
child_pid = spawn 'ruby', '-e', 'sleep' # child makes a grandchild which sleeps
|
166
166
|
puts Process.pid, child_pid # print ids so we can check they got killed
|
167
|
+
$stdout.flush
|
167
168
|
sleep # child sleeps
|
168
169
|
RUBY
|
169
170
|
post = Time.now
|
data/spec/event_stream_spec.rb
CHANGED
@@ -664,6 +664,7 @@ module SeeingIsBelieving::EventStream
|
|
664
664
|
[Events::StderrClosed , :stderr_closed],
|
665
665
|
[Events::EventStreamClosed, :event_stream_closed],
|
666
666
|
[Events::Finished , :finished],
|
667
|
+
[Events::FileLoaded , :file_loaded],
|
667
668
|
]
|
668
669
|
pairs.each { |klass, name| expect(klass.event_name).to eq name }
|
669
670
|
|
@@ -251,8 +251,8 @@ RSpec.describe SeeingIsBelieving do
|
|
251
251
|
# Currently we dont' differentiate between inline and multiline if statements,
|
252
252
|
# also, we can't wrap the whole statement since it's void value, which means we'd have to introduce
|
253
253
|
# the idea of multiple wrappings for the same line, which I just don't care enough about to consider
|
254
|
-
expect(values_for("def meth \n return 1 if true \n end \n meth")).to eq [[], ['1'], [], ['1']] # records true instead of 1
|
255
|
-
expect(values_for("def meth \n return 1 if false \n end \n meth")).to eq [[], ['nil'], [], ['nil']] # records false instead of nil
|
254
|
+
expect(values_for("def meth \n return 1 if true \n end \n meth")).to eq [[], ['1'], [':meth'], ['1']] # records true instead of 1
|
255
|
+
expect(values_for("def meth \n return 1 if false \n end \n meth")).to eq [[], ['nil'], [':meth'], ['nil']] # records false instead of nil
|
256
256
|
end
|
257
257
|
|
258
258
|
it 'does not try to record the keyword next' do
|
@@ -557,7 +557,7 @@ RSpec.describe SeeingIsBelieving do
|
|
557
557
|
it 'sees refined inspect (#128)' do
|
558
558
|
result = invoke <<-RUBY
|
559
559
|
module BinMeUp
|
560
|
-
refine
|
560
|
+
refine Integer do
|
561
561
|
def inspect
|
562
562
|
"%08b" % self
|
563
563
|
end
|
@@ -569,10 +569,6 @@ RSpec.describe SeeingIsBelieving do
|
|
569
569
|
expect(result[9]).to eq ['00000101']
|
570
570
|
end
|
571
571
|
|
572
|
-
it 'works when the exception does not have a backtrace (#134)' do
|
573
|
-
|
574
|
-
end
|
575
|
-
|
576
572
|
it 'respects timeout, even when children do semi-ridiculous things, it cleans up children rather than orphaning them' do
|
577
573
|
pre = Time.now
|
578
574
|
result = invoke <<-CHILD, timeout_seconds: 0.5
|
@@ -654,6 +650,18 @@ RSpec.describe SeeingIsBelieving do
|
|
654
650
|
end
|
655
651
|
end
|
656
652
|
|
653
|
+
# Okay, doesn't totally belong here, b/c there could be another implementation which doesn't overwrite files,
|
654
|
+
# but it requires coordination between the rewritten code and the evaluator, so isn't really an evaluator unit test,
|
655
|
+
# so putting this here.
|
656
|
+
context 'cleaning up files' do
|
657
|
+
it 'replaces the rewritten source with the original source as soon as it sees a result' do
|
658
|
+
program = "sleep 0.01 while File.read(__FILE__).include?('RECORD_RESULT'.downcase); File.read __FILE__\n"
|
659
|
+
result = invoke program
|
660
|
+
expect(result[1]).to eq [program.inspect]
|
661
|
+
expect(File.exist? result.filename).to eq false
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
657
665
|
|
658
666
|
context 'when given a debugger' do
|
659
667
|
let(:stream) { StringIO.new }
|
@@ -823,23 +831,19 @@ RSpec.describe SeeingIsBelieving do
|
|
823
831
|
').stderr).to eq ''
|
824
832
|
end
|
825
833
|
|
826
|
-
specify 'when
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
expect(result.stderr).to eq ''
|
840
|
-
expect(result.exitstatus).to eq 0
|
841
|
-
expect(result.to_a.last).to eq ['"a"']
|
842
|
-
end
|
834
|
+
specify 'when Integer does not have <, <<, next, ==, inspect, to_s' do
|
835
|
+
result = invoke('class Integer
|
836
|
+
undef <
|
837
|
+
undef <<
|
838
|
+
undef ==
|
839
|
+
undef next
|
840
|
+
undef to_s
|
841
|
+
undef inspect
|
842
|
+
end
|
843
|
+
"a"')
|
844
|
+
expect(result.stderr).to eq ''
|
845
|
+
expect(result.exitstatus).to eq 0
|
846
|
+
expect(result.to_a.last).to eq ['"a"']
|
843
847
|
end
|
844
848
|
|
845
849
|
specify 'when Integer does not have <, <<, next, ==, inspect, to_s' do
|
@@ -1015,5 +1019,76 @@ RSpec.describe SeeingIsBelieving do
|
|
1015
1019
|
end
|
1016
1020
|
').stderr).to eq ''
|
1017
1021
|
end
|
1022
|
+
|
1023
|
+
def executes_without_error!(code)
|
1024
|
+
result = invoke(code)
|
1025
|
+
expect(result.stderr).to eq ''
|
1026
|
+
begin
|
1027
|
+
expect(result).to_not have_exception, result.exception.pretty_inspect
|
1028
|
+
rescue NoMethodError
|
1029
|
+
require 'pp'
|
1030
|
+
retry
|
1031
|
+
end
|
1032
|
+
result
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
specify 'when constants are reassigned' do
|
1036
|
+
result = executes_without_error!('
|
1037
|
+
# producer
|
1038
|
+
Hash = "fake Hash"
|
1039
|
+
String = "fake String"
|
1040
|
+
class << String
|
1041
|
+
undef ===
|
1042
|
+
end
|
1043
|
+
Exception = "fake Exception"
|
1044
|
+
|
1045
|
+
kernel = Kernel
|
1046
|
+
Kernel = "fake Kernel"
|
1047
|
+
SystemExit = "fake SystemExit"
|
1048
|
+
NoMethodError = "fake NoMethodError"
|
1049
|
+
Marshal = "fake Marshal"
|
1050
|
+
TypeError = "fake TypeError"
|
1051
|
+
|
1052
|
+
# only matter when forking
|
1053
|
+
Thread = "fake Thread"
|
1054
|
+
IOError = "fake IOError"
|
1055
|
+
Errno::EPIPE = "fake Errno::EPIPE" # we don\'t actually put it in a situation to hit this
|
1056
|
+
Errno = "fake Errno"
|
1057
|
+
|
1058
|
+
# the matrix
|
1059
|
+
Exception = "Exception"
|
1060
|
+
Symbol = "Symbol"
|
1061
|
+
|
1062
|
+
# normal inspect
|
1063
|
+
1 + 1
|
1064
|
+
|
1065
|
+
# force it down the sad path
|
1066
|
+
obj = Object.new
|
1067
|
+
def obj.inspect
|
1068
|
+
"pass"
|
1069
|
+
end
|
1070
|
+
obj
|
1071
|
+
puts __LINE__-1 # so we know where to assert
|
1072
|
+
|
1073
|
+
NotAString = Module.new
|
1074
|
+
def obj.inspect
|
1075
|
+
NotAString
|
1076
|
+
end
|
1077
|
+
obj
|
1078
|
+
|
1079
|
+
def obj.inspect
|
1080
|
+
raise "whoops"
|
1081
|
+
end
|
1082
|
+
obj
|
1083
|
+
puts __LINE__-1 # should be replaced with Kernel#inspect
|
1084
|
+
|
1085
|
+
kernel.exit 0
|
1086
|
+
')
|
1087
|
+
|
1088
|
+
pass_str, kernel_inspect = result.stdout.lines.map(&:to_i)
|
1089
|
+
expect(result[pass_str]).to eq ['pass']
|
1090
|
+
expect(result[kernel_inspect].size).to eq 1
|
1091
|
+
expect(result[kernel_inspect][0]).to match /^#<Object/
|
1092
|
+
end
|
1018
1093
|
end
|
1019
1094
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SibSpecHelpers
|
2
|
+
class Version
|
3
|
+
attr_reader :segments
|
4
|
+
include Comparable
|
5
|
+
def initialize(version_string)
|
6
|
+
@segments = version_string.scan(/\d+/).map(&:to_i)
|
7
|
+
end
|
8
|
+
def <=>(other)
|
9
|
+
other = Version.new other unless other.kind_of? Version
|
10
|
+
segments.zip(other.segments).each do |ours, theirs|
|
11
|
+
return 1 if theirs.nil? || theirs < ours
|
12
|
+
return -1 if ours < theirs
|
13
|
+
end
|
14
|
+
segments.length <=> other.segments.length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|