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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4cc5ca0f30eacf4bb2729e295c557de2dcc20acb856389dda16f776bb0df2ad
|
4
|
+
data.tar.gz: e5711ae5d11fff1d65259beb1f65f9bbf6fba5e2d1494e22394d652fac26dfa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c00e05b913ad9122b4f2a76146c447868cec4b53dad4f8cc71ee6ef8e0607005c9b6cf8c31bd5e439096ea52ee445f71ff06be44872b65b4e6ce00a3931cfe64
|
7
|
+
data.tar.gz: 155ee68e5acaaf7b9d6e813bb3f1710b759c9b093a467c8aeb9d84f4df298716ab1cfba2cab504b75c9b98aad8f6d0d6146ae13be35db1f26dbacfa70e035e14
|
data/.travis.yml
CHANGED
data/Rakefile
CHANGED
@@ -42,6 +42,8 @@ task cuke: :bundle do
|
|
42
42
|
sh 'ruby', '--disable-gem',
|
43
43
|
*require_paths,
|
44
44
|
'-S', 'bundle/bin/cucumber',
|
45
|
+
'--quiet', # omit stepdefs, which are usually spammy for me https://twitter.com/josh_cheek/status/1082767053071765504
|
46
|
+
'--fail-fast', # stop as soon as we see an error (don't waste CI time, don't make it hard to find error info later)
|
45
47
|
'--tags', '~@not-implemented',
|
46
48
|
'--tags', "~@not-#{RUBY_VERSION}",
|
47
49
|
'--tags', "~@not-#{ruby_version_without_patchlevel}",
|
data/Readme.md
CHANGED
@@ -8,7 +8,7 @@ Evaluates Ruby code, recording the results of each line.
|
|
8
8
|
Integrates with any extensible editor (I've integrated it with many already, see [the list](https://github.com/JoshCheek/seeing_is_believing/wiki/Editor-Integration)).
|
9
9
|
If you like Swift Playgrounds, you'll like SiB.
|
10
10
|
|
11
|
-
![example](
|
11
|
+
![example](docs/example.gif)
|
12
12
|
|
13
13
|
|
14
14
|
Helpful links
|
data/appveyor.yml
CHANGED
data/features/regression.feature
CHANGED
@@ -267,6 +267,8 @@ Feature:
|
|
267
267
|
"""
|
268
268
|
|
269
269
|
|
270
|
+
# ChildProcess doesn't seem to handle this well in Windows. IDK.
|
271
|
+
@not-windows
|
270
272
|
Scenario: Long DATA segment in a valid file
|
271
273
|
Given the file "long_valid_data_segment.rb":
|
272
274
|
"""
|
@@ -434,6 +436,9 @@ Feature:
|
|
434
436
|
"""
|
435
437
|
|
436
438
|
|
439
|
+
# 2.4 and 2.5 don't correclty detect deadlock on Windows: https://bugs.ruby-lang.org/issues/16110
|
440
|
+
@not-2.4
|
441
|
+
@not-2.5
|
437
442
|
Scenario: Deadlocked
|
438
443
|
Given the file "deadlocked.rb":
|
439
444
|
"""
|
@@ -472,6 +477,8 @@ Feature:
|
|
472
477
|
# but just generally that it doesn't fkn blow up
|
473
478
|
@not-2.4
|
474
479
|
@not-2.5
|
480
|
+
@not-2.6
|
481
|
+
@not-2.7
|
475
482
|
Scenario: Old JSON bug
|
476
483
|
Given the file "json_and_encodings.rb":
|
477
484
|
"""
|
data/lib/seeing_is_believing.rb
CHANGED
@@ -43,8 +43,9 @@ class SeeingIsBelieving
|
|
43
43
|
options.debugger.context("REWRITTEN PROGRAM") { new_program }
|
44
44
|
|
45
45
|
EvaluateByMovingFiles.call \
|
46
|
-
new_program,
|
47
46
|
filename,
|
47
|
+
@program,
|
48
|
+
new_program,
|
48
49
|
event_handler: event_handler(options.debugger, options.event_handler),
|
49
50
|
provided_input: options.stdin,
|
50
51
|
require_files: options.require_files,
|
@@ -46,6 +46,7 @@ class SeeingIsBelieving
|
|
46
46
|
should_pp = false
|
47
47
|
WrapExpressions.call \
|
48
48
|
program,
|
49
|
+
before_all: -> { "BEGIN { $SiB.file_loaded };" },
|
49
50
|
before_each: -> line_number {
|
50
51
|
inspect = "$SiB.record_result(:inspect, #{line_number}, ("
|
51
52
|
pp = "$SiB.record_result(:pp, #{line_number}, ("
|
@@ -17,7 +17,7 @@ class SeeingIsBelieving
|
|
17
17
|
def call
|
18
18
|
@call ||= begin
|
19
19
|
commentable_lines = CommentableLines.new raw_code
|
20
|
-
commentable_lines.call.each do |line_number, (index_of_newline,
|
20
|
+
commentable_lines.call.each do |line_number, (index_of_newline, _col)|
|
21
21
|
first_index = last_index = index_of_newline
|
22
22
|
first_index -= 1 while first_index > 0 && raw_code[first_index-1] != "\n"
|
23
23
|
comment_text = commenter.call raw_code[first_index...last_index], line_number
|
@@ -54,13 +54,13 @@ class SeeingIsBelieving
|
|
54
54
|
def remove_lines_whose_newline_is_escaped(line_num_to_location)
|
55
55
|
ors_indexes = code_obj.indexes_of_ors_at_eol
|
56
56
|
line_num_to_location
|
57
|
-
.select { |line_number, (index_of_newline,
|
57
|
+
.select { |line_number, (index_of_newline, _col)|
|
58
58
|
code[index_of_newline-1] == '\\'
|
59
59
|
}
|
60
|
-
.reject { |line_number, (index_of_newline,
|
60
|
+
.reject { |line_number, (index_of_newline, _col)|
|
61
61
|
ors_indexes.include? index_of_newline
|
62
62
|
}
|
63
|
-
.each { |line_number, (
|
63
|
+
.each { |line_number, (_index_of_newline, _col)|
|
64
64
|
line_num_to_location.delete line_number
|
65
65
|
}
|
66
66
|
end
|
@@ -73,8 +73,8 @@ class SeeingIsBelieving
|
|
73
73
|
begin_pos = comment.location.expression.begin_pos
|
74
74
|
end_pos = comment.location.expression.end_pos
|
75
75
|
range = begin_pos...end_pos
|
76
|
-
line_num_to_location.select { |line_number, (index_of_newline,
|
77
|
-
.each { |line_number, (
|
76
|
+
line_num_to_location.select { |line_number, (index_of_newline, _col)| range.include? index_of_newline }
|
77
|
+
.each { |line_number, (_index_of_newline, _col)| line_num_to_location.delete line_number }
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -82,8 +82,8 @@ class SeeingIsBelieving
|
|
82
82
|
def remove_lines_inside_of_strings_and_things(line_num_to_location, ast)
|
83
83
|
invalid_boundaries = ranges_of_atomic_expressions ast, []
|
84
84
|
invalid_boundaries.each do |invalid_boundary|
|
85
|
-
line_num_to_location.select { |line_number, (index_of_newline,
|
86
|
-
.each { |line_number, (
|
85
|
+
line_num_to_location.select { |line_number, (index_of_newline, _col)| invalid_boundary.include? index_of_newline }
|
86
|
+
.each { |line_number, (_index_of_newline, _col)| line_num_to_location.delete line_number }
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
@@ -108,7 +108,6 @@ class SeeingIsBelieving
|
|
108
108
|
self.lib_options.rewrite_code = AnnotateMarkedLines.code_rewriter(markers)
|
109
109
|
self.remove_value_prefixes = false
|
110
110
|
self.lib_options.require_files << 'pp'
|
111
|
-
self.lib_options.require_files << 'seeing_is_believing/customize_pp'
|
112
111
|
|
113
112
|
when '--toggle-mark'
|
114
113
|
extract_positive_int_for.call arg do |n|
|
@@ -313,8 +312,7 @@ class SeeingIsBelieving
|
|
313
312
|
end
|
314
313
|
|
315
314
|
def Binary.help_screen(markers)
|
316
|
-
value
|
317
|
-
stdout = markers[:stdout][:prefix]
|
315
|
+
value = markers[:value][:prefix]
|
318
316
|
|
319
317
|
<<FLAGS
|
320
318
|
Usage: seeing_is_believing [options] [filename]
|
@@ -76,7 +76,7 @@ class SeeingIsBelieving
|
|
76
76
|
[annotation, comment]
|
77
77
|
}
|
78
78
|
.slice_before { |annotation, comment| annotation } # annotations begin chunks
|
79
|
-
.select { |(annotation,
|
79
|
+
.select { |(annotation, _start), *| annotation } # discard chunks not beginning with an annotation (probably can only happens on first comment)
|
80
80
|
.map { |(annotation, start), *rest| # end the chunk if the comment doesn't meet nextline criteria
|
81
81
|
nextline_comments = []
|
82
82
|
prev = start
|
@@ -91,8 +91,8 @@ class SeeingIsBelieving
|
|
91
91
|
def indexes_of_ors_at_eol
|
92
92
|
Set.new(
|
93
93
|
@tokens.select { |type, *| type == :tGVAR }
|
94
|
-
.select { |_, (var,
|
95
|
-
.map { |_, (
|
94
|
+
.select { |_, (var, _range)| var == '$\\'.freeze }
|
95
|
+
.map { |_, (_var, range)| range.end_pos }
|
96
96
|
)
|
97
97
|
end
|
98
98
|
|
@@ -106,7 +106,7 @@ class SeeingIsBelieving
|
|
106
106
|
|
107
107
|
def body_range_from_tokens(tokens)
|
108
108
|
return range_for(0, 0) if raw.start_with? "__END__\n"
|
109
|
-
(name, (_, range)) = tokens.max_by { |name, (
|
109
|
+
(name, (_, range)) = tokens.max_by { |name, (_data, range)| range.end_pos } ||
|
110
110
|
[nil, [nil, range_for(0, 1)]]
|
111
111
|
end_pos = range.end_pos
|
112
112
|
end_pos += 1 if name == :tCOMMENT || name == :tSTRING_END || name == :tSEMI
|
@@ -17,8 +17,9 @@ require "childprocess"
|
|
17
17
|
|
18
18
|
require 'seeing_is_believing/result'
|
19
19
|
require 'seeing_is_believing/debugger'
|
20
|
-
require 'seeing_is_believing/
|
20
|
+
require 'seeing_is_believing/swap_files'
|
21
21
|
require 'seeing_is_believing/event_stream/consumer'
|
22
|
+
require 'seeing_is_believing/event_stream/events'
|
22
23
|
|
23
24
|
ChildProcess.posix_spawn = true # forking locks up for some reason when we run SiB inside of SiB
|
24
25
|
|
@@ -28,13 +29,14 @@ class SeeingIsBelieving
|
|
28
29
|
new(*args).call
|
29
30
|
end
|
30
31
|
|
31
|
-
attr_accessor :
|
32
|
+
attr_accessor :user_program, :rewritten_program, :provided_input, :require_flags, :load_path_flags, :encoding, :timeout_seconds, :debugger, :event_handler, :max_line_captures
|
32
33
|
|
33
|
-
attr_accessor :file_directory, :file_path, :local_cwd, :relative_filename, :
|
34
|
+
attr_accessor :file_directory, :file_path, :local_cwd, :relative_filename, :backup_path
|
34
35
|
|
35
|
-
def initialize(
|
36
|
+
def initialize(file_path, user_program, rewritten_program, options={})
|
36
37
|
options = options.dup
|
37
|
-
self.
|
38
|
+
self.user_program = user_program
|
39
|
+
self.rewritten_program = rewritten_program
|
38
40
|
self.encoding = options.delete(:encoding) || "u"
|
39
41
|
self.timeout_seconds = options.delete(:timeout_seconds) || 0 # 0 is the new infinity
|
40
42
|
self.provided_input = options.delete(:provided_input) || String.new
|
@@ -47,26 +49,20 @@ class SeeingIsBelieving
|
|
47
49
|
self.file_directory = File.dirname file_path
|
48
50
|
file_name = File.basename file_path
|
49
51
|
self.relative_filename = local_cwd ? file_name : file_path
|
50
|
-
self.
|
52
|
+
self.backup_path = File.join file_directory, "seeing_is_believing_backup.#{file_name}"
|
51
53
|
|
52
54
|
options.any? && raise(ArgumentError, "Unknown options: #{options.inspect}")
|
53
55
|
end
|
54
56
|
|
55
57
|
def call
|
56
|
-
|
57
|
-
|
58
|
-
evaluate_file
|
58
|
+
SwapFiles.call file_path, backup_path, user_program, rewritten_program do |swap_files|
|
59
|
+
evaluate_file swap_files
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
63
|
private
|
63
64
|
|
64
|
-
def
|
65
|
-
File.open(file_path, 'w', external_encoding: "utf-8") { |f| f.write program.to_s }
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
def evaluate_file
|
65
|
+
def evaluate_file(swap_files)
|
70
66
|
event_server = TCPServer.new(0) # dynamically allocates an available port
|
71
67
|
|
72
68
|
# setup streams
|
@@ -78,7 +74,7 @@ class SeeingIsBelieving
|
|
78
74
|
[Marshal.dump(
|
79
75
|
event_stream_port: event_server.addr[1],
|
80
76
|
max_line_captures: max_line_captures,
|
81
|
-
num_lines:
|
77
|
+
num_lines: user_program.lines.count,
|
82
78
|
filename: relative_filename,
|
83
79
|
)].pack('m0')
|
84
80
|
|
@@ -113,7 +109,12 @@ class SeeingIsBelieving
|
|
113
109
|
|
114
110
|
# set up the event consumer
|
115
111
|
consumer = EventStream::Consumer.new(events: eventstream, stdout: stdout, stderr: stderr)
|
116
|
-
consumer_thread = Thread.new
|
112
|
+
consumer_thread = Thread.new do
|
113
|
+
consumer.each do |e|
|
114
|
+
swap_files.show_user_program if e.is_a? SeeingIsBelieving::EventStream::Events::FileLoaded
|
115
|
+
event_handler.call e
|
116
|
+
end
|
117
|
+
end
|
117
118
|
|
118
119
|
# wait for completion
|
119
120
|
if timeout_seconds == 0
|
@@ -131,13 +132,19 @@ class SeeingIsBelieving
|
|
131
132
|
# On Windows, we need to call stop if there is an error since it interrupted
|
132
133
|
# the previos waiting/polling. If we don't call stop, in that situation, it will
|
133
134
|
# leave orphan processes. On Unix, we need to always call stop or it may leave orphans
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
begin
|
136
|
+
if ChildProcess.unix?
|
137
|
+
child.stop
|
138
|
+
elsif $!
|
139
|
+
child.stop
|
140
|
+
consumer.process_exitstatus(child.exit_code)
|
141
|
+
end
|
142
|
+
child.alive? && child.stop
|
143
|
+
rescue ChildProcess::Error
|
144
|
+
# On AppVeyor, I keep getting errors
|
145
|
+
# The handle is invalid: https://ci.appveyor.com/project/JoshCheek/seeing-is-believing/build/22
|
146
|
+
# Access is denied: https://ci.appveyor.com/project/JoshCheek/seeing-is-believing/build/24
|
139
147
|
end
|
140
|
-
cleanup_run(child)
|
141
148
|
close_streams(stdout, stderr, eventstream, event_server)
|
142
149
|
end
|
143
150
|
|
@@ -154,14 +161,5 @@ class SeeingIsBelieving
|
|
154
161
|
def close_streams(*streams)
|
155
162
|
streams.each { |io| io.close unless io.closed? }
|
156
163
|
end
|
157
|
-
|
158
|
-
# On AppVeyor, I keep getting errors
|
159
|
-
# The handle is invalid: https://ci.appveyor.com/project/JoshCheek/seeing-is-believing/build/22
|
160
|
-
# Access is denied: https://ci.appveyor.com/project/JoshCheek/seeing-is-believing/build/24
|
161
|
-
def cleanup_run(child, *streams)
|
162
|
-
child.alive? && child.stop
|
163
|
-
rescue ChildProcess::Error
|
164
|
-
# noop
|
165
|
-
end
|
166
164
|
end
|
167
165
|
end
|
@@ -208,6 +208,8 @@ class SeeingIsBelieving
|
|
208
208
|
Events::RubyVersion.new(value: shift_string(line))
|
209
209
|
when :filename
|
210
210
|
Events::Filename.new(value: shift_string(line))
|
211
|
+
when :file_loaded
|
212
|
+
Events::FileLoaded.new
|
211
213
|
when :exec
|
212
214
|
Events::Exec.new(args: shift_string(line))
|
213
215
|
else
|
@@ -58,6 +58,13 @@ class SeeingIsBelieving
|
|
58
58
|
attributes :value
|
59
59
|
end
|
60
60
|
|
61
|
+
# For knowing when you can perform certain tasks (eg moving the file back)
|
62
|
+
class FileLoaded < Event
|
63
|
+
def self.event_name
|
64
|
+
:file_loaded
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
61
68
|
# Number of lines in the program.
|
62
69
|
class NumLines < Event
|
63
70
|
def self.event_name
|
@@ -7,8 +7,8 @@ class SeeingIsBelieving
|
|
7
7
|
attr_reader :exitstatus
|
8
8
|
attr_reader :timeout_seconds
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(next_handler)
|
11
|
+
@next_handler = next_handler
|
12
12
|
end
|
13
13
|
|
14
14
|
def call(event)
|
@@ -18,7 +18,7 @@ class SeeingIsBelieving
|
|
18
18
|
when Events::Timeout
|
19
19
|
@timeout_seconds = event.seconds
|
20
20
|
end
|
21
|
-
@
|
21
|
+
@next_handler.call(event)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -7,6 +7,16 @@ using SeeingIsBelieving::Safe
|
|
7
7
|
class SeeingIsBelieving
|
8
8
|
module EventStream
|
9
9
|
class Producer
|
10
|
+
|
11
|
+
# Guarding against hostile users (e.g. me) that do ridiculous things like blowing away these constants
|
12
|
+
old_w, $-w = $-w, nil # Ruby warns about accessing deprecated constants
|
13
|
+
Object.constants.each do |name|
|
14
|
+
const_set name, Object.const_get(name)
|
15
|
+
end
|
16
|
+
$-w = old_w
|
17
|
+
|
18
|
+
ErrnoEPIPE = Errno::EPIPE # not actually tested, but we can see it is referenced below
|
19
|
+
|
10
20
|
module NullQueue
|
11
21
|
extend self
|
12
22
|
Queue.instance_methods(false).each do |name|
|
@@ -40,6 +50,10 @@ class SeeingIsBelieving
|
|
40
50
|
queue << "max_line_captures #{max_line_captures}"
|
41
51
|
end
|
42
52
|
|
53
|
+
def file_loaded
|
54
|
+
queue << "file_loaded"
|
55
|
+
end
|
56
|
+
|
43
57
|
StackErrors = [SystemStackError]
|
44
58
|
StackErrors << Java::JavaLang::StackOverflowError if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
|
45
59
|
def record_result(type, line_number, value)
|
@@ -50,13 +50,7 @@ class SeeingIsBelieving
|
|
50
50
|
alias to_str to_str
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
# to detect this. eg defined?(Fixnum) returns "constant". Accessing it
|
55
|
-
# leads to a warning, but SiB turns warnings off so you don't see it.
|
56
|
-
# So.... for now, it incidentally doesn't do anything annoying, but would
|
57
|
-
# be good to figure out something better (eg if we ever wanted to allow the
|
58
|
-
# user to decide whether warnings should display or not)
|
59
|
-
refine Fixnum do
|
53
|
+
refine Integer do
|
60
54
|
alias to_s to_s
|
61
55
|
alias next next
|
62
56
|
alias < <
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'seeing_is_believing/error'
|
2
|
+
require 'seeing_is_believing/hard_core_ensure'
|
3
|
+
|
4
|
+
class SeeingIsBelieving
|
5
|
+
class SwapFiles
|
6
|
+
# Might honeslty make more sense to break this out into 2 different classes.
|
7
|
+
# We've got to do some confusing state accounting since there are really 2
|
8
|
+
# algorithms here:
|
9
|
+
#
|
10
|
+
# if the file exists:
|
11
|
+
# make sure there isn't a backup (could cause the user to lose their file)
|
12
|
+
# back the file up
|
13
|
+
# write the rewritten code to the file
|
14
|
+
# if we are told to show the user program
|
15
|
+
# move the backup over the top of the rewritten file
|
16
|
+
# do nothing in the ensure block
|
17
|
+
# else
|
18
|
+
# in the ensure block: move the backup over the top of the rewritten file
|
19
|
+
# if the file DNE:
|
20
|
+
# write the rewritten code to the file
|
21
|
+
# if we are told to show the user program
|
22
|
+
# write the user program to the file
|
23
|
+
# delete the file in the ensure block
|
24
|
+
def self.call(*args, &block)
|
25
|
+
new(*args, &block).call
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(file_path, backup_path, user_program, rewritten_program, &block)
|
29
|
+
self.file_path = file_path
|
30
|
+
self.block = block
|
31
|
+
self.backup_path = backup_path
|
32
|
+
self.user_program = user_program
|
33
|
+
self.rewritten_program = rewritten_program
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
HardCoreEnsure.call \
|
38
|
+
code: -> {
|
39
|
+
File.exist? backup_path and
|
40
|
+
raise TempFileAlreadyExists.new(file_path, backup_path)
|
41
|
+
|
42
|
+
@has_file = File.exist? file_path
|
43
|
+
|
44
|
+
if @has_file
|
45
|
+
File.rename file_path, backup_path
|
46
|
+
@needs_restore = true
|
47
|
+
end
|
48
|
+
|
49
|
+
save_file rewritten_program
|
50
|
+
|
51
|
+
block.call self
|
52
|
+
},
|
53
|
+
ensure: -> {
|
54
|
+
set_back_to_initial_conditions
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def show_user_program
|
59
|
+
if @needs_restore
|
60
|
+
restore
|
61
|
+
else
|
62
|
+
save_file user_program
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
attr_accessor :block, :file_path, :backup_path, :rewritten_program, :user_program
|
69
|
+
|
70
|
+
def restore
|
71
|
+
File.rename(backup_path, file_path)
|
72
|
+
@needs_restore = false
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def save_file(program)
|
77
|
+
File.open file_path, 'w', external_encoding: "utf-8" do |f|
|
78
|
+
f.write program.to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_back_to_initial_conditions
|
83
|
+
if @needs_restore
|
84
|
+
restore
|
85
|
+
elsif !@has_file
|
86
|
+
File.delete(file_path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|