seeing_is_believing 3.6.1 → 4.0.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/.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
|
-

|
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
|