remote_ruby 0.3.0 → 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.
- checksums.yaml +4 -4
- data/.rspec +1 -2
- data/.rubocop.yml +37 -6
- data/CHANGELOG.md +64 -0
- data/LICENSE.txt +1 -1
- data/README.md +348 -81
- data/lib/remote_ruby/adapter_builder.rb +75 -0
- data/lib/remote_ruby/cache_adapter.rb +41 -0
- data/lib/remote_ruby/{connection_adapter/caching_adapter.rb → caching_adapter.rb} +23 -13
- data/lib/remote_ruby/code_templates/compiler/main.rb.erb +17 -29
- data/lib/remote_ruby/compat_io_reader.rb +36 -0
- data/lib/remote_ruby/compat_io_writer.rb +38 -0
- data/lib/remote_ruby/compiler.rb +8 -8
- data/lib/remote_ruby/connection_adapter.rb +16 -16
- data/lib/remote_ruby/execution_context.rb +56 -74
- data/lib/remote_ruby/extensions.rb +14 -0
- data/lib/remote_ruby/parser_factory.rb +29 -0
- data/lib/remote_ruby/plugin.rb +25 -0
- data/lib/remote_ruby/{flavour/rails_flavour.rb → rails_plugin.rb} +7 -5
- data/lib/remote_ruby/remote_context.rb +52 -0
- data/lib/remote_ruby/remote_error.rb +55 -0
- data/lib/remote_ruby/source_extractor.rb +2 -12
- data/lib/remote_ruby/ssh_adapter.rb +128 -0
- data/lib/remote_ruby/stream_prefixer.rb +25 -0
- data/lib/remote_ruby/tee_writer.rb +16 -0
- data/lib/remote_ruby/text_mode_adapter.rb +63 -0
- data/lib/remote_ruby/text_mode_builder.rb +44 -0
- data/lib/remote_ruby/tmp_file_adapter.rb +62 -0
- data/lib/remote_ruby/version.rb +1 -1
- data/lib/remote_ruby.rb +57 -15
- metadata +72 -28
- data/.github/workflows/main.yml +0 -26
- data/.gitignore +0 -17
- data/Gemfile +0 -23
- data/lib/remote_ruby/connection_adapter/cache_adapter.rb +0 -41
- data/lib/remote_ruby/connection_adapter/eval_adapter.rb +0 -96
- data/lib/remote_ruby/connection_adapter/local_stdin_adapter.rb +0 -29
- data/lib/remote_ruby/connection_adapter/ssh_stdin_adapter.rb +0 -34
- data/lib/remote_ruby/connection_adapter/stdin_process_adapter.rb +0 -35
- data/lib/remote_ruby/flavour.rb +0 -27
- data/lib/remote_ruby/runner.rb +0 -55
- data/lib/remote_ruby/stream_cacher.rb +0 -36
- data/lib/remote_ruby/unmarshaler.rb +0 -59
- data/remote_ruby.gemspec +0 -36
@@ -1,17 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'remote_ruby/plugin'
|
4
|
+
|
3
5
|
module RemoteRuby
|
4
|
-
#
|
5
|
-
class
|
6
|
+
# Plugin to load Rails environment
|
7
|
+
class RailsPlugin < ::RemoteRuby::Plugin
|
6
8
|
def initialize(environment: :development)
|
7
9
|
super
|
8
10
|
@environment = environment
|
9
11
|
end
|
10
12
|
|
11
13
|
def code_header
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
<<~RUBY
|
15
|
+
ENV['RAILS_ENV'] = '#{environment}'
|
16
|
+
require './config/environment'
|
15
17
|
RUBY
|
16
18
|
end
|
17
19
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module RemoteRuby
|
6
|
+
# This class is inlined to the remote script and used
|
7
|
+
# to collect errors and other information about the remote run.
|
8
|
+
# It is serialized and sent back to the client.
|
9
|
+
class RemoteContext
|
10
|
+
attr_reader :file_name, :has_error, :error_class, :error_message, :error_backtrace, :locals, :result
|
11
|
+
|
12
|
+
def initialize(filename)
|
13
|
+
@file_name = filename
|
14
|
+
@has_error = false
|
15
|
+
@locals = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def error?
|
19
|
+
@has_error
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle_error(err)
|
23
|
+
@error_class = err.class.to_s
|
24
|
+
@error_message = err.message
|
25
|
+
@error_backtrace = err.backtrace
|
26
|
+
@has_error = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute(&block)
|
30
|
+
@result = begin
|
31
|
+
block.call
|
32
|
+
rescue StandardError => e
|
33
|
+
handle_error(e)
|
34
|
+
ensure
|
35
|
+
locals.each_key do |name|
|
36
|
+
locals[name] = block.binding.local_variable_get(name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dump
|
42
|
+
Marshal.dump(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
def unmarshal(name, data)
|
46
|
+
locals[name] = Marshal.load(Base64.strict_decode64(data)) # rubocop:disable Security/MarshalLoad
|
47
|
+
rescue ArgumentError
|
48
|
+
warn("Warning: could not resolve type for '#{name}' variable")
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
module RemoteRuby
|
5
|
+
# Raised when an error occurs during remote execution
|
6
|
+
# Wraps the original error and provides additional information
|
7
|
+
# about the error and the source code that caused it.
|
8
|
+
# Allows to display the source code around the line that caused the error.
|
9
|
+
class RemoteError < StandardError
|
10
|
+
attr_reader :code_source, :remote_context, :source_path, :stack_trace_regexp
|
11
|
+
|
12
|
+
def initialize(code_source, remote_context, source_path)
|
13
|
+
@code_source = code_source
|
14
|
+
@remote_context = remote_context
|
15
|
+
@source_path = source_path
|
16
|
+
@stack_trace_regexp = /^#{Regexp.escape(remote_context.file_name)}:(?<line_number>\d+):in (?<method_name>.*)$/
|
17
|
+
super(build_message)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def format_source(line_no, context_lines: 3)
|
23
|
+
code_source.lines.each.with_index(1).drop(line_no - context_lines - 1)
|
24
|
+
.take((2 * context_lines) + 1).map do |line, index|
|
25
|
+
if index == line_no
|
26
|
+
"#{index}: >> #{line}"
|
27
|
+
else
|
28
|
+
"#{index}: #{line}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_message
|
34
|
+
res = StringIO.new
|
35
|
+
res.puts "Remote error: #{remote_context.error_class}"
|
36
|
+
res.puts remote_context.error_message
|
37
|
+
|
38
|
+
write_backtrace(res)
|
39
|
+
|
40
|
+
res.string
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_backtrace(res)
|
44
|
+
remote_context.error_backtrace.each do |line|
|
45
|
+
res.puts
|
46
|
+
res.puts "from #{line}"
|
47
|
+
|
48
|
+
next unless (m = stack_trace_regexp.match(line))
|
49
|
+
|
50
|
+
res.puts "(See #{source_path}:#{m[:line_number]}:in #{m[:method_name]}" if source_path
|
51
|
+
res.puts format_source(m[:line_number].to_i)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,24 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'method_source'
|
4
|
-
require '
|
5
|
-
require 'unparser'
|
6
|
-
|
7
|
-
# Opt-in to most recent AST format
|
8
|
-
Parser::Builders::Default.emit_lambda = true
|
9
|
-
Parser::Builders::Default.emit_procarg0 = true
|
10
|
-
Parser::Builders::Default.emit_encoding = true
|
11
|
-
Parser::Builders::Default.emit_index = true
|
12
|
-
Parser::Builders::Default.emit_arg_inside_procarg0 = true
|
13
|
-
Parser::Builders::Default.emit_forward_arg = true
|
14
|
-
Parser::Builders::Default.emit_kwargs = true
|
15
|
-
Parser::Builders::Default.emit_match_pattern = true
|
4
|
+
require 'remote_ruby/parser_factory'
|
16
5
|
|
17
6
|
module RemoteRuby
|
18
7
|
# Receives a block and extracts Ruby code (as a string) with this block's
|
19
8
|
# source
|
20
9
|
class SourceExtractor
|
21
10
|
def extract(&block)
|
11
|
+
RemoteRuby::ParserFactory.require_parser
|
22
12
|
ast = Parser::CurrentRuby.parse(block.source)
|
23
13
|
block_node = find_block(ast)
|
24
14
|
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/ssh'
|
4
|
+
require 'remote_ruby/compat_io_reader'
|
5
|
+
|
6
|
+
module RemoteRuby
|
7
|
+
# An adapter for executing Ruby code on a remote host via SSH
|
8
|
+
class SSHAdapter < ConnectionAdapter
|
9
|
+
UnableToExecuteError = Class.new(StandardError)
|
10
|
+
|
11
|
+
attr_reader :host, :config, :working_dir, :user
|
12
|
+
|
13
|
+
def initialize(host:, working_dir: nil, use_ssh_config_file: true, **params)
|
14
|
+
super
|
15
|
+
@host = host
|
16
|
+
@working_dir = working_dir
|
17
|
+
@config = Net::SSH.configuration_for(@host, use_ssh_config_file)
|
18
|
+
|
19
|
+
@config = @config.merge(params)
|
20
|
+
@user = @config[:user]
|
21
|
+
end
|
22
|
+
|
23
|
+
def open(code, stdin, stdout, stderr)
|
24
|
+
ret = nil
|
25
|
+
Net::SSH.start(host, nil, config) do |ssh|
|
26
|
+
with_temp_file(code, ssh) do |fname|
|
27
|
+
res = run_code(ssh, fname, stdin, stdout, stderr)
|
28
|
+
raise "Process exited with code #{status}" unless res.zero?
|
29
|
+
|
30
|
+
ret = get_result(ssh, fname)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
ret
|
34
|
+
end
|
35
|
+
|
36
|
+
def connection_name
|
37
|
+
"#{user}@#{host}:#{working_dir || '~'}> "
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def handle_stdin(chan, stdin)
|
43
|
+
return if stdin.nil?
|
44
|
+
|
45
|
+
if stdin.is_a?(StringIO)
|
46
|
+
chan.send_data(stdin.string)
|
47
|
+
chan.eof!
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
stdin = RemoteRuby::CompatIOReader.new(stdin)
|
52
|
+
|
53
|
+
chan.connection.listen_to(stdin.readable) do |io|
|
54
|
+
data = io.read_nonblock(4096)
|
55
|
+
chan.send_data(data)
|
56
|
+
rescue EOFError
|
57
|
+
chan.connection.stop_listening_to(stdin.readable)
|
58
|
+
chan.eof!
|
59
|
+
end
|
60
|
+
|
61
|
+
chan.on_close do
|
62
|
+
chan.connection.stop_listening_to(stdin.readable)
|
63
|
+
stdin.join
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_stdout(chan, stdout)
|
68
|
+
return if stdout.nil?
|
69
|
+
|
70
|
+
chan.on_data do |_, data|
|
71
|
+
stdout.write(data)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def handle_stderr(chan, stderr)
|
76
|
+
return if stderr.nil?
|
77
|
+
|
78
|
+
chan.on_extended_data do |_, _, data|
|
79
|
+
stderr.write(data)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_exit_code(chan)
|
84
|
+
chan.on_request('exit-status') do |_, data|
|
85
|
+
yield data.read_long
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def run_remote_process(ssh, cmd, stdin, stdout, stderr)
|
90
|
+
res = nil
|
91
|
+
|
92
|
+
ssh.open_channel do |channel|
|
93
|
+
channel.exec(cmd) do |ch, success|
|
94
|
+
raise UnableToExecuteError unless success
|
95
|
+
|
96
|
+
handle_stdin(ch, stdin)
|
97
|
+
handle_stdout(ch, stdout)
|
98
|
+
handle_stderr(ch, stderr)
|
99
|
+
handle_exit_code(ch) do |code|
|
100
|
+
res = code
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end.wait
|
104
|
+
|
105
|
+
res
|
106
|
+
end
|
107
|
+
|
108
|
+
def run_code(ssh, fname, stdin, stdout, stderr)
|
109
|
+
cmd = "cd '#{working_dir}' && ruby \"#{fname}\""
|
110
|
+
run_remote_process(ssh, cmd, stdin, stdout, stderr)
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_result(ssh, fname)
|
114
|
+
ssh.exec!("cat \"#{fname}\"")
|
115
|
+
end
|
116
|
+
|
117
|
+
def with_temp_file(code, ssh)
|
118
|
+
out = StringIO.new
|
119
|
+
cmd = 'f=$(mktemp --tmpdir remote_ruby.XXXXXX) && cat > $f && echo $f'
|
120
|
+
run_remote_process(ssh, cmd, StringIO.new(code), out, nil)
|
121
|
+
fname = out.string.strip
|
122
|
+
|
123
|
+
yield fname
|
124
|
+
ensure
|
125
|
+
ssh.exec!("rm \"#{fname}\"")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RemoteRuby
|
4
|
+
# Decorates the source stream prepending a prefix to each line
|
5
|
+
# read from the source
|
6
|
+
class StreamPrefixer
|
7
|
+
attr_reader :stream, :prefix
|
8
|
+
|
9
|
+
def initialize(stream, prefix)
|
10
|
+
@stream = stream
|
11
|
+
@prefix = prefix
|
12
|
+
@prefix_needed = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(data)
|
16
|
+
res = 0
|
17
|
+
data.each_line do |line|
|
18
|
+
res += stream.write(prefix) if @prefix_needed
|
19
|
+
@prefix_needed = line.end_with?("\n")
|
20
|
+
res += stream.write(line)
|
21
|
+
end
|
22
|
+
res
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RemoteRuby
|
4
|
+
# Implements a tee writer that writes to multiple writers
|
5
|
+
class TeeWriter
|
6
|
+
attr_reader :writers
|
7
|
+
|
8
|
+
def initialize(*writers)
|
9
|
+
@writers = writers
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(*args)
|
13
|
+
writers.map { |writer| writer.write(*args) }.min
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'remote_ruby/stream_prefixer'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
module RemoteRuby
|
7
|
+
# Decorates a connection adapter.
|
8
|
+
# Reads the output streams line-by-line and prefixes them according to the settings.
|
9
|
+
class TextModeAdapter < ConnectionAdapter
|
10
|
+
DEFAULT_SETTINGS = {
|
11
|
+
stdout_mode: { color: :green, mode: :italic },
|
12
|
+
stderr_mode: { color: :red, mode: :italic },
|
13
|
+
cache_mode: { color: :blue, mode: :bold },
|
14
|
+
cache_prefix: '[C] '
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_reader :adapter, :stdout_prefix, :stderr_prefix, :cache_prefix, :stdout_mode, :stderr_mode,
|
18
|
+
:cache_mode
|
19
|
+
|
20
|
+
def initialize(adapter, **params)
|
21
|
+
super()
|
22
|
+
@adapter = adapter
|
23
|
+
@stdout_prefix = params[:stdout_prefix]
|
24
|
+
@stderr_prefix = params[:stderr_prefix]
|
25
|
+
@cache_prefix = params[:cache_prefix]
|
26
|
+
@stdout_mode = params[:stdout_mode]
|
27
|
+
@stderr_mode = params[:stderr_mode]
|
28
|
+
@cache_mode = params[:cache_mode]
|
29
|
+
end
|
30
|
+
|
31
|
+
def open(code, stdin, stdout, stderr)
|
32
|
+
stdout_pref = "#{cache_prefix_string}#{stdout_prefix_string}"
|
33
|
+
stderr_pref = "#{cache_prefix_string}#{stderr_prefix_string}"
|
34
|
+
stdout = StreamPrefixer.new(stdout, stdout_pref) unless stdout_prefix_string.nil?
|
35
|
+
stderr = StreamPrefixer.new(stderr, stderr_pref) unless stderr_prefix_string.nil?
|
36
|
+
|
37
|
+
adapter.open(code, stdin, stdout, stderr)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def stdout_prefix_string
|
43
|
+
return nil if stdout_prefix.nil? || stdout_prefix.empty?
|
44
|
+
return stdout_prefix if stdout_mode.nil?
|
45
|
+
|
46
|
+
stdout_prefix.colorize(stdout_mode)
|
47
|
+
end
|
48
|
+
|
49
|
+
def stderr_prefix_string
|
50
|
+
return nil if stderr_prefix.nil? || stderr_prefix.empty?
|
51
|
+
return stderr_prefix if stderr_mode.nil?
|
52
|
+
|
53
|
+
stderr_prefix.colorize(stderr_mode)
|
54
|
+
end
|
55
|
+
|
56
|
+
def cache_prefix_string
|
57
|
+
return nil if cache_prefix.nil? || cache_prefix.empty?
|
58
|
+
return cache_prefix if cache_mode.nil?
|
59
|
+
|
60
|
+
cache_prefix.colorize(cache_mode)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RemoteRuby
|
4
|
+
# Wraps the connection adapter in a text mode adapter if text mode is enabled.
|
5
|
+
class TextModeBuilder
|
6
|
+
attr_reader :out_tty, :err_tty, :text_mode
|
7
|
+
|
8
|
+
def initialize(params:, out_tty: true, err_tty: true)
|
9
|
+
@out_tty = out_tty
|
10
|
+
@err_tty = err_tty
|
11
|
+
@text_mode = params.delete(:text_mode) || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def build(adapter)
|
15
|
+
return adapter unless text_mode
|
16
|
+
|
17
|
+
cache_mode = adapter.is_a? CacheAdapter
|
18
|
+
|
19
|
+
tm_params = text_mode_params(adapter, cache_mode, out_tty, err_tty)
|
20
|
+
|
21
|
+
return adapter unless tm_params[:stdout_prefix] || tm_params[:stderr_prefix]
|
22
|
+
|
23
|
+
::RemoteRuby::TextModeAdapter.new(adapter, **tm_params)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def text_mode_params(adapter, cache_mode, out_tty, err_tty)
|
29
|
+
tm_params = ::RemoteRuby::TextModeAdapter::DEFAULT_SETTINGS.merge(
|
30
|
+
stdout_prefix: adapter.connection_name,
|
31
|
+
stderr_prefix: adapter.connection_name
|
32
|
+
)
|
33
|
+
|
34
|
+
tm_params = tm_params.merge(text_mode) if text_mode.is_a? Hash
|
35
|
+
|
36
|
+
disable_unless_tty = tm_params.delete(:disable_unless_tty) { |_| true }
|
37
|
+
|
38
|
+
tm_params[:stdout_prefix] = nil if disable_unless_tty && !out_tty
|
39
|
+
tm_params[:stderr_prefix] = nil if disable_unless_tty && !err_tty
|
40
|
+
tm_params[:cache_prefix] = nil unless cache_mode
|
41
|
+
tm_params
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
require 'remote_ruby/compat_io_reader'
|
5
|
+
require 'remote_ruby/compat_io_writer'
|
6
|
+
|
7
|
+
module RemoteRuby
|
8
|
+
# An adapter to expecute Ruby code on the local machine
|
9
|
+
# inside a temporary file
|
10
|
+
class TmpFileAdapter < ::RemoteRuby::ConnectionAdapter
|
11
|
+
attr_reader :working_dir
|
12
|
+
|
13
|
+
def initialize(working_dir: Dir.pwd)
|
14
|
+
super
|
15
|
+
@working_dir = working_dir
|
16
|
+
end
|
17
|
+
|
18
|
+
def open(code, stdin, stdout, stderr)
|
19
|
+
result = nil
|
20
|
+
|
21
|
+
stdin = RemoteRuby::CompatIOReader.new(stdin)
|
22
|
+
stdout = RemoteRuby::CompatIOWriter.new(stdout)
|
23
|
+
stderr = RemoteRuby::CompatIOWriter.new(stderr)
|
24
|
+
|
25
|
+
with_temp_file(code) do |filename|
|
26
|
+
pid = Process.spawn(
|
27
|
+
command(filename),
|
28
|
+
in: stdin.readable,
|
29
|
+
out: stdout.writeable,
|
30
|
+
err: stderr.writeable
|
31
|
+
)
|
32
|
+
|
33
|
+
_, status = Process.wait2(pid)
|
34
|
+
raise "Process exited with code #{status}" unless status.success?
|
35
|
+
|
36
|
+
[stdin, stdout, stderr].each(&:join)
|
37
|
+
|
38
|
+
result = File.binread(filename)
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection_name
|
44
|
+
"#{ENV.fetch('USER', nil)}@localhost:#{working_dir}> "
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def with_temp_file(code)
|
50
|
+
f = Tempfile.create('remote_ruby')
|
51
|
+
f.write(code)
|
52
|
+
f.close
|
53
|
+
yield f.path
|
54
|
+
ensure
|
55
|
+
File.unlink(f.path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def command(code_path)
|
59
|
+
"cd \"#{working_dir}\" && ruby \"#{code_path}\""
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/remote_ruby/version.rb
CHANGED
data/lib/remote_ruby.rb
CHANGED
@@ -1,27 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'remote_ruby/version'
|
4
|
-
require 'remote_ruby/
|
4
|
+
require 'remote_ruby/rails_plugin'
|
5
|
+
require 'remote_ruby/extensions'
|
5
6
|
|
6
7
|
# Namespace module for other RemoteRuby classes. Also contains methods, which
|
7
8
|
# are included in the global scope
|
8
9
|
module RemoteRuby
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
execution_context.execute(locals, &block)
|
13
|
-
end
|
10
|
+
DEFAULT_CONFIG_DIR_NAME = '.remote_ruby'
|
11
|
+
DEFAULT_CACHE_DIR_NAME = 'cache'
|
12
|
+
DEFAULT_CODE_DIR_NAME = 'code'
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
class << self
|
15
|
+
attr_reader :plugins, :ignored_types
|
16
|
+
attr_accessor :cache_dir, :code_dir, :suppress_parser_warnings
|
17
|
+
|
18
|
+
def root(*params)
|
19
|
+
root_dir = ::Gem::Specification.find_by_name('remote_ruby').gem_dir
|
20
|
+
File.join(root_dir, *params)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ensure_cache_dir
|
24
|
+
FileUtils.mkdir_p(cache_dir)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ensure_code_dir
|
28
|
+
FileUtils.mkdir_p(code_dir)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lib_path(*params)
|
32
|
+
File.join(root, 'lib', *params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear_cache
|
36
|
+
FileUtils.rm_rf(cache_dir)
|
37
|
+
end
|
19
38
|
|
20
|
-
|
21
|
-
|
39
|
+
def clear_code
|
40
|
+
FileUtils.rm_rf(code_dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_plugin(name, klass)
|
44
|
+
@plugins ||= {}
|
45
|
+
@plugins[name] = klass
|
46
|
+
end
|
47
|
+
|
48
|
+
def ignore_types(*types)
|
49
|
+
@ignored_types ||= []
|
50
|
+
@ignored_types.concat(types)
|
51
|
+
end
|
52
|
+
|
53
|
+
def configure
|
54
|
+
yield self
|
55
|
+
end
|
22
56
|
end
|
23
57
|
end
|
24
58
|
|
25
|
-
|
26
|
-
|
27
|
-
|
59
|
+
RemoteRuby.configure do |config|
|
60
|
+
config_dir = File.join(Dir.pwd, RemoteRuby::DEFAULT_CONFIG_DIR_NAME)
|
61
|
+
config.cache_dir = File.join(config_dir, RemoteRuby::DEFAULT_CACHE_DIR_NAME)
|
62
|
+
config.code_dir = File.join(config_dir, RemoteRuby::DEFAULT_CODE_DIR_NAME)
|
63
|
+
|
64
|
+
config.ignore_types RemoteRuby::ExecutionContext
|
65
|
+
|
66
|
+
config.suppress_parser_warnings = false
|
67
|
+
|
68
|
+
config.register_plugin(:rails, RemoteRuby::RailsPlugin)
|
69
|
+
end
|