pry-timetravel 0.0.1 → 0.0.2

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: de1866b7ed3e671da6c46593c64cf8a6d67f79b1
4
- data.tar.gz: 3250ab254a725690e9918c419eb99e9bea65a213
3
+ metadata.gz: 5d28f753687e1f5f96c87e7ce51949dc68d8fab6
4
+ data.tar.gz: be676c5cc7e8ff236314b31e6161aef76ce017a4
5
5
  SHA512:
6
- metadata.gz: 72cd2382bb52e80f742995b9055c0e4680a0468413b431c26f097e3ffb3c022d45d43930f54c6eea2712ba8b8bca1e4c70ed2933f30eb8ac2c1b4f32b5c1484b
7
- data.tar.gz: ddeda18934b6405411a1971a1cfd4a0b5dc68d49cb234bc2f0ca5044ac623668312e06c909c4f63737a3b3801c26b34dba3c45f2ccf90fca5d025ec65516d611
6
+ metadata.gz: c42224c2d9c89770cf5f22c861b37fa254a71713d11c46696fbc6d912027f21454a0ea98688f13f690a69e7fc7a5e12b8a1c63e2b2d61603c7c192bbebfb0c01
7
+ data.tar.gz: 27bfcc69e86ff9838820f350c5fb617e24d6d1c456e702b670c8c25d7156b9afdb5be0c728b9416f8645696759c4482930e3f0f9e309344635fc9638b2636fed
data/LICENSE.MIT CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Conrad Irwin <conrad.irwin@gmail.com>
1
+ Copyright (c) 2014 Brock Wilcox <awwaiid@thelackthereof.org>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,21 +1,33 @@
1
1
  # pry-timetravel
2
2
 
3
- DOES NOT WORK. DO NOT USE.
4
-
5
3
  Time travel! For your REPL!
6
4
 
7
5
  > x = 5
8
6
  5
9
- > checkpoint
7
+ > snap
10
8
  > x = 10
11
9
  10
12
- > timetravel
10
+ > back
13
11
  > x
14
12
  5
15
13
 
16
14
  This is an attempt to package the proof of concept. API WILL CHANGE!
17
15
 
16
+ ## How it works
17
+
18
+ The 'snap' command causes a fork, and the child is sent a SIGSTOP to freeze the
19
+ process. The parent continues on. Later when you do 'back' the saved child is
20
+ sent SIGCONT and the current process is sent a SIGSTOP. Ultimately you can have
21
+ a whole pool of frozen snapshots and can resume any of them.
22
+
23
+ In theory copy-on-write semantics makes this a tollerable thing to do :)
24
+
25
+ There are a few more details, but that is the important bit.
26
+
27
+ WARNING: Time travel may cause zombies.
28
+
18
29
  ## Meta
19
30
 
20
- Released under the MIT license, see LICENSE.MIT for details. Contributions and bug-reports
21
- are welcome.
31
+ Released under the MIT license, see LICENSE.MIT for details. License is
32
+ negotiable. Contributions and bug-reports are welcome!
33
+
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'
@@ -7,81 +7,110 @@ class PryTimetravel
7
7
  class << self
8
8
 
9
9
  def dlog(msg)
10
- # File.open("meta.log", 'a') do |file|
11
- # file.puts(msg)
12
- # end
10
+ if ENV["TIMETRAVEL_DEBUG"]
11
+ File.open("meta.log", 'a') do |file|
12
+ file.puts("#{Time.now} [#{$$}] #{msg}")
13
+ end
14
+ end
15
+ end
16
+
17
+ at_exit do
18
+ PryTimetravel.dlog "at_exit"
19
+ if $root_parent && $$ != $root_parent
20
+ PryTimetravel.dlog "Sending SIGUSR1 up to #{$root_parent}"
21
+ Process.kill 'SIGUSR1', $root_parent
22
+ end
23
+ end
24
+
25
+ def enter_suspended_animation
26
+ old_sigcont_handler = Signal.trap('CONT') do
27
+ dlog("Got a SIGCONT")
28
+ end
29
+
30
+ old_sigexit_handler = Signal.trap('EXIT') do
31
+ dlog("got EXIT")
32
+ Kernel.exit!
33
+ end
34
+
35
+ dlog("Stopping myself")
36
+ Process.kill 'SIGSTOP', $$
37
+ dlog("Back from SIGSTOP!")
38
+
39
+ dlog("Returning to old SIGCONT")
40
+ Signal.trap('CONT', old_sigcont_handler)
41
+ dlog("Returning to old SIGEXIT")
42
+ Signal.trap('EXIT', old_sigexit_handler)
13
43
  end
14
44
 
15
- def checkpoint
45
+ def snapshot
46
+
47
+ # We need a root-parent to keep the shell happy
48
+ if ! $root_parent
49
+ $root_parent = $$
50
+ child_pid = fork
51
+ if child_pid
52
+ Signal.trap('INT') do
53
+ dlog("root-parent got INT")
54
+ end
55
+ Signal.trap('USR1') do
56
+ dlog("root-parent got USR1")
57
+ Kernel.exit!
58
+ end
59
+ dlog "Root parent waiting on #{child_pid}"
60
+ Process.waitpid child_pid
61
+ dlog "Root parent exiting!"
62
+ Kernel.exit!
63
+ end
64
+ end
65
+
66
+ $timetravel_root ||= $$
67
+
16
68
  parent_pid = $$
17
69
  child_pid = fork
18
70
  if child_pid
19
- dlog("PARENT #{parent_pid}: I have a child pid #{child_pid}")
20
-
21
- # Method 1: Child suspends themselves, parent adds them to list
22
- # @previous_pid ||= []
23
- # @previous_pid.push child_pid
24
-
25
- # Method 2: Parent does a global-blocking waitall
26
- # when the child exits, we can continue
27
- # Problem: What if the child exits in a bad way? Check out '$?'
28
- Process.waitpid(child_pid)
29
- dlog("ME #{$$}: Previous exit: #{$?.inspect}")
30
- if $?.exitstatus != 42
31
- # Since this wasn't a timetravel return, we must unravel this world
32
- Kernel.exit! $?.exitstatus
33
- end
71
+ dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}")
72
+
73
+ # Method 3: Child suspends themselves, parent adds them to list
74
+ @previous_pid ||= []
75
+ @previous_pid.push child_pid
76
+
77
+ # Perform operation
78
+ yield
34
79
 
35
- # The parent universe freezes
36
- # dlog("PARENT #{$$}: I am suspending. My child is #{parent_pid}")
37
- # dlog("PARENT #{$$}: suspending")
38
- # Process.setpgrp
39
- # Process.setsid
40
- # dlog("PARENT #{$$}: resumed!")
41
80
  else
42
81
  child_pid = $$
43
- dlog("CHILD: #{child_pid}: I have a parent pid #{parent_pid}")
82
+ dlog("I am child #{child_pid}: I have a parent pid #{parent_pid}")
83
+ enter_suspended_animation
84
+ end
85
+ end
86
+
87
+ def snapshot_list
88
+ @previous_pid && @previous_pid.join(" ")
89
+ end
44
90
 
45
- # Method 1: Child suspends themselves, parent adds them to list
46
- # The child is eventually resumed
47
- # Process.kill 'SIGSTOP', child_pid
48
- # dlog("CHILD #{child_pid}: resumed!")
91
+ def restore_snapshot(target_pid = nil, count = nil)
92
+ dlog("Thinking about time travel...");
49
93
 
50
- # Method 2: Parent does a global-blocking waitall
51
- # Child doesn't need to keep track of parent at all?
52
- # Child will just exit! when it is done
53
- @previous_pid ||= []
54
- @previous_pid.push parent_pid
94
+ if target_pid.nil? && @previous_pid && ! @previous_pid.empty?
95
+ count = 1 if count == 0
96
+ target_pid = @previous_pid[-count]
97
+ else
98
+ target_pid = target_pid
99
+ end
55
100
 
101
+ if target_pid
102
+ dlog("ME #{$$}: I found a target pid #{target_pid}! TIME TRAVEL TIME")
103
+ Process.kill 'SIGCONT', target_pid
104
+ enter_suspended_animation
105
+ else
106
+ dlog("I was unable to time travel. Maybe it is a myth.");
56
107
  end
57
108
  end
58
109
 
59
- def go_back
60
- dlog("ME #{$$}: Thinking about time travel...");
61
- dlog("ME #{$$}: previous_pid = #{ @previous_pid }");
62
- if @previous_pid && ! @previous_pid.empty?
63
- previous_pid = @previous_pid.pop
64
- dlog("ME #{$$}: I found a previous pid #{previous_pid}! TIME TRAVEL TIME")
65
-
66
- # Method 1: Awaken the child and let them take over
67
- # Main parent can't exit or shell will get upset, so wait for all children
68
- # Once all children are done, kill ourself
69
- # Process.kill 'SIGCONT', previous_pid
70
- # dlog("ME #{$$}: I resumed pid #{previous_pid}... now time to wait")
71
- # # Process.waitpid(previous_pid)
72
- # Process.waitall
73
- # dlog("ME #{$$}: If you meet your previous self, kill yourself.")
74
- # # Process.kill 'SIGKILL', $$
75
- # Kernel.exit!
76
-
77
- # Method 2: Kill ourself and let the parent take over
78
- # The parent was just doing a waitpid, so it is ready
79
- # Process.kill 'SIGKILL', $$
80
- Process.exit! 42
81
-
82
- end
83
- dlog("ME #{$$}: I was unable to time travel. Maybe it is a myth.");
110
+ def restore_root_snapshot
111
+ restore_snapshot($timetravel_root) if $timetravel_root
84
112
  end
113
+
85
114
  end
86
115
  end
87
116
 
@@ -1,49 +1,57 @@
1
1
 
2
- Pry::Commands.create_command "checkpoint", "Set a marker in the timeline" do
3
- match 'checkpoint'
2
+ Pry::Commands.create_command "snap", "Create a snapshot that you can later return to" do
3
+ match 'snap'
4
4
  group 'Timetravel'
5
- # description 'Snapshot the world so we can timetravel back here later'
6
5
  banner <<-'BANNER'
7
- Usage: checkpoint
8
- checkpoint --list
9
- checkpoint --delete [INDEX]
6
+ Usage: snap [cmd]
7
+ snap --list
10
8
 
11
- This will add a checkpoint which you can return to later.
9
+ This will add a snapshot which you can return to later.
10
+
11
+ If you provide [cmd] then that command will also be run -- nice for "snap next" in conjunction with pry-byebug.
12
12
  BANNER
13
13
 
14
- # def options(opt)
15
- # opt.on :d, :delete,
16
- # "Delete the checkpoint with the given index. If no index is given delete them all",
14
+ def options(opt)
15
+ # opt.on :d, :delete=,
16
+ # "Delete the snapshot with the given index. If no index is given delete them all",
17
17
  # :optional_argument => true, :as => Integer
18
- # opt.on :l, :list,
19
- # "Show all checkpoints"
20
- # end
18
+ opt.on :l, :list,
19
+ "Show a list of existing snapshots"
20
+ end
21
21
  def process
22
- PryTimetravel.checkpoint
22
+ if opts.l?
23
+ output.puts PryTimetravel.snapshot_list
24
+ else
25
+ PryTimetravel.snapshot do
26
+ run(args.join(" ")) unless args.empty?
27
+ end
28
+ end
23
29
  end
24
30
  end
25
31
 
26
- Pry::Commands.create_command "timetravel", "Set a marker in the timeline" do
27
- match 'timetravel'
32
+ Pry::Commands.create_command "back", "Go back to the most recent snapshot" do
33
+ match 'back'
28
34
  group 'Timetravel'
29
- # description 'Snapshot the world so we can timetravel back here later'
30
35
  banner <<-'BANNER'
31
- Usage: checkpoint
32
- checkpoint --list
33
- checkpoint --delete [INDEX]
36
+ Usage: back [count]
37
+ back --pid pid
38
+ back --home
34
39
 
35
- This will add a checkpoint which you can return to later.
40
+ Go back to a previous snapshot.
36
41
  BANNER
37
42
 
38
- # def options(opt)
39
- # opt.on :d, :delete,
40
- # "Delete the checkpoint with the given index. If no index is given delete them all",
41
- # :optional_argument => true, :as => Integer
42
- # opt.on :l, :list,
43
- # "Show all checkpoints"
44
- # end
43
+ def options(opt)
44
+ opt.on :p, :pid=, "Jump (back) to a specific snapshot identified by [pid]",
45
+ :as => Integer
46
+ opt.on :home, "Jump to the end of the original execution sequence"
47
+ end
45
48
  def process
46
- PryTimetravel.go_back
49
+ if opts.h?
50
+ PryTimetravel.restore_root_snapshot
51
+ else
52
+ count = args.first.to_i
53
+ PryTimetravel.restore_snapshot(opts[:p], count)
54
+ end
47
55
  end
48
56
  end
49
57
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'pry-timetravel'
3
- s.version = '0.0.1'
3
+ s.version = '0.0.2'
4
4
  s.summary = 'Timetravel'
5
5
  s.description = 'Allows you to timetravel!'
6
6
  s.homepage = 'https://github.com/awwaiid/pry-timetravel'
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.1
4
+ version: 0.0.2
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-08-07 00:00:00.000000000 Z
11
+ date: 2014-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -81,4 +81,3 @@ signing_key:
81
81
  specification_version: 4
82
82
  summary: Timetravel
83
83
  test_files: []
84
- has_rdoc: