pry-timetravel 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 036cd80689a69996c2e965251b5eea0d8ad65958
4
- data.tar.gz: 4469ac880bb66bcea64873f4cc423f06c2603a76
3
+ metadata.gz: 3289f1ed4dd5e11a532a3d191c836616c520f5fa
4
+ data.tar.gz: 4cca9997562fb1a4ec503a758a869918981056df
5
5
  SHA512:
6
- metadata.gz: 948b4b85a37039e6829ff9a511ce3cf9c40f3c28d10b0f62e3d9726089c7e2280a9350553f903e15cbce77a36efc5334a1cd917a6c0cb23c0e35fd835204c727
7
- data.tar.gz: c045d71745d255a7e3c37978cc212a666cde0e40adb625deded075b8323283fc12c070b7377e7b8dcbb3267ba50c05cc97e43f24740d8e6c1c95d62b92e62a75
6
+ metadata.gz: 2cc90461d45f52950855ddd68fea6ce8182bb53a1baae82e64def62866ead46512dbab867b4b46cc5a92e11f20868e26876e97d2f2315cdb3a4754f08aea0284
7
+ data.tar.gz: 9d76b24d9980686987bf3b85a9d881652bc1eee7ab4ccc6274017bc8bc568a5ff4b2e6dfc9ff0dee54632b5fab99e2b249272a6683b103fadc7d4bb6d9f5ba18
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format d
@@ -0,0 +1,4 @@
1
+ ## 0.0.4 released 2017-01-02
2
+ * Add auto-snapshot mode via `snap -a`
3
+ * Include `snap next` and `snap step` aliases by default
4
+
data/README.md CHANGED
@@ -28,6 +28,10 @@ WARNING: Time travel may cause zombies.
28
28
 
29
29
  ## KNOWN ISSUES
30
30
 
31
+ ### General
32
+
33
+ * If you close a connection, like to a DB, it is closed for all snapshots
34
+
31
35
  ### Redis fork detection
32
36
 
33
37
  Redis checks to see if you forked and yells at you about needing to reconnect.
@@ -44,8 +48,12 @@ this to bypass safety measures:
44
48
  end
45
49
  end
46
50
 
47
- Maybe this will be a default hack that gets activated on your first snapshot at
48
- some point in the near future.
51
+ ## Similar Technology
52
+
53
+ * [Time Travel Python Debugger](https://github.com/TomOnTime/timetravelpdb)
54
+ * [Elm's Time Traveling Debugger](http://debug.elm-lang.org/)
55
+ * [OCaml Time Travel](http://caml.inria.fr/pub/docs/manual-ocaml-4.00/manual030.html#htoc195)
56
+ * [Chronon (java - proprietary)](http://chrononsystems.com/)
49
57
 
50
58
  ## Meta
51
59
 
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
- # require 'rspec/core/rake_task'
1
+ require 'rspec/core/rake_task'
2
2
 
3
3
  task :default => :test
4
4
  task :spec => :test
5
5
 
6
- # RSpec::Core::RakeTask.new(:test)
6
+ RSpec::Core::RakeTask.new(:test)
7
7
 
8
8
  task :build do
9
9
  sh 'gem build *.gemspec'
@@ -4,6 +4,15 @@ require 'json'
4
4
 
5
5
  require_relative 'pry-timetravel/commands'
6
6
 
7
+ # = Timetravel!
8
+ #
9
+ # This is a pry plugin that keeps a pool of fork()ed process checkpoints, so
10
+ # that you can jump "back in time" to a previous place in your execution.
11
+ #
12
+ # == Forking Model
13
+ #
14
+ # When you create a snapshot, fork() is executed. The parent (original) process
15
+ # is suspended, and the new child process picks up where that left off.
7
16
  class PryTimetravel
8
17
  class << self
9
18
 
@@ -15,6 +24,7 @@ class PryTimetravel
15
24
  end
16
25
  end
17
26
 
27
+ # TODO: Should probably delay calling this until first snapshot
18
28
  at_exit do
19
29
  PryTimetravel.dlog "at_exit"
20
30
  if $root_parent && $$ != $root_parent
@@ -24,136 +34,200 @@ class PryTimetravel
24
34
  end
25
35
 
26
36
  def enter_suspended_animation
27
- dlog("Installing SIGCONT trap")
37
+ dlog("Suspend: Installing SIGCONT trap")
28
38
  old_sigcont_handler = Signal.trap('CONT') do
29
39
  dlog("Got a SIGCONT")
30
40
  end
31
41
 
32
- dlog("Installing SIGEXIT trap")
42
+ dlog("Suspend: Installing SIGEXIT trap")
33
43
  old_sigexit_handler = Signal.trap('EXIT') do
34
44
  dlog("got EXIT")
35
45
  Kernel.exit! true
36
46
  end
37
47
 
38
- dlog("Stopping myself")
48
+ dlog("Suspend: Stopping myself")
39
49
  Process.kill 'SIGSTOP', $$
40
- dlog("Back from SIGSTOP!")
41
50
 
42
- @snap_tree = JSON.parse(File.read("/tmp/timetravel_#{$root_parent}.json"))
51
+ dlog("Resume: Back from SIGSTOP! Loading snapshot tree")
52
+ load_global_timetravel_state
43
53
 
44
- dlog("Returning to old SIGCONT")
54
+ dlog("Resume: Returning to old SIGCONT")
45
55
  Signal.trap('CONT', old_sigcont_handler || "DEFAULT")
46
56
 
47
- dlog("Returning to old SIGEXIT")
57
+ dlog("Resume: Returning to old SIGEXIT")
48
58
  Signal.trap('EXIT', old_sigexit_handler || "DEFAULT")
49
59
  end
50
60
 
61
+ # The root parent is a sort of overseer of the whole tree, and something
62
+ # that is still alive and waiting for any signals from the outside world.
63
+ #
64
+ # Once any time travel starts, the parent forks and waits for its one and only direct child. All the other action happens in that child and its own children.
65
+ #
66
+ # If you USR1 this root parent, it will try to clean up the entire tree.
67
+ #
68
+ # It ignores INT, so that if you ^C the process it won't die so quickly.
51
69
  def start_root_parent
52
70
  $root_parent = $$
53
71
  child_pid = fork
54
72
  if child_pid
55
73
  Signal.trap('INT') do
56
- dlog("root-parent got INT, ignoring")
74
+ dlog("Root-parent: Got INT, ignoring")
57
75
  end
58
76
  Signal.trap('USR1') do
59
- dlog("root-parent got USR1, exiting")
60
- FileUtils.rm("/tmp/timetravel_#{$root_parent}.json")
77
+ dlog("Root-parent: Got USR1, exiting")
78
+ cleanup_global_timetravel_state
61
79
  Kernel.exit! true
62
80
  end
63
- dlog "Root parent waiting on #{child_pid}"
64
- # sleep
65
- # Process.waitall
81
+ dlog "Root parent: Waiting on child pid #{child_pid}"
66
82
  Process.waitpid child_pid
67
- # Process.waitpid 0
68
- dlog "Root parent exiting after wait"
83
+ dlog "Root parent: Exiting after wait"
84
+ cleanup_global_timetravel_state
69
85
  FileUtils.rm("/tmp/timetravel_#{$root_parent}.json")
70
86
  Kernel.exit! true
71
87
  end
72
88
  end
73
89
 
74
- def snapshot(target, parent = -> {}, child = -> {})
90
+ def auto_snapshot(target)
91
+ if !@do_trace
92
+ @trace = TracePoint.new(:line) do |tp|
93
+ # puts "trace status: #{$in_trace}"
94
+ if !$in_trace
95
+ $in_trace = true
96
+ # puts "path: #{File.expand_path(tp.path)}"
97
+ # puts "snapshotting trace"
98
+ # if ! (tp.defined_class.to_s =~ /Pry/)
99
+ # if tp.path =~ /<main>/
100
+ # if tp.path =~ /<main>/
101
+ # p [tp.path, tp.lineno, tp.event, tp.defined_class, Dir.pwd] # , tp.raised_exception]
102
+ # end
103
+ # if tp.path.include?(Dir.pwd) || tp.path =~ /<main>/
104
+ if File.expand_path(tp.path).include?(Dir.pwd)
105
+ p [tp.path, tp.lineno, tp.event, tp.defined_class, Dir.pwd] # , tp.raised_exception]
106
+ # p caller
107
+ # if tp.path =~ /<main>/
108
+ # # p tp
109
+ # p [tp.path, tp.lineno, tp.event] # , tp.raised_exception]
110
+ PryTimetravel.snapshot(
111
+ tp.binding,
112
+ # now_do: -> { run(args.join(" ")) unless args.empty? },
113
+ # on_return_do: -> { run('whereami') }
114
+ )
115
+ # end
116
+ end
117
+ $in_trace = false
118
+ end
119
+ end
120
+ @trace.enable
121
+ @do_trace = true
122
+ else
123
+ @trace.disable
124
+ @do_trace = false
125
+ end
126
+ end
127
+
128
+ def update_current_snapshot_info(target, parent_pid = nil)
129
+ my_pid = $$.to_s
130
+ @snap_tree ||= {}
131
+ @snap_tree[my_pid] ||= {}
132
+ @snap_tree[my_pid]["file"] = target.eval('__FILE__')
133
+ @snap_tree[my_pid]["line"] = target.eval('__LINE__')
134
+ @snap_tree[my_pid]["time"] = Time.now.to_f
135
+ @snap_tree[my_pid]["id"] = @id
136
+ @snap_tree[my_pid]["previous"] = parent_pid if parent_pid
137
+ end
138
+
139
+ def snapshot(target, opts = {})
140
+ opts[:now_do] ||= -> {}
141
+ opts[:on_return_do] ||= -> {}
75
142
 
76
143
  # We need a root-parent to keep the shell happy
77
144
  if ! $root_parent
78
145
  start_root_parent
79
146
  end
80
147
 
148
+ @id ||= 0
81
149
  @timetravel_root ||= $$
82
- @snap_tree ||= {
83
- $$.to_s => {
84
- "file" => target.eval('__FILE__'),
85
- "line" => target.eval('__LINE__'),
86
- }
87
- }
150
+ update_current_snapshot_info(target)
151
+ @id += 1
88
152
 
89
153
  parent_pid = $$
90
154
  child_pid = fork
91
155
 
92
156
  if child_pid
93
157
 
94
- dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}")
158
+ dlog("Snapshot: I am parent #{parent_pid}. I have a child pid #{child_pid}. Now suspending.")
95
159
  enter_suspended_animation
96
160
 
97
- # Perform child operation
98
- child.()
161
+ dlog("Snapshot: Back from suspended animation. Running on_return_do.")
162
+ # Perform operation now that we've come back
163
+ opts[:on_return_do].()
99
164
 
100
165
  else
101
166
 
102
167
  child_pid = $$
103
- dlog("I am child #{child_pid}: I have a parent pid #{parent_pid}")
168
+ dlog("Snapshot: I am child #{child_pid}. I have a parent pid #{parent_pid}")
104
169
 
105
- @snap_tree[child_pid.to_s] = {
106
- "previous" => parent_pid,
107
- "file" => target.eval('__FILE__'),
108
- "line" => target.eval('__LINE__'),
109
- }
170
+ update_current_snapshot_info(target, parent_pid)
110
171
 
111
- # Perform parent operation
112
- parent.()
172
+ # Perform immediate operation
173
+ dlog("Snapshot: Running now_do.")
174
+ opts[:now_do].()
113
175
 
114
176
  end
115
177
  end
116
178
 
117
- def snapshot_list(target, indent = "", node = @timetravel_root.to_s)
118
- # @snap_tree && @snap_tree.keys.join(" ")
179
+ def snapshot_list(target, indent = "", node = @timetravel_root.to_s, my_indent = nil)
180
+ if node == ""
181
+ return "No snapshots"
182
+ end
119
183
  return unless node && node != ""
120
184
 
121
- # This shouldn't be here
122
- @snap_tree[$$.to_s]["file"] = target.eval('__FILE__')
123
- @snap_tree[$$.to_s]["line"] = target.eval('__LINE__')
185
+ # Freshen the current snapshot so it looks right
186
+ update_current_snapshot_info(target)
124
187
 
125
- out = "#{indent}#{node} #{@snap_tree[node]["file"]} #{@snap_tree[node]["line"]} #{ node == $$.to_s ? '***' : ''}\n"
126
- @snap_tree.keys.select { |n|
127
- @snap_tree[n]["previous"] == node.to_i
128
- }.each do |n|
129
- out += snapshot_list(target, indent + " ", n)
188
+ code_line = IO.readlines(@snap_tree[node]["file"])[@snap_tree[node]["line"].to_i - 1].chomp
189
+
190
+ out = "#{my_indent || indent}#{node} (#{@snap_tree[node]["id"]}) #{@snap_tree[node]["file"]} #{@snap_tree[node]["line"]} #{ node == $$.to_s ? '***' : ''} #{code_line}\n"
191
+ children = @snap_tree.keys.select { |n| @snap_tree[n]["previous"] == node.to_i }
192
+ if children.length == 1
193
+ out += snapshot_list(target, indent, children[0])
194
+ else
195
+ children.each { |n|
196
+ out += snapshot_list(target, indent + " ", n, indent + "> ")
197
+ }
130
198
  end
199
+ # @snap_tree.keys.select { |n|
200
+ # @snap_tree[n]["previous"] == node.to_i
201
+ # }.each do |n|
202
+ # out += snapshot_list(target, indent + " ", n)
203
+ # end
131
204
  out
132
205
  end
133
206
 
134
- def restore_snapshot(target, target_pid = nil, count = 1)
135
- dlog("Thinking about time travel... $$");
207
+ def restore_snapshot(target, target_pid = nil)
208
+ dlog("Restore: Trying to restore,");
136
209
 
137
210
  if target_pid.nil? && @snap_tree && ! @snap_tree[$$.to_s].nil?
138
- count = 1 if count < 1
139
211
  target_pid = @snap_tree[$$.to_s]["previous"]
140
- @snap_tree[$$.to_s]["file"] = target.eval('__FILE__')
141
- @snap_tree[$$.to_s]["line"] = target.eval('__LINE__')
142
212
  else
143
213
  target_pid = target_pid
144
214
  end
145
215
 
146
- if target_pid
147
- dlog("ME #{$$}: I found a target pid #{target_pid}! TIME TRAVEL TIME")
216
+ if target_pid && @snap_tree[target_pid.to_s]
217
+ dlog("Restore: I found a target pid #{target_pid}! TIME TRAVEL TIME")
148
218
 
149
- File.open("/tmp/timetravel_#{$root_parent}.json", 'w') do |f|
150
- f.puts @snap_tree.to_json
151
- end
219
+ # Update our current information of our current running snapshot
220
+ update_current_snapshot_info(target)
221
+
222
+ save_global_timetravel_state
152
223
 
224
+ # Bring our target back to life
153
225
  Process.kill 'SIGCONT', target_pid
226
+
227
+ # Go to sleeeeeeeppppp
154
228
  enter_suspended_animation
155
229
  else
156
- dlog("I was unable to time travel. Maybe it is a myth.");
230
+ dlog("Restore: I was unable to time travel. Maybe it is a myth.");
157
231
  puts "No previous snapshot found."
158
232
  end
159
233
  end
@@ -162,6 +236,32 @@ class PryTimetravel
162
236
  restore_snapshot(@timetravel_root) if @timetravel_root
163
237
  end
164
238
 
239
+ def global_timetravel_state_filename
240
+ "/tmp/timetravel_#{$root_parent}.json"
241
+ end
242
+
243
+ def save_global_timetravel_state
244
+ File.open(global_timetravel_state_filename, 'w') do |f|
245
+ global_state = {
246
+ snap_tree: @snap_tree,
247
+ do_trace: @do_trace
248
+ }
249
+ f.puts global_state.to_json
250
+ end
251
+ end
252
+
253
+ def load_global_timetravel_state
254
+ global_state = JSON.parse(File.read(global_timetravel_state_filename))
255
+ @snap_tree = global_state["snap_tree"]
256
+ @do_trace = global_state["do_trace"]
257
+ dlog("Loaded: " + @snap_tree.to_json)
258
+ @id = (@snap_tree.values.map{|snap| snap['id']}.max || 0) + 1
259
+ end
260
+
261
+ def cleanup_global_timetravel_state
262
+ FileUtils.rm(global_timetravel_state_filename)
263
+ end
264
+
165
265
  end
166
266
  end
167
267
 
@@ -4,10 +4,11 @@ Pry::Commands.create_command "snap", "Create a snapshot that you can later retur
4
4
  group 'Timetravel'
5
5
  banner <<-'BANNER'
6
6
  Usage: snap [cmd]
7
- snap --list
7
+ snap -l|--list List existing snapshots
8
+ snap -a|--auto Automatically take new snapshots
8
9
 
9
10
  This will add a snapshot which you can return to later.
10
-
11
+
11
12
  If you provide [cmd] then that command will also be run -- nice for "snap next" in conjunction with pry-byebug.
12
13
  BANNER
13
14
 
@@ -17,17 +18,19 @@ Pry::Commands.create_command "snap", "Create a snapshot that you can later retur
17
18
  # :optional_argument => true, :as => Integer
18
19
  opt.on :l, :list,
19
20
  "Show a list of existing snapshots"
21
+ opt.on :a, :auto, "Automatically take snapshots!"
20
22
  end
21
23
  def process
22
24
  if opts.l?
23
25
  output.puts PryTimetravel.snapshot_list(target)
26
+ elsif opts.a?
27
+ output.puts PryTimetravel.auto_snapshot(target)
24
28
  else
25
29
  PryTimetravel.snapshot(
26
30
  target,
27
- -> { run(args.join(" ")) unless args.empty? },
28
- -> { run('whereami') }
31
+ now_do: -> { run(args.join(" ")) unless args.empty? },
32
+ on_return_do: -> { run('whereami') }
29
33
  )
30
- # run('whereami')
31
34
  end
32
35
  end
33
36
  end
@@ -49,7 +52,7 @@ Pry::Commands.create_command "back", "Go back to the most recent snapshot" do
49
52
  opt.on :home, "Jump to the end of the original execution sequence"
50
53
  end
51
54
  def process
52
- if opts.h?
55
+ if opts.home?
53
56
  PryTimetravel.restore_root_snapshot(target)
54
57
  else
55
58
  target_pid = args.first ? args.first.to_i : opts[:p]
@@ -58,3 +61,6 @@ Pry::Commands.create_command "back", "Go back to the most recent snapshot" do
58
61
  end
59
62
  end
60
63
 
64
+ Pry::Commands.alias_command 'n', 'snap next'
65
+ Pry::Commands.alias_command 's', 'snap step'
66
+ Pry::Commands.alias_command 'p', 'back'
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'pry-timetravel'
3
- s.version = '0.0.3'
3
+ s.version = '0.0.4'
4
4
  s.summary = 'Timetravel'
5
5
  s.description = 'Allows you to timetravel!'
6
6
  s.homepage = 'https://github.com/awwaiid/pry-timetravel'
@@ -12,8 +12,6 @@ Gem::Specification.new do |s|
12
12
  s.add_dependency 'pry'
13
13
 
14
14
  s.add_development_dependency 'rake'
15
- # s.add_development_dependency 'rspec'
16
- # s.add_development_dependency 'yard'
17
- # s.add_development_dependency 'redcarpet'
18
- # s.add_development_dependency 'capybara'
15
+ s.add_development_dependency 'rspec'
16
+ s.add_development_dependency 'pry-byebug'
19
17
  end
@@ -1,11 +1,126 @@
1
1
  require './spec/spec_helper'
2
+ require 'pty'
3
+ require 'expect'
2
4
 
3
- describe "pry-timetravel commands" do
4
- describe "checkpoint" do
5
- it "forks a child"
5
+ pry_timetravel_cmd = "TERM=dumb pry -f --no-color --no-pager --no-history --noprompt -s timetravel"
6
+
7
+ describe "pry-timetravel" do
8
+ it "starts with no snapshots" do
9
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer|
10
+ writer.puts("snap --list")
11
+ found = reader.expect(/No snapshots/,1)
12
+ expect(found).to be_truthy
13
+ end
14
+ end
15
+
16
+ it "Exits with 0 snapshots cleanly" do
17
+ saved_pid = nil
18
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
19
+ saved_pid = cmd_pid
20
+ writer.puts("snap --list")
21
+ found = reader.expect(/No snapshots/,1)
22
+ expect(found).to be_truthy
23
+ writer.puts("exit")
24
+ sleep 0.1 # Give time to exit?
25
+ end
26
+
27
+ pid_list = `ps h -o pid,ppid -s #{saved_pid}`.split(/\n/)
28
+ expect(pid_list.count).to be == 0
29
+ end
30
+
31
+ it "creates one snapshot" do
32
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
33
+ writer.puts("snap")
34
+ writer.puts("snap --list")
35
+
36
+ all1, pid1 = reader.expect(/^(\d+) \(0\) <main> 1/,1)
37
+ all2, pid2 = reader.expect(/^ (\d+) \(1\) <main> 1 \*\*\*/,1)
38
+
39
+ expect(pid1.to_i).to be > 0
40
+ expect(pid2.to_i).to be > 0
41
+
42
+ pid_list = `ps h -o pid,ppid,sess -s #{cmd_pid}`.split(/\n/)
43
+ # 0: shell
44
+ # 1: root
45
+ # 2: base snapshot
46
+ # 3: current running branch
47
+ expect(pid_list.count).to be == 4
48
+ end
49
+ end
50
+
51
+ it "creates second snapshot" do
52
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
53
+ writer.puts("snap")
54
+ writer.puts("snap")
55
+ writer.puts("snap --list")
56
+
57
+ all1, pid1 = reader.expect(/^(\d+) \(0\) <main> 1/,1)
58
+ all2, pid2 = reader.expect(/^ (\d+) \(1\) <main> 1/,1)
59
+ all3, pid3 = reader.expect(/^ (\d+) \(2\) <main> 1 \*\*\*/,1)
60
+
61
+ expect(pid1.to_i).to be > 0
62
+ expect(pid2.to_i).to be > 0
63
+ expect(pid3.to_i).to be > 0
64
+
65
+ pid_list = `ps h -o pid,ppid -s #{cmd_pid}`.split(/\n/)
66
+ # 0: shell
67
+ # 1: root
68
+ # 2: base snapshot
69
+ # 2: second snapshot
70
+ # 3: current running branch
71
+ expect(pid_list.count).to be == 5
72
+ end
73
+ end
74
+
75
+ it "Can time-travel to before a var existed" do
76
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
77
+ writer.puts("snap")
78
+ writer.puts("x = 7")
79
+ reader.expect(/^=> 7/,1)
80
+ writer.puts("x")
81
+ reader.expect(/^=> 7/,1)
82
+ writer.puts("back")
83
+ reader.expect(/^At the top level\./,1)
84
+ writer.puts("x")
85
+ result = reader.expect(/^NameError: undefined local variable or method `x' for main:Object/,1)
86
+ expect(result).to be_truthy
87
+ end
6
88
  end
7
- describe "timetravel" do
8
- it "reinvokes the saved checkpoint and kills itself"
89
+
90
+ it "Can time-travel to a previous var value" do
91
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
92
+ writer.puts("x = 7")
93
+ expect(reader.expect(/^=> 7/,1)).to be_truthy
94
+ writer.puts("snap")
95
+ writer.puts("x")
96
+ expect(reader.expect(/^=> 7/,1)).to be_truthy
97
+ writer.puts("snap")
98
+ writer.puts("x = 7")
99
+ reader.expect(/^=> 13/,1)
100
+ writer.puts("back")
101
+ reader.expect(/^At the top level\./,1)
102
+ writer.puts("x")
103
+ result = reader.expect(/^=> 7/,1)
104
+ expect(result).to be_truthy
105
+ end
9
106
  end
107
+
108
+ it "Can auto-checkpoint" do
109
+ PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
110
+ writer.puts("snap -a")
111
+ writer.puts("x = 7")
112
+ expect(reader.expect(/^=> 7/,1)).to be_truthy
113
+ writer.puts("x")
114
+ expect(reader.expect(/^=> 7/,1)).to be_truthy
115
+ writer.puts("x = 13")
116
+ expect(reader.expect(/^=> 13/,1)).to be_truthy
117
+ writer.puts("back")
118
+ expect(reader.expect(/^At the top level\./,1)).to be_truthy
119
+ writer.puts("x")
120
+ result = reader.expect(/^=> 7/,1)
121
+ expect(result).to be_truthy
122
+ end
123
+ end
124
+
10
125
  end
11
126
 
@@ -1,6 +1,25 @@
1
1
  require 'rspec'
2
- require 'rspec/autorun'
2
+ require './lib/pry-timetravel'
3
3
 
4
- require 'pry/test/helper'
4
+ #require 'pry/test/helper'
5
5
 
6
- require './lib/pry-timetravel'
6
+ # in case the tests call reset_defaults, ensure we reset them to
7
+ # amended (test friendly) values
8
+ # class << Pry
9
+ # alias_method :orig_reset_defaults, :reset_defaults
10
+ # def reset_defaults
11
+ # orig_reset_defaults
12
+
13
+ # Pry.config.color = false
14
+ # Pry.config.pager = false
15
+ # Pry.config.should_load_rc = false
16
+ # Pry.config.should_load_local_rc= false
17
+ # Pry.config.should_load_plugins = false
18
+ # Pry.config.history.should_load = false
19
+ # Pry.config.history.should_save = false
20
+ # Pry.config.correct_indent = false
21
+ # Pry.config.hooks = Pry::Hooks.new
22
+ # Pry.config.collision_warning = false
23
+ # end
24
+ # end
25
+ # Pry.reset_defaults
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pry-timetravel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brock Wilcox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-18 00:00:00.000000000 Z
11
+ date: 2017-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
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'
41
69
  description: Allows you to timetravel!
42
70
  email:
43
71
  - awwaiid@thelackthereof.org
@@ -46,7 +74,9 @@ extensions: []
46
74
  extra_rdoc_files: []
47
75
  files:
48
76
  - ".gitignore"
77
+ - ".rspec"
49
78
  - ".travis.yml"
79
+ - CHANGES.md
50
80
  - Gemfile
51
81
  - LICENSE.MIT
52
82
  - README.md
@@ -76,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
106
  version: '0'
77
107
  requirements: []
78
108
  rubyforge_project:
79
- rubygems_version: 2.2.2
109
+ rubygems_version: 2.5.1
80
110
  signing_key:
81
111
  specification_version: 4
82
112
  summary: Timetravel