pry-remote-reloaded 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: '00734439f9315ab83e95a0fc900a0b702f0606cf'
4
+ data.tar.gz: 239a33b24177c77ac0c5583287745b1fb3ebf7aa
5
+ SHA512:
6
+ metadata.gz: 0e35049e72c41389a5f0973bb52e5daaa4778a0415f3d6b0e144df51465d127b0d91168cf6b65911014b77fbac9bd30b028d918500b77968de97028c61a3a437
7
+ data.tar.gz: 2355f7e0d1a1e7af035207ef12d8e01a61d01026772183822169f55ca8ede349bc2a5b2142fca46148600eb3ca1d07c8e4761b416d2dd4b4c11a85e2ccf90649
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ ## 1.0.0 (2017-12-13)
2
+
3
+ * Relaxed the Pry dependencies version condition (>=0.9)
4
+ * [Reuse default Pry hooks](https://github.com/Mon-Ouie/pry-remote/pull/64)
5
+ * Added improved interrupt handling (Ctrl+C results in a clean Pry exit)
6
+ * Improved general error handling
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,61 @@
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
+ **Heads up!** This is a fork of the excellent
7
+ [pry-remote](https://github.com/Mon-Ouie/pry-remote) gem with some
8
+ enhancements.
9
+
10
+ # Installation
11
+
12
+ gem install pry-remote-reloaded
13
+
14
+ # Usage
15
+
16
+ Here's a program starting pry-remote:
17
+
18
+ require 'pry-remote'
19
+
20
+ class Foo
21
+ def initialize(x, y)
22
+ binding.remote_pry
23
+ end
24
+ end
25
+
26
+ Foo.new 10, 20
27
+
28
+ Running it will prompt you with a message telling you Pry is waiting for a
29
+ program to connect itself to it:
30
+
31
+ [pry-remote] Waiting for client on drb://localhost:9876
32
+
33
+ You can then connect yourself using ``pry-remote``:
34
+
35
+ $ pry-remote
36
+ From: example.rb @ line 7 in Foo#initialize:
37
+ 2:
38
+ 3: require 'pry-remote'
39
+ 4:
40
+ 5: class Foo
41
+ 6: def initialize(x, y)
42
+ => 7: binding.remote_pry
43
+ 8: end
44
+ 9: end
45
+ 10:
46
+ 11: Foo.new 10, 20
47
+ pry(#<Foo:0x00000000d9b5e8>):1> self
48
+ => #<Foo:0x1efb3b0>
49
+ pry(#<Foo:0x00000001efb3b0>):2> ls -l
50
+ Local variables: [
51
+ [0] :_,
52
+ [1] :_dir_,
53
+ [2] :_file_,
54
+ [3] :_ex_,
55
+ [4] :_pry_,
56
+ [5] :_out_,
57
+ [6] :_in_,
58
+ [7] :x,
59
+ [8] :y
60
+ ]
61
+ pry(#<Foo:0x00000001efb3b0>):3> ^D
data/bin/pry-remote ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pry-remote-reloaded'
4
+ PryRemoteReloaded::CLI.new.run
@@ -0,0 +1,388 @@
1
+ require 'pry'
2
+ require 'slop'
3
+ require 'drb'
4
+ require 'readline'
5
+ require 'open3'
6
+
7
+ module PryRemoteReloaded
8
+ DefaultHost = ENV['PRY_REMOTE_DEFAULT_HOST'] || "127.0.0.1"
9
+ DefaultPort = ENV['PRY_REMOTE_DEFAULT_PORT'] || 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 = PryRemoteReloaded::Client.new
155
+ DRb.start_service uri, @client
156
+ rescue Errno::EADDRINUSE => e
157
+ puts "[pry-remote] Port already in use: #{e.message}"
158
+ end
159
+
160
+ # Code that has to be called for Pry-remote to work properly
161
+ def setup
162
+ @hooks = Pry::DEFAULT_HOOKS.dup
163
+
164
+ @hooks.add_hook :before_eval, :pry_remote_capture do
165
+ capture_output
166
+ end
167
+
168
+ @hooks.add_hook :after_eval, :pry_remote_uncapture do
169
+ uncapture_output
170
+ end
171
+
172
+ # Before Pry starts, save the pager config.
173
+ # We want to disable this because the pager won't do anything useful in
174
+ # this case (it will run on the server).
175
+ Pry.config.pager, @old_pager = false, Pry.config.pager
176
+
177
+ # As above, but for system config
178
+ Pry.config.system, @old_system = PryRemoteReloaded::System, Pry.config.system
179
+
180
+ Pry.config.editor, @old_editor = editor_proc, Pry.config.editor
181
+ end
182
+
183
+ # Code that has to be called after setup to return to the initial state
184
+ def teardown
185
+ # Reset config
186
+ Pry.config.editor = @old_editor
187
+ Pry.config.pager = @old_pager
188
+ Pry.config.system = @old_system
189
+
190
+ puts "[pry-remote] Remote session terminated"
191
+
192
+ begin
193
+ @client.kill
194
+ rescue DRb::DRbConnError, Errno::ECONNREFUSED
195
+ puts "[pry-remote] Continuing to stop service"
196
+ ensure
197
+ puts "[pry-remote] Ensure stop service"
198
+ DRb.stop_service
199
+ end
200
+ end
201
+
202
+ # Captures $stdout and $stderr if so requested by the client.
203
+ def capture_output
204
+ @old_stdout, $stdout = if @client.stdout
205
+ [$stdout, @client.stdout]
206
+ else
207
+ [$stdout, $stdout]
208
+ end
209
+
210
+ @old_stderr, $stderr = if @client.stderr
211
+ [$stderr, @client.stderr]
212
+ else
213
+ [$stderr, $stderr]
214
+ end
215
+ end
216
+
217
+ # Resets $stdout and $stderr to their previous values.
218
+ def uncapture_output
219
+ $stdout = @old_stdout
220
+ $stderr = @old_stderr
221
+ end
222
+
223
+ def editor_proc
224
+ proc do |file, line|
225
+ File.write(file, @client.editor.call(File.read(file), line))
226
+ end
227
+ end
228
+
229
+ # Actually runs pry-remote
230
+ def run
231
+ puts "[pry-remote] Waiting for client on #{uri}"
232
+ @client.wait
233
+
234
+ puts "[pry-remote] Client received, starting remote session"
235
+ setup
236
+
237
+ Pry.start(@object, @options.merge(:input => client.input_proxy,
238
+ :output => client.output,
239
+ :hooks => @hooks))
240
+ rescue => e
241
+ puts "[pry-remote] Error: #{e.message}"
242
+ ensure
243
+ teardown
244
+ end
245
+
246
+ # @return Object to enter into
247
+ attr_reader :object
248
+
249
+ # @return [PryServer::Client] Client connecting to the pry-remote server
250
+ attr_reader :client
251
+
252
+ # @return [String] Host of the server
253
+ attr_reader :host
254
+
255
+ # @return [Integer] Port of the server
256
+ attr_reader :port
257
+
258
+ # @return [String] URI for DRb
259
+ def uri
260
+ "druby://#{host}:#{port}"
261
+ end
262
+ end
263
+
264
+ # Parses arguments and allows to start the client.
265
+ class CLI
266
+ def initialize(args = ARGV)
267
+ params = Slop.parse args, :help => true do
268
+ banner "#$PROGRAM_NAME [OPTIONS]"
269
+
270
+ on :s, :server=, "Host of the server (#{DefaultHost})", :argument => :optional,
271
+ :default => DefaultHost
272
+ on :p, :port=, "Port of the server (#{DefaultPort})", :argument => :optional,
273
+ :as => Integer, :default => DefaultPort
274
+ on :w, :wait, "Wait for the pry server to come up",
275
+ :default => false
276
+ on :r, :persist, "Persist the client to wait for the pry server to come up each time",
277
+ :default => false
278
+ on :c, :capture, "Captures $stdout and $stderr from the server (true)",
279
+ :default => true
280
+ on :f, "Disables loading of .pryrc and its plugins, requires, and command history "
281
+ end
282
+
283
+ exit if params.help?
284
+
285
+ @host = params[:server]
286
+ @port = params[:port]
287
+
288
+ @wait = params[:wait]
289
+ @persist = params[:persist]
290
+ @capture = params[:capture]
291
+
292
+ Pry.initial_session_setup unless params[:f]
293
+ end
294
+
295
+ # @return [String] Host of the server
296
+ attr_reader :host
297
+
298
+ # @return [Integer] Port of the server
299
+ attr_reader :port
300
+
301
+ # @return [String] URI for DRb
302
+ def uri
303
+ "druby://#{host}:#{port}"
304
+ end
305
+
306
+ attr_reader :wait
307
+ attr_reader :persist
308
+ attr_reader :capture
309
+ alias wait? wait
310
+ alias persist? persist
311
+ alias capture? capture
312
+
313
+ def run
314
+ while true
315
+ connect
316
+ break unless persist?
317
+ end
318
+ end
319
+
320
+ # Connects to the server
321
+ #
322
+ # @param [IO] input Object holding input for pry-remote
323
+ # @param [IO] output Object pry-debug will send its output to
324
+ def connect(input = Pry.config.input, output = Pry.config.output)
325
+ local_ip = UDPSocket.open {|s| s.connect(@host, 1); s.addr.last}
326
+ DRb.start_service "druby://#{local_ip}:0"
327
+ client = DRbObject.new(nil, uri)
328
+
329
+ cleanup(client)
330
+
331
+ input = IOUndumpedProxy.new(input)
332
+ output = IOUndumpedProxy.new(output)
333
+
334
+ begin
335
+ client.input = input
336
+ client.output = output
337
+ rescue DRb::DRbConnError => ex
338
+ if wait? || persist?
339
+ sleep 1
340
+ retry
341
+ else
342
+ raise ex
343
+ end
344
+ end
345
+
346
+ if capture?
347
+ client.stdout = $stdout
348
+ client.stderr = $stderr
349
+ end
350
+
351
+ client.editor = ClientEditor
352
+
353
+ client.thread = Thread.current
354
+
355
+ sleep
356
+ rescue Interrupt
357
+ Pry.run_command('exit', context: Pry.binding_for(Object.new))
358
+ puts "\n"
359
+ ensure
360
+ DRb.stop_service
361
+ end
362
+
363
+ # Clean up the client
364
+ def cleanup(client)
365
+ begin
366
+ # The method we are calling here doesn't matter.
367
+ # This is a hack to close the connection of DRb.
368
+ client.cleanup
369
+ rescue DRb::DRbConnError, NoMethodError
370
+ end
371
+ end
372
+ end
373
+ end
374
+
375
+ class Object
376
+ # Starts a remote Pry session
377
+ #
378
+ # @param [String] host Host of the server
379
+ # @param [Integer] port Port of the server
380
+ # @param [Hash] options Options to be passed to Pry.start
381
+ def remote_pry(host = PryRemoteReloaded::DefaultHost, port = PryRemoteReloaded::DefaultPort, options = {})
382
+ PryRemoteReloaded::Server.new(self, host, port, options).run
383
+ end
384
+
385
+ # a handy alias as many people may think the method is named after the gem
386
+ # (pry-remote)
387
+ alias pry_remote remote_pry
388
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pry-remote-reloaded
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mon ouie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ description: Connect to Pry remotely using DRb
42
+ email: mon.ouie@gmail.com
43
+ executables:
44
+ - pry-remote
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE
50
+ - README.md
51
+ - bin/pry-remote
52
+ - lib/pry-remote-reloaded.rb
53
+ homepage: https://github.com/Jack12816/pry-remote-reloaded
54
+ licenses: []
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.6.13
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Connect to Pry remotely
76
+ test_files: []