pete-live_console 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,91 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rake/rdoctask'
3
+ require 'lib/live_console_config'
4
+ require 'fileutils'
5
+
6
+ $: << "#{File.dirname(__FILE__)}/lib"
7
+
8
+ spec = Gem::Specification.new { |s|
9
+ s.name = LiveConsoleConfig::PkgName
10
+ s.version = LiveConsoleConfig::Version
11
+ s.author = LiveConsoleConfig::Authors
12
+ s.email = LiveConsoleConfig::Email
13
+ s.homepage = LiveConsoleConfig::URL
14
+ s.rubyforge_project = LiveConsoleConfig::Project
15
+
16
+ s.platform = Gem::Platform::RUBY
17
+
18
+ s.files = Dir["{lib,doc,bin,ext}/**/*"].delete_if {|f|
19
+ /\/rdoc(\/|$)/i.match f
20
+ } + %w(Rakefile)
21
+ s.require_path = 'lib'
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = Dir['doc/*'].select(&File.method(:file?))
24
+ s.extensions << 'ext/extconf.rb' if File.exist? 'ext/extconf.rb'
25
+ Dir['bin/*'].map(&File.method(:basename)).map(&s.executables.method(:<<))
26
+
27
+ s.summary = 'A library to support adding an irb console to your ' \
28
+ 'running application.'
29
+ %w().each &s.method(:add_dependency)
30
+ }
31
+
32
+ Rake::RDocTask.new(:doc) { |t|
33
+ t.main = 'doc/README'
34
+ t.rdoc_files.include 'lib/**/*.rb', 'doc/*', 'bin/*', 'ext/**/*.c',
35
+ 'ext/**/*.rb'
36
+ t.options << '-S' << '-N'
37
+ t.rdoc_dir = 'doc/rdoc'
38
+ }
39
+
40
+ Rake::GemPackageTask.new(spec) { |pkg|
41
+ pkg.need_tar_bz2 = true
42
+ }
43
+
44
+ desc "Builds and installs the gem for #{spec.name}"
45
+ task(:install => :package) {
46
+ g = "pkg/#{spec.name}-#{spec.version}.gem"
47
+ system "sudo gem install -l #{g}"
48
+ }
49
+
50
+ desc "Runs IRB, automatically require()ing #{spec.name}."
51
+ task(:irb) {
52
+ exec "irb -Ilib -r#{spec.name}"
53
+ }
54
+
55
+ desc "Cleans up the pkg directory."
56
+ task(:clean) {
57
+ FileUtils.rm_rf 'pkg'
58
+ }
59
+
60
+
61
+ desc "Generates a static gemspec file; useful for github."
62
+ task(:static_gemspec) {
63
+ # This whole thing is hacky.
64
+ spec.validate
65
+ spec_attrs = %w(
66
+ platform author email files require_path has_rdoc extra_rdoc_files
67
+ extensions executables name summary homepage
68
+ ).map { |attr|
69
+ "\ts.#{attr} = #{spec.send(attr).inspect}\n"
70
+ }.join <<
71
+ "\ts.version = #{spec.version.to_s.inspect}\n" <<
72
+ spec.dependencies.map { |dep|
73
+ "\ts.add_dependency #{dep.inspect}\n"
74
+ }.join
75
+
76
+ File.open("#{spec.name}.gemspec", 'w') { |f|
77
+ f.print <<-EOGEMSPEC
78
+ # This is a static gempsec automatically generated by rake. It's better to
79
+ # edit the Rakefile than this file. It is kept in the repository for the
80
+ # benefit of github.
81
+
82
+ spec = Gem::Specification.new { |s|
83
+ #{spec_attrs}}
84
+ if __FILE__ == $0
85
+ Gem::Builder.new(spec).build
86
+ else
87
+ spec
88
+ end
89
+ EOGEMSPEC
90
+ }
91
+ }
data/bin/udscat ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is a client for the unix domain socket version of LiveConsole. It just
4
+ # talks to the socket. It has been included so that you don't have to track
5
+ # down a version of netcat that does this.
6
+
7
+ require 'socket'
8
+
9
+ socket_path = ARGV.first
10
+ if socket_path.nil?
11
+ $stderr.puts "You must supply a path to the socket you want to connect to."
12
+ exit 1
13
+ end
14
+
15
+ client = UNIXSocket.new socket_path
16
+
17
+ $stdin.sync = $stdout.sync = client.sync = true
18
+
19
+ read_thread =
20
+ Thread.new {
21
+ loop {
22
+ begin
23
+ Thread.pass
24
+ $stdout.write client.read_nonblock(1024)
25
+ rescue Errno::EAGAIN, Errno::EINTR => e
26
+ IO.select [client], [], [], 1
27
+ rescue Errno::EOFError, Errno::EPIPE => e
28
+ # nothing
29
+ end
30
+ }
31
+ }
32
+
33
+ Thread.new {
34
+ loop {
35
+ begin
36
+ l = $stdin.read_nonblock(1024)
37
+ rescue Errno::EAGAIN
38
+ retry
39
+ end
40
+
41
+ begin
42
+ client.print l if IO.select [], [client], [], 1
43
+ rescue Errno::EPIPE => e
44
+ $stderr.puts "Other end closed."
45
+ exit 0
46
+ end
47
+ }
48
+ }
49
+
50
+ trap('INT') { exit 0 }
51
+ loop { sleep 1 }
data/doc/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007 Peter Elmore (pete.elmore at gmail.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
data/doc/README ADDED
@@ -0,0 +1,127 @@
1
+ = LiveConsole
2
+
3
+ == Summary
4
+
5
+ LiveConsole is a library for providing IRB over a TCP connection or a Unix
6
+ Domain Socket. If you add it to your application, you can run arbitrary code
7
+ against your application.
8
+ For example, you can:
9
+ * Inspect the state of a running application
10
+ * Change the state of the application
11
+ * Patch code on the fly, without a restart.
12
+ * Let anyone on the net 0wn you if you bind to a public interface. :)
13
+ It's useful as a diagnostic tool, a debugging tool, and a way to impress your
14
+ friends or get those Lisp guys off your back. You know the ones I mean.
15
+
16
+ == Stern Security Warning. Grrr.
17
+
18
+ Have a look at the bugs section. It should be pretty apparent that incorrect
19
+ use of this library could create a large security hole, especially before
20
+ authentication is implemented.
21
+
22
+ == Installation
23
+
24
+ You can install via rubygems,
25
+
26
+ gem install live_console
27
+
28
+ or plain old setup.rb:
29
+
30
+ ruby setup.rb install
31
+
32
+ == How to use LiveConsole
33
+
34
+ LiveConsole is very easy to use in your own app:
35
+
36
+ require 'rubygems'
37
+ require 'live_console'
38
+
39
+ # Create a LiveConsole using TCP on port 1337
40
+ lc = LiveConsole.new :socket, :port => 1337
41
+ # We're not yet accepting connections. We need to start it up:
42
+ lc.start # Starts the LiveConsole thread
43
+ # At this point, users can connect and get an IRB prompt.
44
+ lc.stop # Kills the LiveConsole thread
45
+ # Now, no one can connect.
46
+
47
+ # Create a LiveConsole using a Unix socket in /tmp/live-console.sock
48
+ lc = LiveConsole.new :unix_socket, :path => '/tmp/live-console.sock'
49
+ # As above:
50
+ lc.start
51
+ lc.stop
52
+
53
+ # Have a LiveConsole run code in a binding other than the top-level:
54
+ lc = LiveConsole.new :unix_socket, :path => '/tmp/live-console.sock'
55
+ :bind => binding
56
+ lc.start
57
+ # That will start IRB in the current binding. There is also an accessor:
58
+ lc.bind = binding
59
+ # Of course, you must restart before IRB will see the new binding:
60
+ lc.restart
61
+
62
+ Have a look at doc/lc_example.rb or doc/lc_unix_example.rb for brief examples
63
+ of how to use LiveConsole.
64
+
65
+ Try just running it:
66
+
67
+ $ ruby doc/lc_example.rb 4000 test
68
+ # Then, in a different shell:
69
+ $ netcat localhost 4000
70
+ irb(main):001:0> puts 'Wow, magic!'
71
+
72
+ $ ruby doc/lc_unix_example.rb /tmp/live-console.sock
73
+ # Then, in a different shell:
74
+ $ udscat /tmp/live-console.sock
75
+ irb(main):001:0> puts 'Words cannot describe the joy I feel.'
76
+
77
+ You can get creative about it, only starting LiveConsole when there's an
78
+ unhandled exception in your server, and then calling LiveConsole#stop when
79
+ you've diagnosed and fixed whatever the problem was.
80
+
81
+ Additionally, if you want to run LiveConsole on a server, but run netcat
82
+ locally, you can use SSH port forwarding to avoid having to open LiveConsole
83
+ to the world:
84
+
85
+ ssh -L4000:localhost:4000 you@server
86
+
87
+ Then, locally, you can do
88
+
89
+ netcat localhost 4000
90
+
91
+ and get the remote LiveConsole. man ssh for more details. Of course, this
92
+ only works for the TCP socket mode.
93
+
94
+ == Bugs
95
+
96
+ LiveConsole lacks many of the niceties of IRB on the console, like Readline
97
+ support.
98
+
99
+ Typing exit, hitting ^D, or sending signals (like INT or STOP) doesn't work.
100
+ Just exit the program you used to connect to it. This has more to do with the
101
+ program you use to connect to the socket.
102
+
103
+ For TCP connections, there is no authentication support yet, although it is
104
+ planned for the near future. This creates a security risk: anyone that can
105
+ connect to the socket can run arbitrary Ruby code as the user who owns the
106
+ process. In fact, even binding to localhost can be a security issue if you're
107
+ on a box with any untrusted users. If there's a chance you don't know what
108
+ you're doing, avoid using this library. The Unix Domain Socket version is more
109
+ secure, as you can control access via filesystem permissions.
110
+
111
+ Only one client can connect at a time. I don't think anyone needs multiple LC
112
+ connections to serve multiple instances of IRB to various clients, but if you
113
+ need it, let me know.
114
+
115
+ The README contains a slur against Lisp guys. Please stop hitting me with that PDP-10 manual. I love your language and the lambda tattoo on your chest.
116
+
117
+ Other than that, LiveConsole doesn't have any known bugs, but it is odd
118
+ software that also monkey-patches IRB, so they are likely to be there. Bug
119
+ reports and patches gratefully accepted.
120
+
121
+ == Credits
122
+
123
+ Pete Elmore -- (pete.elmore(a)gmail.com)
124
+
125
+ == Home page
126
+
127
+ http://debu.gs/live-console
data/doc/lc_example.rb ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'live_console'
5
+
6
+ print <<-EOF
7
+ This is a demo program for LiveConsole. It starts a LiveConsole on the
8
+ specified port, and you can connect to it by using netcat or telnet to connect
9
+ to the specified port.
10
+ Usage:
11
+ #{$0} [port_number [value_for_$x]]
12
+ The default port is 3333, and $x is set by default to nil. Run this program,
13
+ and then in a different terminal, connect to it via netcat or telnet. You can
14
+ check that the value of $x is exactly what you set it to, and that you're
15
+ working inside this process, but there's not much to do inside the example
16
+ script. :)
17
+
18
+ EOF
19
+
20
+ port = ARGV.first.to_i
21
+ port = port.zero? ? 3333 : port
22
+ $x = ARGV[1]
23
+
24
+ lc = LiveConsole.new :socket, :port => port, :bind => binding
25
+ lc.start
26
+
27
+ puts "My PID is #{Process.pid}, " \
28
+ "I'm running on port #{port}, and $x = #{$x.inspect}"
29
+
30
+ oldx = $x
31
+ loop {
32
+ if $x != oldx
33
+ puts "The time is now #{Time.now.strftime('%R:%S')}.",
34
+ "The value of $x changed from #{oldx.inspect} to #{$x.inspect}."
35
+ oldx = $x
36
+ end
37
+ sleep 1
38
+ }
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'live_console'
5
+
6
+ default_path = "/tmp/lc_example_#{Process.uid}.sock"
7
+
8
+ print <<-EOF
9
+ This is a demo program for LiveConsole. It starts a LiveConsole at the
10
+ specified path, and you can connect to it by using netcat or telnet to connect
11
+ to the specified port.
12
+ Usage:
13
+ #{$0} [path_to_socket [value_for_$x]]
14
+ The default path is #{default_path}, and $x is set by default to nil.
15
+ Run this program, and then in a different terminal, connect to it via
16
+ the supplied udscat program or the BSD version of netcat.
17
+ EOF
18
+
19
+ path = ARGV.first
20
+ path = path.nil? ? default_path : path
21
+ $x = ARGV[1]
22
+
23
+ lc = LiveConsole.new :unix_socket, :path => path, :bind => binding
24
+ lc.start
25
+
26
+ puts "My PID is #{Process.pid}, " \
27
+ "I'm running on #{path}, and $x = #{$x.inspect}"
28
+
29
+ oldx = $x
30
+ loop {
31
+ if $x != oldx
32
+ puts "The time is now #{Time.now.strftime('%R:%S')}.",
33
+ "The value of $x changed from #{oldx.inspect} to #{$x.inspect}."
34
+ oldx = $x
35
+ end
36
+ sleep 1
37
+ }
@@ -0,0 +1,27 @@
1
+ class LiveConsole::IOMethods::SocketIO
2
+ DefaultOpts = {
3
+ :host => '127.0.0.1',
4
+ }.freeze
5
+ RequiredOpts = DefaultOpts.keys + [:port]
6
+
7
+ include LiveConsole::IOMethods::IOMethod
8
+
9
+ def start
10
+ @server ||= TCPServer.new host, port
11
+
12
+ begin
13
+ self.raw_input = self.raw_output = server.accept_nonblock
14
+ return true
15
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
16
+ Errno::EINTR => e
17
+ select
18
+ retry
19
+ end
20
+ end
21
+
22
+ def stop
23
+ select
24
+ raw_input.close rescue nil
25
+ end
26
+
27
+ end
@@ -0,0 +1,31 @@
1
+ require 'socket'
2
+
3
+ class LiveConsole::IOMethods::UnixSocketIO
4
+ DefaultOpts = {
5
+ :mode => 0600,
6
+ :uid => Process.uid,
7
+ :gid => Process.gid,
8
+ }
9
+ RequiredOpts = DefaultOpts.keys + [:path]
10
+
11
+ include LiveConsole::IOMethods::IOMethod
12
+
13
+ def start
14
+ @server ||= UNIXServer.new path
15
+
16
+ begin
17
+ self.raw_input = self.raw_output = server.accept_nonblock
18
+ raw_input.sync = true
19
+ return true
20
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
21
+ Errno::EINTR => e
22
+ select
23
+ retry
24
+ end
25
+ end
26
+
27
+ def stop
28
+ select
29
+ raw_input.close
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ module LiveConsole::IOMethods
2
+ List = []
3
+
4
+ Dir[File.join(File.dirname(__FILE__), 'io_methods', '*.rb')].each { |entry|
5
+ fname = entry.sub /\.rb$/, ''
6
+ classname = File.basename(entry, '.rb').capitalize.
7
+ gsub(/_(\w)/) { $1.upcase }.sub(/io$/i, 'IO').to_sym
8
+ mname = File.basename(fname).sub(/_io$/, '').to_sym
9
+
10
+ autoload classname, fname
11
+ List << mname
12
+
13
+ define_method(mname) {
14
+ const_get classname
15
+ }
16
+ }
17
+ List.freeze
18
+
19
+ extend self
20
+
21
+ module IOMethod
22
+ def initialize(opts)
23
+ self.opts = self.class::DefaultOpts.merge opts
24
+ unless missing_opts.empty?
25
+ raise ArgumentError, "Missing opts for " \
26
+ "#{self.class.name}: #{missing_opts.inspect}"
27
+ end
28
+ end
29
+
30
+ def missing_opts
31
+ self.class::RequiredOpts - opts.keys
32
+ end
33
+
34
+ def self.included(other)
35
+ other.instance_eval {
36
+ readers = [:opts, :raw_input, :raw_output]
37
+ attr_accessor *readers
38
+ private *readers.map { |r| (r.to_s + '=').to_sym }
39
+
40
+ other::RequiredOpts.each { |opt|
41
+ define_method(opt) { opts[opt] }
42
+ }
43
+ }
44
+ end
45
+
46
+ def select
47
+ IO.select [server], [], [], 1 if server
48
+ end
49
+
50
+ private
51
+ attr_accessor :server
52
+ end
53
+ end
@@ -0,0 +1,227 @@
1
+ # LiveConsole
2
+ # Pete Elmore (pete.elmore@gmail.com), 2007-10-18
3
+ # debu.gs/live-console
4
+ # See doc/LICENSE.
5
+
6
+ require 'irb'
7
+ require 'irb/frame'
8
+ require 'socket'
9
+ require 'live_console_config'
10
+
11
+ # LiveConsole provides a socket that can be connected to via netcat or telnet
12
+ # to use to connect to an IRB session inside a running process. It creates a
13
+ # thread that listens on the specified address/port or Unix Domain Socket path,
14
+ # and presents connecting clients with an IRB shell. Using this, you can
15
+ # execute code on a running instance of a Ruby process to inspect the state or
16
+ # even patch code on the fly. There is currently no readline support.
17
+ class LiveConsole
18
+ include Socket::Constants
19
+ autoload :IOMethods, 'live_console/io_methods'
20
+
21
+ attr_accessor :io_method, :io, :thread, :bind
22
+ private :io_method=, :io=, :thread=
23
+
24
+ # call-seq:
25
+ # # Bind a LiveConsole to localhost:3030 (only allow clients on this
26
+ # # machine to connect):
27
+ # LiveConsole.new :socket, :port => 3030
28
+ # # Accept connections from anywhere on port 3030. Ridiculously insecure:
29
+ # LiveConsole.new(:socket, :port => 3030, :host => '0.0.0.0')
30
+ # # Use a Unix Domain Socket (which is more secure) instead:
31
+ # LiveConsole.new(:unix_socket, :path => '/tmp/my_liveconsole.sock',
32
+ # :mode => 0600, :uid => Process.uid, :gid => Process.gid)
33
+ # # By default, the mode is 0600, and the uid and gid are those of the
34
+ # # current process. These three options are for the file's permissions.
35
+ # # You can also supply a binding for IRB's toplevel:
36
+ # LiveConsole.new(:unix_socket,
37
+ # :path => "/tmp/live_console_#{Process.pid}.sock", :bind => binding)
38
+ #
39
+ # Creates a new LiveConsole. You must next call LiveConsole#start when you
40
+ # want to spawn the thread to accept connections and start the console.
41
+ def initialize(io_method, opts = {})
42
+ self.io_method = io_method.to_sym
43
+ self.bind = opts.delete :bind
44
+ unless IOMethods::List.include?(self.io_method)
45
+ raise ArgumentError, "Unknown IO method: #{io_method}"
46
+ end
47
+
48
+ init_io opts
49
+ end
50
+
51
+ # LiveConsole#start spawns a thread to listen for, accept, and provide an
52
+ # IRB console to new connections. If a thread is already running, this
53
+ # method simply returns false; otherwise, it returns the new thread.
54
+ def start
55
+ if thread
56
+ if thread.alive?
57
+ return false
58
+ else
59
+ thread.join
60
+ self.thread = nil
61
+ end
62
+ end
63
+
64
+ self.thread = Thread.new {
65
+ loop {
66
+ Thread.pass
67
+ if io.start
68
+ irb_io = GenericIOMethod.new io.raw_input, io.raw_output
69
+ begin
70
+ IRB.start_with_io(irb_io, bind)
71
+ rescue Errno::EPIPE => e
72
+ io.stop
73
+ end
74
+ end
75
+ }
76
+ }
77
+ thread
78
+ end
79
+
80
+ # Ends the running thread, if it exists. Returns true if a thread was
81
+ # running, false otherwise.
82
+ def stop
83
+ if thread
84
+ if thread == Thread.current
85
+ self.thread = nil
86
+ Thread.current.exit!
87
+ end
88
+
89
+ thread.exit
90
+ if thread.join(0.1).nil?
91
+ thread.exit!
92
+ end
93
+ self.thread = nil
94
+ true
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+ # Restarts. Useful for binding changes. Return value is the same as for
101
+ # LiveConsole#start.
102
+ def restart
103
+ r = lambda { stop; start }
104
+ if thread == Thread.current
105
+ Thread.new &r # Leaks a thread, but works.
106
+ else
107
+ r.call
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ def init_irb
114
+ return if @@irb_inited_already
115
+ IRB.setup nil
116
+ @@irb_inited_already = true
117
+ end
118
+
119
+ def init_io opts
120
+ self.io = IOMethods.send(io_method).new opts
121
+ end
122
+ end
123
+
124
+ # We need to make a couple of changes to the IRB module to account for using a
125
+ # weird I/O method and re-starting IRB from time to time.
126
+ module IRB
127
+ @inited = false
128
+
129
+ ARGV = []
130
+
131
+ # Overridden a la FXIrb to accomodate our needs.
132
+ def IRB.start_with_io(io, bind, &block)
133
+ unless @inited
134
+ setup '/dev/null'
135
+ IRB.parse_opts
136
+ IRB.load_modules
137
+ @inited = true
138
+ end
139
+
140
+ bind ||= IRB::Frame.top(1)
141
+ ws = IRB::WorkSpace.new(bind)
142
+ irb = Irb.new(ws, io, io)
143
+
144
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
145
+ @CONF[:MAIN_CONTEXT] = irb.context
146
+ @CONF[:PROMPT_MODE] = :INF_RUBY
147
+
148
+ catch(:IRB_EXIT) {
149
+ begin
150
+ irb.eval_input
151
+ rescue StandardError => e
152
+ irb.print([e.to_s, e.backtrace].flatten.join("\n") + "\n")
153
+ retry
154
+ end
155
+ }
156
+ irb.print "\n"
157
+ end
158
+
159
+ class Context
160
+ # Fix an IRB bug; it ignores your output method.
161
+ def output *args
162
+ @output_method.print *args
163
+ end
164
+ end
165
+
166
+ class Irb
167
+ # Fix an IRB bug; it ignores your output method.
168
+ def printf(*args)
169
+ context.output(sprintf(*args))
170
+ end
171
+
172
+ # Fix an IRB bug; it ignores your output method.
173
+ def print(*args)
174
+ context.output *args
175
+ end
176
+ end
177
+ end
178
+
179
+ # The GenericIOMethod is a class that wraps I/O for IRB.
180
+ class GenericIOMethod < IRB::StdioInputMethod
181
+ # call-seq:
182
+ # GenericIOMethod.new io
183
+ # GenericIOMethod.new input, output
184
+ #
185
+ # Creates a GenericIOMethod, using either a single object for both input
186
+ # and output, or one object for input and another for output.
187
+ def initialize(input, output = nil)
188
+ @input, @output = input, output
189
+ @line = []
190
+ @line_no = 0
191
+ end
192
+
193
+ attr_reader :input
194
+ def output
195
+ @output || input
196
+ end
197
+
198
+ def gets
199
+ output.print @prompt
200
+ output.flush
201
+ @line[@line_no += 1] = input.gets
202
+ # @io.flush # Not sure this is needed.
203
+ @line[@line_no]
204
+ end
205
+
206
+ # Returns the user input history.
207
+ def lines
208
+ @line.dup
209
+ end
210
+
211
+ def print(*a)
212
+ output.print *a
213
+ end
214
+
215
+ def file_name
216
+ input.inspect
217
+ end
218
+
219
+ def eof?
220
+ input.eof?
221
+ end
222
+
223
+ def close
224
+ input.close
225
+ output.close if @output
226
+ end
227
+ end
@@ -0,0 +1,9 @@
1
+ # This module houses a pile of informative constants for LiveConsole.
2
+ module LiveConsoleConfig
3
+ Authors = 'Pete Elmore'
4
+ Email = 'pete.elmore@gmail.com'
5
+ PkgName = 'live_console'
6
+ Version = '0.2.1'
7
+ URL = 'http://debu.gs/live-console'
8
+ Project = 'live-console'
9
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pete-live_console
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Pete Elmore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: pete.elmore@gmail.com
18
+ executables:
19
+ - udscat
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - doc/LICENSE
24
+ - doc/README
25
+ - doc/lc_example.rb
26
+ - doc/lc_unix_example.rb
27
+ files:
28
+ - lib/live_console/io_methods/socket_io.rb
29
+ - lib/live_console/io_methods/unix_socket_io.rb
30
+ - lib/live_console/io_methods.rb
31
+ - lib/live_console.rb
32
+ - lib/live_console_config.rb
33
+ - doc/LICENSE
34
+ - doc/README
35
+ - doc/lc_example.rb
36
+ - doc/lc_unix_example.rb
37
+ - bin/udscat
38
+ - Rakefile
39
+ has_rdoc: true
40
+ homepage: http://debu.gs/live-console
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: A library to support adding an irb console to your running application.
65
+ test_files: []
66
+