repl_runner 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 05eaef051153f7d7b06f9c68c4933a0955d58e6b
4
+ data.tar.gz: a371ba6c53712e73296da79c69c3f604fa7c841e
5
+ SHA512:
6
+ metadata.gz: 7d406e33b664960f9af50c4aea6a6ba3ebfbf3d848193f18732aad7c340f6d3e11c9125786b0b70fddcd72b482f57f90e2a432d6d6d7ea63c879b7739bc2a1a2
7
+ data.tar.gz: ecaee16aaf62250a286ffb4cf1e0deeda8cbecdaabb7d1b8015a537c6f89e1f68fe067ca2df8e6eefa9e72c5115012a2071db34601b149629ced825dc317a7bb
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in repl_runner.gemspec
4
+ gemspec
5
+
6
+
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ MIT Yo
@@ -0,0 +1,80 @@
1
+ # REPL Runner
2
+
3
+ Drive irb, bash, or another REPL like environment programatically.
4
+
5
+ ## Why?
6
+
7
+ I needed to be able to run `rails console` commands programatically for testing buildpacks with [Hatchet](http://github.com/heroku/hatchet).
8
+
9
+ ## How?
10
+
11
+ Ruby includes the PTY library for creating and running [pseudo terminals](http://en.wikipediaorg/wiki/Pseudo_terminal). Unfortunately opening up a remote `irb` or `rails console` session and driving it without deadlocking your process is fairly maddeningly difficult. This library provides a safe abstraction for running commands and parsing the inputs from the outputs.
12
+
13
+ ## Shouldn't this be Called TerminalRunner?
14
+
15
+ Well technically it should be called Pseudo Terminal Runner, but ReplRunner just has a certain ring to it.
16
+
17
+ ## Install
18
+
19
+ In your gemfile add:
20
+
21
+ ```
22
+ gem 'repl_runner'
23
+ ```
24
+
25
+ Then run `$ bundle install`.
26
+
27
+ ## Use
28
+
29
+ To open a remote rails console on heroku with the heroku toolbelt installed, you could drive it like this:
30
+
31
+ ```
32
+ ReplRunner.new(:rails_console, "heroku run rails console -a testapp").run do |repl|
33
+ repl.run('a = 1 + 1') {|result| assert_match '2', result }
34
+ repl.run('"hello" + "world"') {|result| assert_match 'helloworld', result }
35
+ repl.run("a * 'foo'") {|result| assert_match 'foofoo', result}
36
+ end
37
+ ```
38
+
39
+ The first argument `:rails_console` tells ReplReader what type of a session we are going to open up. The second `"heroku run rails console -a testapp"` is the command we want to use to start our psuedo remote terminal.
40
+
41
+ You can then call `run` on this and pass in a block. The block yields to a `MultiRepl` instance that can take the command `run` along with arguments to pass into the command line such as `'1+1'`
42
+
43
+ All outputs will be strings. Commands wait for all commands to finish before returning anything, this is why you supply the `run` command with a block. When the command is done the block will be executed and the result of the command passed to it. This helps us parse the results much more effectively. If you need an immediate return, it's possible but I wouldn't recommend it. As a result I've left that functionality out for now.
44
+
45
+ Also note that you will get the entire return including any prompts if you run an `irb` session locally with `.9.3` you might see a result like this:
46
+
47
+ ```
48
+ $ irb
49
+ 1.9.3p392 :001 > 1 + 1
50
+ => 2
51
+ ```
52
+
53
+ So when you run this via ReplRunner you will get a result string like this
54
+
55
+ ```
56
+ ReplRunner.new(:irb) do |repl|
57
+ repl.run('1 + 1') {|result| puts result }
58
+ end
59
+ " => 2\r\r\n"
60
+ ```
61
+
62
+ Note: if you don't pass in a second parameter i.e. only pass in `:irb` that exact command will be used to start your session.
63
+
64
+ ## Configure
65
+
66
+ By default ReplRunner knows how to run `:rails_console`, `:bash`, and `:irb`. You can over-write existing defaults by re-defining them. You can register more custom commands you want like this:
67
+
68
+ ```ruby
69
+ ReplRunner.register_commands(:rails_console, :irb) do |config|
70
+ config.terminate_command "exit" # the command you use to end the 'rails console'
71
+ config.startup_timeout 60 # seconds to boot
72
+ config.return_char "\n" # the character that submits the command
73
+ config.sync_stdout "STDOUT.sync = true" # force REPL to not buffer standard out
74
+ end
75
+ ```
76
+
77
+ ## License
78
+
79
+ MIT (The Georgia Tech of the North) License. Do whatever you want with this code: I'm not liable or responsible for anything.
80
+
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ task :default => [:test]
8
+
9
+ test_task = Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = false
14
+ end
15
+
16
+
17
+ STDOUT.sync = true
@@ -0,0 +1,75 @@
1
+ # stdlib
2
+ require 'timeout'
3
+ require 'pty'
4
+
5
+ # gems
6
+ require 'active_support/core_ext/object/blank'
7
+
8
+ class ReplRunner
9
+ attr_accessor :command, :repl
10
+
11
+ class NoResults < StandardError
12
+ def initialize(command, string)
13
+ msg = "No result found for command: #{command.inspect} in output: #{string.inspect}"
14
+ super(msg)
15
+ end
16
+ end
17
+
18
+ class UnregisteredCommand < StandardError
19
+ def initialize(cmd_type)
20
+ msg = "Cannot find registered command type: #{cmd_type.inspect}"
21
+ super(msg)
22
+ end
23
+ end
24
+
25
+ def initialize(cmd_type, command = nil, options = {})
26
+ command = cmd_type.to_s if command.nil?
27
+ cmd_type = cmd_type.chomp.gsub(/\s/, '_').to_sym if cmd_type.is_a?(String)
28
+ @command = command
29
+ @repl = nil
30
+ @config = known_configs[cmd_type]
31
+ raise UnregisteredCommand.new(cmd_type) unless @config
32
+ @options = options
33
+ end
34
+
35
+ def known_configs
36
+ self.class.known_configs
37
+ end
38
+
39
+ class << self
40
+ def known_configs
41
+ @known_configs ||= {}
42
+ end
43
+
44
+ def register_command(*commands, &block)
45
+ config = Config.new(commands)
46
+ commands.each do |command|
47
+ known_configs[command] = config
48
+ end
49
+ yield config
50
+ end
51
+ alias :register_commands :register_command
52
+ end
53
+
54
+ def start
55
+ @repl = MultiRepl.new(command, @config.to_options.merge(@options))
56
+ end
57
+
58
+ def close
59
+ repl.close
60
+ end
61
+
62
+ def run(&block)
63
+ repl = start
64
+ yield repl
65
+ repl.execute
66
+ ensure
67
+ close if repl
68
+ end
69
+ end
70
+
71
+ require 'repl_runner/pty_party'
72
+ require 'repl_runner/multi_command_parser'
73
+ require 'repl_runner/multi_repl'
74
+ require 'repl_runner/config'
75
+ require 'repl_runner/default_config'
@@ -0,0 +1,34 @@
1
+ class ReplRunner
2
+ class Config
3
+ attr_accessor :commands
4
+
5
+ def initialize(*commands)
6
+ @commands = commands
7
+ end
8
+
9
+ def terminate_command(command)
10
+ @terminate_command = command
11
+ end
12
+
13
+ def startup_timeout(command)
14
+ @startup_timeout = command
15
+ end
16
+
17
+ def return_char(char)
18
+ @return_char = char
19
+ end
20
+
21
+ def sync_stdout(string)
22
+ @sync_stdout = string
23
+ end
24
+
25
+ def to_options
26
+ {
27
+ terminate_command: @terminate_command,
28
+ startup_timeout: @startup_timeout,
29
+ return_char: @return_char,
30
+ sync_stdout: @sync_stdout
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ ReplRunner.register_command(:rails_console, :irb) do |config|
2
+ config.terminate_command "exit" # the command you use to end the 'rails console'
3
+ config.startup_timeout 60 # seconds to boot
4
+ config.return_char "\n" # the character that submits the command
5
+ config.sync_stdout "STDOUT.sync = true" # force REPL to not buffer standard out
6
+ end
7
+
8
+ ReplRunner.register_command(:bash, :sh) do |config|
9
+ config.terminate_command "exit" # the command you use to end the 'bash' session
10
+ config.startup_timeout 60 # seconds to boot
11
+ config.return_char "\n" # the character that submits the command
12
+ config.sync_stdout nil # not needed
13
+ end
@@ -0,0 +1,34 @@
1
+ class ReplRunner
2
+ class MultiCommandParser
3
+ attr_accessor :commands, :raw
4
+
5
+ def initialize(commands, terminate_command = nil)
6
+ @commands = commands
7
+ @terminate_command = terminate_command
8
+ @raw = ""
9
+ end
10
+
11
+ def command_to_regex(command)
12
+ /#{Regexp.quote(command)}\r*\n+/
13
+ end
14
+
15
+ def parse(string)
16
+ self.raw = string.dup
17
+ @parsed_result = []
18
+
19
+ # remove terminate command
20
+ string = string.gsub(command_to_regex(@terminate_command), '') if @terminate_command
21
+ # attack the string from the end
22
+ commands.reverse.each do |command|
23
+ regex = command_to_regex(command)
24
+ result_array = string.split(regex)
25
+ @parsed_result << result_array.pop
26
+ raise NoResults.new(command, raw) if @parsed_result.last.blank?
27
+ string = result_array.join('')
28
+ end
29
+
30
+ @parsed_result.reverse!
31
+ return @parsed_result
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,80 @@
1
+ class ReplRunner
2
+ # Takes in a command, to start a REPL session with PTY
3
+ # builds up a list of commands we want to run in our REPL
4
+ # executes them all at a time, reads in the result and returns results
5
+ # back to blocks
6
+ class MultiRepl
7
+ class UnexpectedExit < StandardError
8
+ end
9
+
10
+ DEFAULT_STARTUP_TIMEOUT = 60
11
+ DEFAULT_RETURN_CHAR = "\n"
12
+
13
+ attr_accessor :command
14
+
15
+ def initialize(command, options = {})
16
+ @command_parser = options[:command_parser] || MultiCommandParser
17
+ @return_char = options[:return_char] || DEFAULT_RETURN_CHAR
18
+ @startup_timeout = options[:startup_timeout] || DEFAULT_STARTUP_TIMEOUT
19
+ @terminate_command = options[:terminate_command] or raise "must set default `terminate_command`"
20
+ @stync_stdout = options[:sync_stdout]
21
+
22
+ @command = command
23
+ @commands = []
24
+ @jobs = []
25
+ end
26
+
27
+ def pty
28
+ @pty ||= PtyParty.new(command)
29
+ end
30
+
31
+ def run(command, &block)
32
+ @commands << command
33
+ @jobs << (block || Proc.new {|result| })
34
+ end
35
+
36
+ def write(command)
37
+ pty.write("#{command}#{@return_char}")
38
+ end
39
+
40
+ def alive?
41
+ !!::Process.kill(0, pty.pid) rescue false # kill zero will return the status of the proceess without killing it
42
+ end
43
+
44
+ def dead?
45
+ !alive?
46
+ end
47
+
48
+ def read
49
+ raise UnexpectedExit, "Repl: '#{@command}' exited unexpectedly" if dead?
50
+ @output = pty.read(@startup_timeout)
51
+ end
52
+
53
+ def parse_results
54
+ @parsed_results ||= @command_parser.new(@commands, @terminate_command).parse(read)
55
+ end
56
+
57
+ def close
58
+ pty.close
59
+ end
60
+
61
+ def execute
62
+ write(@sync_stdout) if @sync_stdout
63
+
64
+ @commands.each do |command|
65
+ write(command)
66
+ end
67
+
68
+ write(@terminate_command)
69
+
70
+
71
+ output_array = parse_results
72
+
73
+ @jobs.each_with_index do |job, index|
74
+ job.call(output_array[index])
75
+ end
76
+ rescue NoResults => e
77
+ raise e, "Booting up REPL with command: #{command.inspect} \n#{e.message}"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,51 @@
1
+ class ReplRunner
2
+ class PtyParty
3
+ attr_accessor :input, :output, :pid
4
+ TIMEOUT = 1
5
+
6
+ def initialize(command)
7
+ @output, @input, @pid = PTY.spawn(command)
8
+ end
9
+
10
+ def write(cmd)
11
+ input.write(cmd)
12
+ rescue Errno::EIO => e
13
+ raise e, "#{e.message} | trying to write '#{cmd}'"
14
+ end
15
+
16
+ def run(cmd, timeout = TIMEOUT)
17
+ write(cmd)
18
+ return read(timeout)
19
+ end
20
+
21
+ def close(timeout = TIMEOUT)
22
+ Timeout::timeout(timeout) do
23
+ input.close
24
+ output.close
25
+ end
26
+ rescue Timeout::Error
27
+ # do nothing
28
+ ensure
29
+ Process.kill('TERM', pid) if pid.present?
30
+ end
31
+
32
+ # There be dragons - (You're playing with process deadlock)
33
+ #
34
+ # We want to read the whole output of the command
35
+ # First pull all contents from stdout (except we don't know how many there are)
36
+ # So we have to go until our process deadlocks, then we timeout and return the string
37
+ #
38
+ def read(timeout = TIMEOUT, str = "")
39
+ while true
40
+ Timeout::timeout(timeout) do
41
+ str << output.readline
42
+ break if output.eof?
43
+ end
44
+ end
45
+
46
+ return str
47
+ rescue Timeout::Error, EOFError, Errno::EIO
48
+ return str
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ class ReplRunner
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/repl_runner/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Richard Schneeman"]
6
+ gem.email = ["richard@heroku.com"]
7
+ gem.description = %q{ Programatically drive REPL like interfaces, irb, bash, etc. }
8
+ gem.summary = %q{ Run your REPL like interfaces like never before}
9
+ gem.homepage = "https://github.com/schneems/repl_runner"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "repl_runner"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = ReplRunner::VERSION
17
+ gem.license = 'MIT' # the Georgia Tech of the North
18
+
19
+ gem.add_dependency "activesupport"
20
+
21
+ gem.add_development_dependency "rake"
22
+ gem.add_development_dependency "mocha"
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @hash = {terminate_command: "exitYoSelf",
7
+ startup_timeout: 99,
8
+ return_char: "poof",
9
+ sync_stdout: "NSync"}
10
+ end
11
+
12
+ def test_config
13
+ config = ReplRunner::Config.new(:irb, :rails_console)
14
+ config.terminate_command @hash[:terminate_command]
15
+ assert_equal @hash[:terminate_command], config.to_options[:terminate_command]
16
+
17
+ config.startup_timeout @hash[:startup_timeout]
18
+ assert_equal @hash[:startup_timeout], config.to_options[:startup_timeout]
19
+
20
+ config.return_char @hash[:return_char]
21
+ assert_equal @hash[:return_char], config.to_options[:return_char]
22
+
23
+ config.sync_stdout @hash[:sync_stdout]
24
+ assert_equal @hash[:sync_stdout], config.to_options[:sync_stdout]
25
+
26
+ assert_equal @hash, config.to_options
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class MultiCommandParserTest < Test::Unit::TestCase
4
+ def test_removes_command_from_string
5
+ hash = {commands: ["1+1", "'hello' + 'world'"],
6
+ string: "1+1\r\n => 2 \r\n'hello' + 'world'\r\n => \"helloworld\" \r\n",
7
+ expect: [" => 2 \r\n", " => \"helloworld\" \r\n"]
8
+ }
9
+ cp = ReplRunner::MultiCommandParser.new(hash[:commands])
10
+ assert_equal hash[:expect], cp.parse(hash[:string])
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ class MultiReplTest < Test::Unit::TestCase
4
+ def test_local_irb_stream
5
+ repl = ReplRunner::MultiRepl.new('irb', terminate_command: 'exit')
6
+ repl.run('111+111') {|r| assert_match '222', r }
7
+ repl.run("'hello' + 'world'") {|r| assert_match 'helloworld', r }
8
+ repl.run("a = 'foo'")
9
+ repl.run("b = 'bar'") {} # test empty block doesn't throw exceptions
10
+ repl.run("a * 5") {|r| assert_match 'foofoofoofoofoo', r }
11
+ repl.execute
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class StreamExecTest < Test::Unit::TestCase
4
+ def test_local_irb_stream
5
+ repl = ReplRunner::PtyParty.new("irb")
6
+ repl.run("STDOUT.sync = true\n")
7
+ assert_equal "1+1\r\n => 2 \r\n", repl.run("1+1\n")
8
+ assert_equal "'hello' + 'world'\r\n => \"helloworld\" \r\n", repl.run("'hello' + 'world'\n")
9
+ end
10
+
11
+ def test_multi_command_read
12
+ repl = ReplRunner::PtyParty.new("irb")
13
+ repl.write("STDOUT.sync = true\n")
14
+ repl.write("1+1\n")
15
+ repl.write("exit\n")
16
+ assert_equal "STDOUT.sync = true\r\n1+1\r\nexit\r\n2.0.0-p0 :001 > STDOUT.sync = true\r\n => true \r\n2.0.0-p0 :002 > 1+1\r\n => 2 \r\n2.0.0-p0 :003 > exit\r\n", repl.read
17
+ end
18
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class ReplRunnerTest < Test::Unit::TestCase
4
+ def test_local_irb_stream
5
+ ReplRunner.new(:irb).run do |repl|
6
+ repl.run('111+111') {|r| assert_match '222', r }
7
+ repl.run("'hello' + 'world'") {|r| assert_match 'helloworld', r }
8
+ repl.run("a = 'foo'")
9
+ repl.run("b = 'bar'") {} # test empty block doesn't throw exceptions
10
+ repl.run("a * 5") {|r| assert_match 'foofoofoofoofoo', r }
11
+ end
12
+ end
13
+
14
+ def test_ensure_exit
15
+ assert_raise(ReplRunner::NoResults) do
16
+ ReplRunner.new(:irb, "irb -r ./test/require/never-boots.rb", startup_timeout: 2).run do |repl|
17
+ repl.run('111+111') {|r| }
18
+ end
19
+ end
20
+ end
21
+
22
+ def test_bash
23
+ ReplRunner.new(:bash).run do |repl|
24
+ repl.run('ls') {|r| assert_match /Gemfile/, r}
25
+ end
26
+ end
27
+
28
+ def test_unknown_command
29
+ assert_raise ReplRunner::UnregisteredCommand do
30
+ ReplRunner.new(:zootsuite)
31
+ end
32
+ end
33
+
34
+ def test_heroku
35
+ # ReplRunner.new('rails console', "heroku run rails console -a test-app-1372231309-0734751").run do |repl|
36
+ # repl.run("'foo' * 5") {|r| assert_match /foofoofoofoofoo/, r }
37
+ # repl.run("'hello ' + 'world'") {|r| assert_match /hello world/, r }
38
+ # end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ # require this file while booting up IRB, and it will never return
2
+
3
+ while true
4
+ sleep 1
5
+ end
@@ -0,0 +1,10 @@
1
+ Bundler.require
2
+
3
+
4
+ require 'repl_runner'
5
+ require 'test/unit'
6
+ require "mocha/setup"
7
+
8
+
9
+ def assert_tests_run
10
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: repl_runner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Richard Schneeman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: ' Programatically drive REPL like interfaces, irb, bash, etc. '
56
+ email:
57
+ - richard@heroku.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - lib/repl_runner.rb
68
+ - lib/repl_runner/config.rb
69
+ - lib/repl_runner/default_config.rb
70
+ - lib/repl_runner/multi_command_parser.rb
71
+ - lib/repl_runner/multi_repl.rb
72
+ - lib/repl_runner/pty_party.rb
73
+ - lib/repl_runner/version.rb
74
+ - repl_runner.gemspec
75
+ - test/config_test.rb
76
+ - test/multi_command_parser_test.rb
77
+ - test/multi_repl_test.rb
78
+ - test/pty_party_test.rb
79
+ - test/repl_runner_test.rb
80
+ - test/require/never-boots.rb
81
+ - test/test_helper.rb
82
+ homepage: https://github.com/schneems/repl_runner
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.0.2
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Run your REPL like interfaces like never before
106
+ test_files:
107
+ - test/config_test.rb
108
+ - test/multi_command_parser_test.rb
109
+ - test/multi_repl_test.rb
110
+ - test/pty_party_test.rb
111
+ - test/repl_runner_test.rb
112
+ - test/require/never-boots.rb
113
+ - test/test_helper.rb