pry-timetravel 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: