replr 0.3.0 → 0.9.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3baafd9472b824734be5f808a2618e87120ba9adb87ef3a5fa6e09685d0a12a
4
- data.tar.gz: d54f510d068f819de8aae695e76abe3fe7f45642cc7f5574d44ccf1d39509856
3
+ metadata.gz: 97e6344e0f3d944df4c3ee74081b41fa096b51a5ac7c510b5b870a55e3d41562
4
+ data.tar.gz: 7476054ed59966a2a910b78b85b8274cb8246df9854de7a9459e13febe5cd302
5
5
  SHA512:
6
- metadata.gz: df87112be9e15dcb0a9754d6baa12c520567a335c78cd582201ed76aaa190dd5c0e49faba2f4c18e3d7a7dff43cf2e9fca0fe79fc63f0fc9561e1849e207e8c6
7
- data.tar.gz: '03887359a7e34df5f4ec3c8583cc93cb0bb7663941b1db3a7643923259f0cde76a4ea14e24ed404e579c54437c8b91bba25162f134189ba60ea8b45ac540bb67'
6
+ metadata.gz: 4382a006858f642290cf762b9f433fd9e74a60cb4faa07a96d53a55dbed7dc5212411f9c82508a1a83952699becfb42461c3dc2d96dca84ccfd6576fc345b1ff
7
+ data.tar.gz: 3924726d63a00d8e6da33d90cfe3d9067b7a0f0da5a7a1d190633fd0e615df82bba15a25ef9ddf5e6b5c57723a6bd7733e81182a7078146a0afa6959e1113b25
data/Gemfile CHANGED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/bin/replr CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require_relative '../lib/replr'
4
- Replr::REPLMaker.new.start
4
+ Replr::Boot.new.start
@@ -1,2 +1,2 @@
1
1
  require_relative 'replr/base'
2
- require_relative 'replr/repl_maker'
2
+ require_relative 'replr/boot'
@@ -1,13 +1,11 @@
1
- require_relative '../replr'
2
-
3
1
  # :nodoc:
4
2
  module Replr
5
3
  # Processes command-line arguments
6
4
  class ArgumentProcessor
7
- COMMANDS = ['prune']
8
- STACKS = ['ruby']
5
+ COMMANDS = ['prune'].freeze
6
+ STACKS = ['ruby', 'python', 'node'].freeze
9
7
 
10
- attr_reader :arguments
8
+ attr_reader :arguments, :stack, :command, :libraries
11
9
 
12
10
  def initialize
13
11
  @arguments = ARGV.map { |argument| argument.downcase.strip }
@@ -23,27 +21,45 @@ module Replr
23
21
  end
24
22
 
25
23
  def check_arguments!
26
- valid_stack = STACKS.detect do |stack|
27
- arguments[0].match(/^#{stack}:?.*?/)
28
- end
29
- valid_command = COMMANDS.detect do |command|
30
- arguments[0] == command
31
- end
24
+ detect_stack
25
+ detect_command
26
+ detect_libraries
32
27
 
33
- unless valid_stack or valid_command
28
+ unless stack || command
34
29
  puts_error "First argument must be either be a command:
35
- \t#{COMMANDS.join(' ')}
36
- or one of a supported stack:
30
+ \t#{COMMANDS.join(' ')}\nor one of a supported stack:
37
31
  \t#{STACKS.join(' ')}"
38
32
  puts_usage
39
33
  exit
40
34
  end
41
35
  end
42
36
 
37
+ def detect_stack
38
+ detected_stack = STACKS.detect do |stack|
39
+ arguments[0].match(/^#{stack}:?.*?/)
40
+ end
41
+
42
+ # return the full stack string (that includes version)
43
+ # instead of just the stack name
44
+ if detected_stack
45
+ @stack = arguments[0]
46
+ end
47
+ end
48
+
49
+ def detect_command
50
+ @command = COMMANDS.detect do |command|
51
+ arguments[0] == command
52
+ end
53
+ end
54
+
55
+ def detect_libraries
56
+ @libraries = arguments[1..-1].sort!
57
+ end
58
+
43
59
  def puts_usage
44
60
  puts_error "\nUsage: replr <stack> <libraries...>\n\n"
45
61
  puts_error "A single line REPL for your favorite languages & libraries\n\n"
46
- puts_error "\t<stack> is now only 'ruby'"
62
+ puts_error "\t<stack> is now one of #{STACKS.join(', ')}"
47
63
  puts_error "\t<libraries...> is a space separated list of libraries for the stack\n\n"
48
64
  puts_error "More commands:\n\n\treplr prune to delete all replr docker images (this saves space)"
49
65
  end
@@ -0,0 +1,48 @@
1
+ require_relative 'argument_processor'
2
+ require_relative 'process_runner'
3
+ require_relative 'stack/repl_maker'
4
+
5
+ require 'tmpdir'
6
+
7
+ # :nodoc:
8
+ module Replr
9
+ # Starts up REPL creation & wires up rest of the pieces
10
+ class Boot
11
+ attr_reader :argument_processor, :process_runner
12
+
13
+ def start
14
+ @argument_processor = Replr::ArgumentProcessor.new
15
+ @process_runner = Replr::ProcessRunner.new
16
+
17
+ check_docker!
18
+ execute_processsed_arguments!
19
+ end
20
+
21
+ private
22
+
23
+ def check_docker!
24
+ unless process_runner.process_exists?('docker')
25
+ puts_error 'Needs docker installed & in path to work.'
26
+ exit
27
+ end
28
+ end
29
+
30
+ def execute_processsed_arguments!
31
+ if argument_processor.command == 'prune'
32
+ execute_prune_command
33
+ elsif argument_processor.stack
34
+ Replr::Stack::REPLMaker.load(stack: argument_processor.stack,
35
+ libraries: argument_processor.libraries)
36
+ end
37
+ end
38
+
39
+ def execute_prune_command
40
+ prune_command = %q(docker images -a | grep "replr/" | awk '{print $3}' | xargs docker rmi)
41
+ process_runner.execute_command(prune_command)
42
+ end
43
+
44
+ def puts_error(string)
45
+ STDERR.puts(string)
46
+ end
47
+ end
48
+ end
@@ -1,4 +1,6 @@
1
- require_relative '../replr'
1
+ require 'open3'
2
+ require 'pty'
3
+ require 'expect'
2
4
 
3
5
  module Replr
4
6
  # Executes and manages processes
@@ -44,5 +46,28 @@ module Replr
44
46
  STDERR.puts outerror
45
47
  end
46
48
  end
49
+
50
+ def execute_repl_with_input(command:, inputs:, prompt_line:, expected_output:, debug: false)
51
+ outputs = []
52
+ inputs << ''
53
+
54
+ PTY.spawn(command) do |read, write, _pid|
55
+ read.expect(prompt_line)
56
+ inputs.each do |cmd|
57
+ begin
58
+ write.puts cmd
59
+ read.flush
60
+ read.expect(/(.*?)\r\n(.*)>/m) do |output|
61
+ puts "DEBUG: OUTPUT: #{output.inspect}" if debug
62
+ outputs << output[2].match(expected_output) if output
63
+ end
64
+ rescue Errno::EIO
65
+ next
66
+ end
67
+ end
68
+ end
69
+
70
+ outputs.any? { |output| output.is_a? MatchData }
71
+ end
47
72
  end
48
73
  end
@@ -0,0 +1,6 @@
1
+ FROM node:%%VERSION%%alpine
2
+ ADD package.json /app/
3
+ RUN cd /app ; npm install
4
+ ADD . /app
5
+ WORKDIR /app
6
+ CMD ["npm", "run", "start"]
@@ -0,0 +1,59 @@
1
+ require_relative '../../../replr'
2
+ require_relative '../../process_runner'
3
+
4
+ require 'json'
5
+
6
+ # :nodoc:
7
+ module Replr
8
+ module Stack
9
+ module Node
10
+ # Creates a Ruby REPL using docker for a stack and libraries combo
11
+ class REPLMaker < Replr::Stack::REPLMaker
12
+ private
13
+
14
+ def set_library_file_name
15
+ @library_file_name = 'package.json'
16
+ end
17
+
18
+ # It's optional to set a bootstrap file
19
+ def set_bootstrap_file_name
20
+ @bootstrap_file_name = 'replr-bootstrap.js'
21
+ end
22
+
23
+ def set_template_dir
24
+ @template_dir = __dir__
25
+ end
26
+
27
+ def set_filter_lines_for_install
28
+ @filter_matching_lines_for_install = [/packages/i, /download/i]
29
+ @filter_not_matching_lines_for_install = []
30
+ end
31
+
32
+ def library_file_with(libraries)
33
+ package_file = {
34
+ name: "@#{docker_image_tag}",
35
+ version: '1.0.0',
36
+ description: 'Replr for Node',
37
+ author: 'Vishnu Gopal <vishnugopal@users.noreply.github.com>',
38
+ repository: 'https://github.com/vishnugopal/replr',
39
+ license: 'MIT',
40
+ scripts: {
41
+ start: 'node replr-bootstrap.js'
42
+ }
43
+ }
44
+ dependencies = {}
45
+ libraries.each do |library|
46
+ if library.include?(':')
47
+ library, version = library.split(':')
48
+ dependencies[library] = version
49
+ else
50
+ dependencies[library] = '*'
51
+ end
52
+ end
53
+ package_file[:dependencies] = dependencies
54
+ package_file.to_json
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1 @@
1
+ require("repl").start();
@@ -0,0 +1,6 @@
1
+ FROM python:%%VERSION%%alpine
2
+ ADD requirements.txt /app/
3
+ RUN cd /app ; pip install -r requirements.txt
4
+ ADD . /app
5
+ WORKDIR /app
6
+ CMD ["python"]
@@ -0,0 +1,43 @@
1
+ require_relative '../../../replr'
2
+ require_relative '../../process_runner'
3
+
4
+ require_relative '../../../replr'
5
+ require_relative '../../process_runner'
6
+
7
+ # :nodoc:
8
+ module Replr
9
+ module Stack
10
+ module Python
11
+ # Creates a Python REPL using docker for a stack and libraries combo
12
+ class REPLMaker < Replr::Stack::REPLMaker
13
+ private
14
+
15
+ def set_library_file_name
16
+ @library_file_name = 'requirements.txt'
17
+ end
18
+
19
+ def set_template_dir
20
+ @template_dir = __dir__
21
+ end
22
+
23
+ def set_filter_lines_for_install
24
+ @filter_matching_lines_for_install = [/pip/i]
25
+ @filter_not_matching_lines_for_install = [/\.cache/i]
26
+ end
27
+
28
+ def library_file_with(libraries)
29
+ requirements = ''
30
+ libraries.each do |library|
31
+ if library.include?(':')
32
+ library, version = library.split(':')
33
+ requirements << "#{library}==#{version}\n"
34
+ else
35
+ requirements << "#{library}\n"
36
+ end
37
+ end
38
+ requirements
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,121 @@
1
+ require_relative '../process_runner'
2
+
3
+ # :nodoc:
4
+ module Replr
5
+ module Stack
6
+ # Abstract class to do many things common when creating a REPL
7
+ # across stacks
8
+ class REPLMaker
9
+ attr_reader :process_runner
10
+ attr_reader :stack, :libraries, :work_dir
11
+ attr_reader :library_file_name, :bootstrap_file_name, :template_dir,
12
+ :filter_matching_lines_for_install,
13
+ :filter_not_matching_lines_for_install
14
+
15
+ def self.load(stack:, libraries:)
16
+ stack_name, _stack_version = stack.split(':')
17
+ require_relative "#{stack_name}/repl_maker"
18
+
19
+ repl_maker_class_name = "Replr::Stack::#{stack_name.capitalize}::REPLMaker"
20
+ repl_maker = Object.const_get(repl_maker_class_name).new(
21
+ stack: stack,
22
+ libraries: libraries
23
+ )
24
+ repl_maker.create
25
+ end
26
+
27
+ def initialize(stack:, libraries:)
28
+ @process_runner = Replr::ProcessRunner.new
29
+
30
+ @stack = stack
31
+ @libraries = libraries
32
+ @work_dir = Dir.mktmpdir
33
+ end
34
+
35
+ def create
36
+ set_template_dir
37
+ set_library_file_name
38
+ set_bootstrap_file_name
39
+ set_filter_lines_for_install
40
+
41
+ copy_library_file
42
+ copy_initialization_files
43
+ initialize_docker_repl
44
+ end
45
+
46
+ private
47
+
48
+ def set_template_dir
49
+ raise NotImplementedError, 'This needs to be implemented by a subclass'
50
+ end
51
+
52
+ def set_library_file_name
53
+ raise NotImplementedError, 'This needs to be implemented by a subclass'
54
+ end
55
+
56
+ # It's optional to set a bootstrap file
57
+ def set_bootstrap_file_name; end
58
+
59
+ def set_filter_lines_for_install
60
+ raise NotImplementedError, 'This needs to be implemented by a subclass'
61
+ end
62
+
63
+ def library_file_with(libraries)
64
+ raise NotImplementedError, 'This needs to be implemented by a subclass'
65
+ end
66
+
67
+ def copy_library_file
68
+ Dir.chdir(work_dir) do
69
+ File.open(library_file_name, 'w') do |f|
70
+ f.write(library_file_with(libraries))
71
+ end
72
+ end
73
+ end
74
+
75
+ def copy_initialization_files
76
+ create_docker_file
77
+ if bootstrap_file_name
78
+ bootstrap_file = File.join(template_dir, bootstrap_file_name)
79
+ FileUtils.cp(bootstrap_file, work_dir)
80
+ end
81
+ end
82
+
83
+ def create_docker_file
84
+ _stack, version = stack.split(':')
85
+ docker_file_template = File.join(template_dir, "Dockerfile.template")
86
+ docker_file_contents = File.read(docker_file_template)
87
+ docker_file_contents.gsub!('%%VERSION%%', version ? "#{version}-" : '')
88
+ File.open(File.join(work_dir, 'Dockerfile'), 'w') do |file|
89
+ file.puts docker_file_contents
90
+ end
91
+ end
92
+
93
+ def initialize_docker_repl
94
+ Dir.chdir(work_dir) do
95
+ build_command = "docker build -t #{docker_image_tag} ."
96
+ run_command = "docker run --rm -it #{docker_image_tag}"
97
+ matching = Regexp.union([/upgrading/i, /installing/i] +
98
+ filter_matching_lines_for_install)
99
+ not_matching = Regexp.union([/step/i] + filter_not_matching_lines_for_install)
100
+ process_runner.execute_filtered_process(build_command, matching,
101
+ not_matching) do |stderr, process_thread|
102
+ process_runner.execute_command_if_not_stderr(run_command, stderr, process_thread)
103
+ end
104
+ end
105
+ end
106
+
107
+ def docker_image_tag
108
+ normalized_library_string = libraries.map do |library|
109
+ library.gsub(':', '-v')
110
+ end.join('-')
111
+ normalized_stack_string = stack.gsub(':', '-v')
112
+
113
+ if normalized_library_string.empty?
114
+ "replr/#{normalized_stack_string}"
115
+ else
116
+ "replr/#{normalized_stack_string}-#{normalized_library_string}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,45 @@
1
+ require_relative '../../../replr'
2
+ require_relative '../../process_runner'
3
+
4
+ # :nodoc:
5
+ module Replr
6
+ module Stack
7
+ module Ruby
8
+ # Creates a Ruby REPL using docker for a stack and libraries combo
9
+ class REPLMaker < Replr::Stack::REPLMaker
10
+ private
11
+
12
+ def set_library_file_name
13
+ @library_file_name = 'Gemfile'
14
+ end
15
+
16
+ # It's optional to set a bootstrap file
17
+ def set_bootstrap_file_name
18
+ @bootstrap_file_name = 'replr-bootstrap.rb'
19
+ end
20
+
21
+ def set_template_dir
22
+ @template_dir = __dir__
23
+ end
24
+
25
+ def set_filter_lines_for_install
26
+ @filter_matching_lines_for_install = [/gem/i]
27
+ @filter_not_matching_lines_for_install = []
28
+ end
29
+
30
+ def library_file_with(libraries)
31
+ gemfile = "source 'https://rubygems.org/'\n"
32
+ libraries.each do |library|
33
+ if library.include?(':')
34
+ library, version = library.split(':')
35
+ gemfile << "gem '#{library}', '#{version}'\n"
36
+ else
37
+ gemfile << "gem '#{library}'\n"
38
+ end
39
+ end
40
+ gemfile
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -2,4 +2,3 @@ require 'irb'
2
2
  Bundler.require
3
3
  ARGV.clear
4
4
  IRB.start
5
-
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: replr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vishnu Gopal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-27 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2018-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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'
13
41
  description:
14
42
  email: vg@vishnugopal.com
15
43
  executables:
@@ -20,12 +48,19 @@ files:
20
48
  - Gemfile
21
49
  - bin/replr
22
50
  - lib/replr.rb
23
- - lib/replr/Dockerfile.template
24
51
  - lib/replr/argument_processor.rb
25
52
  - lib/replr/base.rb
53
+ - lib/replr/boot.rb
26
54
  - lib/replr/process_runner.rb
27
- - lib/replr/repl_maker.rb
28
- - lib/replr/replr-bootstrap.rb
55
+ - lib/replr/stack/node/Dockerfile.template
56
+ - lib/replr/stack/node/repl_maker.rb
57
+ - lib/replr/stack/node/replr-bootstrap.js
58
+ - lib/replr/stack/python/Dockerfile.template
59
+ - lib/replr/stack/python/repl_maker.rb
60
+ - lib/replr/stack/repl_maker.rb
61
+ - lib/replr/stack/ruby/Dockerfile.template
62
+ - lib/replr/stack/ruby/repl_maker.rb
63
+ - lib/replr/stack/ruby/replr-bootstrap.rb
29
64
  homepage: https://github.com/vishnugopal/replr
30
65
  licenses:
31
66
  - MIT
@@ -1,114 +0,0 @@
1
- require_relative '../replr'
2
- require_relative 'argument_processor'
3
- require_relative 'process_runner'
4
-
5
- require 'tmpdir'
6
- require 'open3'
7
-
8
- # :nodoc:
9
- module Replr
10
- # Creates a REPL using template Dockerfiles & libraries
11
- class REPLMaker
12
- attr_reader :argument_processor, :process_runner
13
- attr_reader :stack, :libraries, :workdir
14
-
15
- def start
16
- @argument_processor = Replr::ArgumentProcessor.new
17
- @process_runner = Replr::ProcessRunner.new
18
-
19
- @stack = argument_processor.arguments[0]
20
- @libraries = argument_processor.arguments[1..-1].sort!
21
- @workdir = Dir.mktmpdir
22
-
23
- check_docker!
24
- execute_processsed_arguments!
25
- end
26
-
27
- private
28
-
29
- def check_docker!
30
- unless process_runner.process_exists?('docker')
31
- puts_error 'Needs docker installed & in path to work.'
32
- exit
33
- end
34
- end
35
-
36
- def execute_processsed_arguments!
37
- if argument_processor.arguments[0] == 'prune'
38
- execute_prune_command
39
- else
40
- copy_library_file
41
- copy_initialization_files
42
- initialize_docker_repl
43
- end
44
- end
45
-
46
- def execute_prune_command
47
- prune_command = %q(docker images -a | grep "replr/" | awk '{print $3}' | xargs docker rmi)
48
- process_runner.execute_command(prune_command)
49
- end
50
-
51
- def puts_error(string)
52
- STDERR.puts(string)
53
- end
54
-
55
- def copy_library_file
56
- Dir.chdir(workdir) do
57
- File.open('Gemfile', 'w') do |f|
58
- f.write(library_file_with(libraries))
59
- end
60
- end
61
- end
62
-
63
- def copy_initialization_files
64
- create_docker_file
65
- bootstrap_file = "#{__dir__}/replr-bootstrap.rb"
66
- FileUtils.cp(bootstrap_file, workdir)
67
- end
68
-
69
- def create_docker_file
70
- _stack, version = stack.split(':')
71
- docker_file_template = "#{__dir__}/Dockerfile.template"
72
- docker_file_contents = File.read(docker_file_template)
73
- docker_file_contents.gsub!('%%VERSION%%', version ? "#{version}-" : '')
74
- File.open(File.join(workdir, 'Dockerfile'), 'w') do |file|
75
- file.puts docker_file_contents
76
- end
77
- end
78
-
79
- def library_file_with(libraries)
80
- gemfile = "source 'https://rubygems.org/'\n"
81
- libraries.each do |library|
82
- if library.include?(':')
83
- library, version = library.split(':')
84
- gemfile << "gem '#{library}', '#{version}'\n"
85
- else
86
- gemfile << "gem '#{library}'\n"
87
- end
88
- end
89
- gemfile
90
- end
91
-
92
- def initialize_docker_repl
93
- Dir.chdir(workdir) do
94
- build_command = "docker build -t #{docker_image_tag} ."
95
- run_command = "docker run --rm -it #{docker_image_tag}"
96
- matching = Regexp.union([/upgrading/i, /installing/i, /gem/i])
97
- not_matching = Regexp.union([/step/i])
98
- process_runner.execute_filtered_process(build_command, matching,
99
- not_matching) do |stderr, process_thread|
100
- process_runner.execute_command_if_not_stderr(run_command, stderr, process_thread)
101
- end
102
- end
103
- end
104
-
105
- def docker_image_tag
106
- normalized_library_string = libraries.map do |library|
107
- library.gsub(':', '-v')
108
- end.join('-')
109
- normalized_stack_string = stack.gsub(':', '-v')
110
-
111
- "replr/#{normalized_stack_string}-#{normalized_library_string}"
112
- end
113
- end
114
- end