• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Nu-hin / remote_ruby / 13442453582

20 Feb 2025 06:45PM UTC coverage: 94.372% (+0.1%) from 94.237%
13442453582

push

github

Nu-hin
Simplify text mode adapter

5 of 9 new or added lines in 3 files covered. (55.56%)

2 existing lines in 2 files now uncovered.

1157 of 1226 relevant lines covered (94.37%)

85.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.36
/lib/remote_ruby/execution_context.rb
1
# frozen_string_literal: true
2

3
require 'digest'
16✔
4
require 'fileutils'
16✔
5

6
require 'remote_ruby/compiler'
16✔
7
require 'remote_ruby/connection_adapter'
16✔
8
require 'remote_ruby/locals_extractor'
16✔
9
require 'remote_ruby/source_extractor'
16✔
10
require 'remote_ruby/flavour'
16✔
11
require 'remote_ruby/runner'
16✔
12

13
module RemoteRuby
16✔
14
  # This class is responsible for executing blocks on the remote host with the
15
  # specified adapters. This is the entrypoint to RemoteRuby logic.
16
  class ExecutionContext
16✔
17
    # rubocop:disable Metrics/CyclomaticComplexity
18
    # rubocop:disable Metrics/PerceivedComplexity
19
    def initialize(**params)
16✔
20
      add_flavours(params)
176✔
21
      @use_cache = params.delete(:use_cache)         || false
176✔
22
      @save_cache = params.delete(:save_cache)       || false
176✔
23
      @cache_dir = params.delete(:cache_dir)         || File.join(Dir.pwd, 'cache')
176✔
24
      @in_stream = params.delete(:in_stream)         || $stdin
176✔
25
      @out_stream = params.delete(:out_stream)       || $stdout
176✔
26
      @err_stream = params.delete(:err_stream)       || $stderr
176✔
27
      @adapter_klass = params.delete(:adapter)       || ::RemoteRuby::SSHAdapter
176✔
28
      @text_mode = params.delete(:text_mode)         || nil
176✔
29
      @code_dump_dir = params.delete(:code_dump_dir) || nil
176✔
30
      @params = params
176✔
31

32
      FileUtils.mkdir_p(@cache_dir)
176✔
33
    end
34
    # rubocop:enable Metrics/PerceivedComplexity
35
    # rubocop:enable Metrics/CyclomaticComplexity
36

37
    def execute(locals = nil, &block)
16✔
38
      source = code_source(block)
176✔
39
      locals ||= extract_locals(block)
176✔
40

41
      result = execute_code(source, **locals)
176✔
42

43
      assign_locals(locals.keys, result[:locals], block)
176✔
44

45
      result[:result]
176✔
46
    end
47

48
    private
16✔
49

50
    attr_reader :params, :adapter_klass, :use_cache, :save_cache, :cache_dir,
16✔
51
                :in_stream, :out_stream, :err_stream, :flavours, :text_mode, :code_dump_dir
52

53
    def assign_locals(local_names, values, block)
16✔
54
      local_names.each do |local|
176✔
55
        next unless values.key?(local)
112✔
56

57
        block.binding.local_variable_set(local, values[local])
112✔
58
      end
59
    end
60

61
    def extract_locals(block)
16✔
62
      extractor =
63
        ::RemoteRuby::LocalsExtractor.new(block, ignore_types: self.class)
112✔
64
      extractor.locals
112✔
65
    end
66

67
    def code_source(block)
16✔
68
      source_extractor = ::RemoteRuby::SourceExtractor.new
176✔
69
      source_extractor.extract(&block)
176✔
70
    end
71

72
    def context_hash(code_hash)
16✔
73
      Digest::MD5.hexdigest(
80✔
74
        self.class.name +
75
        adapter_klass.name.to_s +
76
        params.to_s +
77
        code_hash
78
      )
79
    end
80

81
    def cache_path(code_hash)
16✔
82
      hsh = context_hash(code_hash)
80✔
83
      File.join(cache_dir, hsh)
80✔
84
    end
85

86
    def cache_exists?(code_hash)
16✔
87
      hsh = cache_path(code_hash)
32✔
88
      File.exist?("#{hsh}.stdout") || File.exist?("#{hsh}.stderr")
32✔
89
    end
90

91
    def compiler(ruby_code, client_locals)
16✔
92
      RemoteRuby::Compiler.new(
176✔
93
        ruby_code,
94
        client_locals: client_locals,
95
        flavours: flavours
96
      )
97
    end
98

99
    def execute_code(ruby_code, client_locals = {})
16✔
100
      compiler = compiler(ruby_code, client_locals)
176✔
101

102
      dump_code(compiler.code_hash, compiler.compiled_code)
176✔
103

104
      runner = ::RemoteRuby::Runner.new(
176✔
105
        code: compiler.compiled_code,
106
        adapter: adapter(compiler.code_hash),
107
        in_stream: in_stream,
108
        out_stream: out_stream,
109
        err_stream: err_stream
110
      )
111

112
      runner.run
176✔
113
    end
114

115
    def dump_code(code_hash, code)
16✔
116
      return unless code_dump_dir
176✔
117

118
      path = File.join(code_dump_dir, "remote_ruby_#{code_hash}.rb")
×
119
      File.write(path, code)
×
120
    end
121

122
    def adapter(code_hash)
16✔
123
      res = adapter_klass.new(**params)
176✔
124

125
      cache_mode = use_cache && cache_exists?(code_hash)
176✔
126

127
      res = if cache_mode
176✔
128
              cache_adapter(code_hash, res.connection_name)
16✔
129
            elsif save_cache
160✔
130
              caching_adapter(res, code_hash)
32✔
131
            else
132
              res
128✔
133
            end
134

135
      wrap_text_mode(res, cache_mode)
176✔
136
    end
137

138
    def text_mode_params(adapter, cache_mode)
16✔
139
      params = ::RemoteRuby::TextModeAdapter::DEFAULT_SETTINGS.merge(
×
140
        stdout_prefix: adapter.connection_name,
141
        stderr_prefix: adapter.connection_name
142
      )
143

144
      params = params.merge(text_mode) if text_mode.is_a? Hash
×
145

146
      disable_unless_tty = params.delete(:disable_unless_tty) { |_| true }
×
147

NEW
148
      params[:stdout_prefix] = nil if disable_unless_tty && !out_stream.tty?
×
NEW
149
      params[:stderr_prefix] = nil if disable_unless_tty && !err_stream.tty?
×
NEW
150
      params[:cache_prefix] = nil unless cache_mode
×
UNCOV
151
      params
×
152
    end
153

154
    def wrap_text_mode(adapter, cache_mode)
16✔
155
      return adapter unless text_mode
176✔
156

157
      params = text_mode_params(adapter, cache_mode)
×
158

NEW
159
      return adapter unless params[:stdout_prefix] || params[:stderr_prefix]
×
160

161
      ::RemoteRuby::TextModeAdapter.new(adapter, **params)
×
162
    end
163

164
    def cache_adapter(code_hash, connection_name)
16✔
165
      ::RemoteRuby::CacheAdapter.new(
16✔
166
        cache_path: cache_path(code_hash),
167
        connection_name: connection_name
168
      )
169
    end
170

171
    def caching_adapter(adapter, code_hash)
16✔
172
      ::RemoteRuby::CachingAdapter.new(
32✔
173
        adapter: adapter,
174
        cache_path: cache_path(code_hash)
175
      )
176
    end
177

178
    def add_flavours(params)
16✔
179
      @flavours = ::RemoteRuby::Flavour.build_flavours(params)
176✔
180
    end
181
  end
182
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc