pry-remote-enhanced 1.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.
- data/LICENSE +21 -0
- data/README.md +65 -0
- data/bin/pry-remote +4 -0
- data/lib/pry-remote.rb +380 -0
- metadata +81 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
pry-remote - Copyright (c) 2012 - Mon ouïe
|
2
|
+
|
3
|
+
This software is provided 'as-is', without any express or
|
4
|
+
implied warranty. In no event will the authors be held
|
5
|
+
liable for any damages arising from the use of this software.
|
6
|
+
|
7
|
+
Permission is granted to anyone to use this software for any purpose,
|
8
|
+
including commercial applications, and to alter it and redistribute
|
9
|
+
it freely, subject to the following restrictions:
|
10
|
+
|
11
|
+
1. The origin of this software must not be misrepresented;
|
12
|
+
you must not claim that you wrote the original software.
|
13
|
+
If you use this software in a product, an acknowledgment
|
14
|
+
in the product documentation would be appreciated but
|
15
|
+
is not required.
|
16
|
+
|
17
|
+
2. Altered source versions must be plainly marked as such,
|
18
|
+
and must not be misrepresented as being the original software.
|
19
|
+
|
20
|
+
3. This notice may not be removed or altered from any
|
21
|
+
source distribution.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# What is it?
|
2
|
+
|
3
|
+
A way to start Pry remotely and to connect to it using DRb. This allows to
|
4
|
+
access the state of the running program from anywhere.
|
5
|
+
|
6
|
+
This repository is a fork of pry-remote with aims to:
|
7
|
+
|
8
|
+
* Resolve existing issues in pry-remote
|
9
|
+
* Refactor architecture to use more idiomatic Ruby
|
10
|
+
* Add a testing suite
|
11
|
+
* Improve support for Sidekiq and Foreman
|
12
|
+
|
13
|
+
# Installation
|
14
|
+
|
15
|
+
gem install pry-remote-enhanced
|
16
|
+
|
17
|
+
# Usage
|
18
|
+
|
19
|
+
Here's a program starting pry-remote:
|
20
|
+
|
21
|
+
require 'pry-remote'
|
22
|
+
|
23
|
+
class Foo
|
24
|
+
def initialize(x, y)
|
25
|
+
binding.remote_pry
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Foo.new 10, 20
|
30
|
+
|
31
|
+
Running it will prompt you with a message telling you Pry is waiting for a
|
32
|
+
program to connect itself to it:
|
33
|
+
|
34
|
+
[pry-remote] Waiting for client on drb://localhost:9876
|
35
|
+
|
36
|
+
You can then connect yourself using ``pry-remote``:
|
37
|
+
|
38
|
+
$ pry-remote
|
39
|
+
From: example.rb @ line 7 in Foo#initialize:
|
40
|
+
2:
|
41
|
+
3: require 'pry-remote'
|
42
|
+
4:
|
43
|
+
5: class Foo
|
44
|
+
6: def initialize(x, y)
|
45
|
+
=> 7: binding.remote_pry
|
46
|
+
8: end
|
47
|
+
9: end
|
48
|
+
10:
|
49
|
+
11: Foo.new 10, 20
|
50
|
+
pry(#<Foo:0x00000000d9b5e8>):1> self
|
51
|
+
=> #<Foo:0x1efb3b0>
|
52
|
+
pry(#<Foo:0x00000001efb3b0>):2> ls -l
|
53
|
+
Local variables: [
|
54
|
+
[0] :_,
|
55
|
+
[1] :_dir_,
|
56
|
+
[2] :_file_,
|
57
|
+
[3] :_ex_,
|
58
|
+
[4] :_pry_,
|
59
|
+
[5] :_out_,
|
60
|
+
[6] :_in_,
|
61
|
+
[7] :x,
|
62
|
+
[8] :y
|
63
|
+
]
|
64
|
+
pry(#<Foo:0x00000001efb3b0>):3> ^D
|
65
|
+
|
data/bin/pry-remote
ADDED
data/lib/pry-remote.rb
ADDED
@@ -0,0 +1,380 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'slop'
|
3
|
+
require 'drb'
|
4
|
+
require 'readline'
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
module PryRemote
|
8
|
+
DefaultHost = "127.0.0.1"
|
9
|
+
DefaultPort = 9876
|
10
|
+
|
11
|
+
# A class to represent an input object created from DRb. This is used because
|
12
|
+
# Pry checks for arity to know if a prompt should be passed to the object.
|
13
|
+
#
|
14
|
+
# @attr [#readline] input Object to proxy
|
15
|
+
InputProxy = Struct.new :input do
|
16
|
+
# Reads a line from the input
|
17
|
+
def readline(prompt)
|
18
|
+
case readline_arity
|
19
|
+
when 1 then input.readline(prompt)
|
20
|
+
else input.readline
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def completion_proc=(val)
|
25
|
+
input.completion_proc = val
|
26
|
+
end
|
27
|
+
|
28
|
+
def readline_arity
|
29
|
+
input.method_missing(:method, :readline).arity
|
30
|
+
rescue NameError
|
31
|
+
0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Class used to wrap inputs so that they can be sent through DRb.
|
36
|
+
#
|
37
|
+
# This is to ensure the input is used locally and not reconstructed on the
|
38
|
+
# server by DRb.
|
39
|
+
class IOUndumpedProxy
|
40
|
+
include DRb::DRbUndumped
|
41
|
+
|
42
|
+
def initialize(obj)
|
43
|
+
@obj = obj
|
44
|
+
end
|
45
|
+
|
46
|
+
def completion_proc=(val)
|
47
|
+
if @obj.respond_to? :completion_proc=
|
48
|
+
@obj.completion_proc = proc { |*args, &block| val.call(*args, &block) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def completion_proc
|
53
|
+
@obj.completion_proc if @obj.respond_to? :completion_proc
|
54
|
+
end
|
55
|
+
|
56
|
+
def readline(prompt)
|
57
|
+
if Readline == @obj
|
58
|
+
@obj.readline(prompt, true)
|
59
|
+
elsif @obj.method(:readline).arity == 1
|
60
|
+
@obj.readline(prompt)
|
61
|
+
else
|
62
|
+
$stdout.print prompt
|
63
|
+
@obj.readline
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def puts(*lines)
|
68
|
+
@obj.puts(*lines)
|
69
|
+
end
|
70
|
+
|
71
|
+
def print(*objs)
|
72
|
+
@obj.print(*objs)
|
73
|
+
end
|
74
|
+
|
75
|
+
def printf(*args)
|
76
|
+
@obj.printf(*args)
|
77
|
+
end
|
78
|
+
|
79
|
+
def write(data)
|
80
|
+
@obj.write data
|
81
|
+
end
|
82
|
+
|
83
|
+
def <<(data)
|
84
|
+
@obj << data
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# Some versions of Pry expect $stdout or its output objects to respond to
|
89
|
+
# this message.
|
90
|
+
def tty?
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Ensure that system (shell command) output is redirected for remote session.
|
96
|
+
System = proc do |output, cmd, _|
|
97
|
+
status = nil
|
98
|
+
Open3.popen3 cmd do |stdin, stdout, stderr, wait_thr|
|
99
|
+
stdin.close # Send EOF to the process
|
100
|
+
|
101
|
+
until stdout.eof? and stderr.eof?
|
102
|
+
if res = IO.select([stdout, stderr])
|
103
|
+
res[0].each do |io|
|
104
|
+
next if io.eof?
|
105
|
+
output.write io.read_nonblock(1024)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
status = wait_thr.value
|
111
|
+
end
|
112
|
+
|
113
|
+
unless status.success?
|
114
|
+
output.puts "Error while executing command: #{cmd}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
ClientEditor = proc do |initial_content, line|
|
119
|
+
# Hack to use Pry::Editor
|
120
|
+
Pry::Editor.new(Pry.new).edit_tempfile_with_content(initial_content, line)
|
121
|
+
end
|
122
|
+
|
123
|
+
# A client is used to retrieve information from the client program.
|
124
|
+
Client = Struct.new(:input, :output, :thread, :stdout, :stderr,
|
125
|
+
:editor) do
|
126
|
+
# Waits until both an input and output are set
|
127
|
+
def wait
|
128
|
+
sleep 0.01 until input and output and thread
|
129
|
+
end
|
130
|
+
|
131
|
+
# Tells the client the session is terminated
|
132
|
+
def kill
|
133
|
+
thread.run
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [InputProxy] Proxy for the input
|
137
|
+
def input_proxy
|
138
|
+
InputProxy.new input
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class Server
|
143
|
+
def self.run(object, host = DefaultHost, port = DefaultPort, options = {})
|
144
|
+
new(object, host, port, options).run
|
145
|
+
end
|
146
|
+
|
147
|
+
def initialize(object, host = DefaultHost, port = DefaultPort, options = {})
|
148
|
+
@host = host
|
149
|
+
@port = port
|
150
|
+
|
151
|
+
@object = object
|
152
|
+
@options = options
|
153
|
+
|
154
|
+
@client = PryRemote::Client.new
|
155
|
+
DRb.start_service uri, @client
|
156
|
+
end
|
157
|
+
|
158
|
+
# Code that has to be called for Pry-remote to work properly
|
159
|
+
def setup
|
160
|
+
@hooks = Pry::Hooks.new
|
161
|
+
|
162
|
+
@hooks.add_hook :before_eval, :pry_remote_capture do
|
163
|
+
capture_output
|
164
|
+
end
|
165
|
+
|
166
|
+
@hooks.add_hook :after_eval, :pry_remote_uncapture do
|
167
|
+
uncapture_output
|
168
|
+
end
|
169
|
+
|
170
|
+
# Before Pry starts, save the pager config.
|
171
|
+
# We want to disable this because the pager won't do anything useful in
|
172
|
+
# this case (it will run on the server).
|
173
|
+
Pry.config.pager, @old_pager = false, Pry.config.pager
|
174
|
+
|
175
|
+
# As above, but for system config
|
176
|
+
Pry.config.system, @old_system = PryRemote::System, Pry.config.system
|
177
|
+
|
178
|
+
Pry.config.editor, @old_editor = editor_proc, Pry.config.editor
|
179
|
+
end
|
180
|
+
|
181
|
+
# Code that has to be called after setup to return to the initial state
|
182
|
+
def teardown
|
183
|
+
# Reset config
|
184
|
+
Pry.config.editor = @old_editor
|
185
|
+
Pry.config.pager = @old_pager
|
186
|
+
Pry.config.system = @old_system
|
187
|
+
|
188
|
+
puts "[pry-remote] Remote session terminated"
|
189
|
+
|
190
|
+
begin
|
191
|
+
@client.kill
|
192
|
+
rescue DRb::DRbConnError
|
193
|
+
puts "[pry-remote] Continuing to stop service"
|
194
|
+
ensure
|
195
|
+
puts "[pry-remote] Ensure stop service"
|
196
|
+
DRb.stop_service
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Captures $stdout and $stderr if so requested by the client.
|
201
|
+
def capture_output
|
202
|
+
@old_stdout, $stdout = if @client.stdout
|
203
|
+
[$stdout, @client.stdout]
|
204
|
+
else
|
205
|
+
[$stdout, $stdout]
|
206
|
+
end
|
207
|
+
|
208
|
+
@old_stderr, $stderr = if @client.stderr
|
209
|
+
[$stderr, @client.stderr]
|
210
|
+
else
|
211
|
+
[$stderr, $stderr]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Resets $stdout and $stderr to their previous values.
|
216
|
+
def uncapture_output
|
217
|
+
$stdout = @old_stdout
|
218
|
+
$stderr = @old_stderr
|
219
|
+
end
|
220
|
+
|
221
|
+
def editor_proc
|
222
|
+
proc do |file, line|
|
223
|
+
File.write(file, @client.editor.call(File.read(file), line))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Actually runs pry-remote
|
228
|
+
def run
|
229
|
+
puts "[pry-remote] Waiting for client on #{uri}"
|
230
|
+
@client.wait
|
231
|
+
|
232
|
+
puts "[pry-remote] Client received, starting remote session"
|
233
|
+
setup
|
234
|
+
|
235
|
+
Pry.start(@object, @options.merge(:input => client.input_proxy,
|
236
|
+
:output => client.output,
|
237
|
+
:hooks => @hooks))
|
238
|
+
ensure
|
239
|
+
teardown
|
240
|
+
end
|
241
|
+
|
242
|
+
# @return Object to enter into
|
243
|
+
attr_reader :object
|
244
|
+
|
245
|
+
# @return [PryServer::Client] Client connecting to the pry-remote server
|
246
|
+
attr_reader :client
|
247
|
+
|
248
|
+
# @return [String] Host of the server
|
249
|
+
attr_reader :host
|
250
|
+
|
251
|
+
# @return [Integer] Port of the server
|
252
|
+
attr_reader :port
|
253
|
+
|
254
|
+
# @return [String] URI for DRb
|
255
|
+
def uri
|
256
|
+
"druby://#{host}:#{port}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Parses arguments and allows to start the client.
|
261
|
+
class CLI
|
262
|
+
def initialize(args = ARGV)
|
263
|
+
params = Slop.parse args, :help => true do
|
264
|
+
banner "#$PROGRAM_NAME [OPTIONS]"
|
265
|
+
|
266
|
+
on :s, :server=, "Host of the server (#{DefaultHost})", :argument => :optional,
|
267
|
+
:default => DefaultHost
|
268
|
+
on :p, :port=, "Port of the server (#{DefaultPort})", :argument => :optional,
|
269
|
+
:as => Integer, :default => DefaultPort
|
270
|
+
on :w, :wait, "Wait for the pry server to come up",
|
271
|
+
:default => false
|
272
|
+
on :r, :persist, "Persist the client to wait for the pry server to come up each time",
|
273
|
+
:default => false
|
274
|
+
on :c, :capture, "Captures $stdout and $stderr from the server (true)",
|
275
|
+
:default => true
|
276
|
+
on :f, "Disables loading of .pryrc and its plugins, requires, and command history "
|
277
|
+
end
|
278
|
+
|
279
|
+
exit if params.help?
|
280
|
+
|
281
|
+
@host = params[:server]
|
282
|
+
@port = params[:port]
|
283
|
+
|
284
|
+
@wait = params[:wait]
|
285
|
+
@persist = params[:persist]
|
286
|
+
@capture = params[:capture]
|
287
|
+
|
288
|
+
Pry.initial_session_setup unless params[:f]
|
289
|
+
end
|
290
|
+
|
291
|
+
# @return [String] Host of the server
|
292
|
+
attr_reader :host
|
293
|
+
|
294
|
+
# @return [Integer] Port of the server
|
295
|
+
attr_reader :port
|
296
|
+
|
297
|
+
# @return [String] URI for DRb
|
298
|
+
def uri
|
299
|
+
"druby://#{host}:#{port}"
|
300
|
+
end
|
301
|
+
|
302
|
+
attr_reader :wait
|
303
|
+
attr_reader :persist
|
304
|
+
attr_reader :capture
|
305
|
+
alias wait? wait
|
306
|
+
alias persist? persist
|
307
|
+
alias capture? capture
|
308
|
+
|
309
|
+
def run
|
310
|
+
while true
|
311
|
+
connect
|
312
|
+
break unless persist?
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Connects to the server
|
317
|
+
#
|
318
|
+
# @param [IO] input Object holding input for pry-remote
|
319
|
+
# @param [IO] output Object pry-debug will send its output to
|
320
|
+
def connect(input = Pry.config.input, output = Pry.config.output)
|
321
|
+
local_ip = UDPSocket.open {|s| s.connect(@host, 1); s.addr.last}
|
322
|
+
DRb.start_service "druby://#{local_ip}:0"
|
323
|
+
client = DRbObject.new(nil, uri)
|
324
|
+
|
325
|
+
cleanup(client)
|
326
|
+
|
327
|
+
input = IOUndumpedProxy.new(input)
|
328
|
+
output = IOUndumpedProxy.new(output)
|
329
|
+
|
330
|
+
begin
|
331
|
+
client.input = input
|
332
|
+
client.output = output
|
333
|
+
rescue DRb::DRbConnError => ex
|
334
|
+
if wait? || persist?
|
335
|
+
sleep 1
|
336
|
+
retry
|
337
|
+
else
|
338
|
+
raise ex
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
if capture?
|
343
|
+
client.stdout = $stdout
|
344
|
+
client.stderr = $stderr
|
345
|
+
end
|
346
|
+
|
347
|
+
client.editor = ClientEditor
|
348
|
+
|
349
|
+
client.thread = Thread.current
|
350
|
+
|
351
|
+
sleep
|
352
|
+
DRb.stop_service
|
353
|
+
end
|
354
|
+
|
355
|
+
# Clean up the client
|
356
|
+
def cleanup(client)
|
357
|
+
begin
|
358
|
+
# The method we are calling here doesn't matter.
|
359
|
+
# This is a hack to close the connection of DRb.
|
360
|
+
client.cleanup
|
361
|
+
rescue DRb::DRbConnError, NoMethodError
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
class Object
|
368
|
+
# Starts a remote Pry session
|
369
|
+
#
|
370
|
+
# @param [String] host Host of the server
|
371
|
+
# @param [Integer] port Port of the server
|
372
|
+
# @param [Hash] options Options to be passed to Pry.start
|
373
|
+
def remote_pry(host = PryRemote::DefaultHost, port = PryRemote::DefaultPort, options = {})
|
374
|
+
PryRemote::Server.new(self, host, port, options).run
|
375
|
+
end
|
376
|
+
|
377
|
+
# a handy alias as many people may think the method is named after the gem
|
378
|
+
# (pry-remote)
|
379
|
+
alias pry_remote remote_pry
|
380
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pry-remote-enhanced
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joseph Jaber
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-09-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: slop
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pry
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0.9'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.9'
|
46
|
+
description: A modern fork of pry-remote
|
47
|
+
email: mail@josephjaber.com
|
48
|
+
executables:
|
49
|
+
- pry-remote
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- lib/pry-remote.rb
|
54
|
+
- README.md
|
55
|
+
- LICENSE
|
56
|
+
- bin/pry-remote
|
57
|
+
homepage: http://github.com/josephjaber/pry-remote
|
58
|
+
licenses: []
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.23.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: A modern fork of pry-remote
|
81
|
+
test_files: []
|