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

Nu-hin / remote_ruby / 13531520122

25 Feb 2025 09:40PM UTC coverage: 89.379% (-0.2%) from 89.617%
13531520122

push

github

Nu-hin
Autoselect adapter based on the host param presence

4 of 7 new or added lines in 1 file covered. (57.14%)

892 of 998 relevant lines covered (89.38%)

58.97 hits per line

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

84.04
/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/plugin'
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_plugins(params)
176✔
21
      chose_adapter_klass(params)
176✔
22
      @use_cache = params.delete(:use_cache)         || false
176✔
23
      @save_cache = params.delete(:save_cache)       || false
176✔
24
      @cache_dir = params.delete(:cache_dir)         || File.join(Dir.pwd, 'cache')
176✔
25
      @in_stream = params.delete(:in_stream)         || $stdin
176✔
26
      @out_stream = params.delete(:out_stream)       || $stdout
176✔
27
      @err_stream = params.delete(:err_stream)       || $stderr
176✔
28
      @text_mode = params.delete(:text_mode)         || false
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 chose_adapter_klass(params)
16✔
38
      @adapter_klass = params.delete(:adapter)
176✔
39
      return unless @adapter_klass.nil?
176✔
40

NEW
41
      @adapter_klass = if params[:host]
×
NEW
42
                         ::RemoteRuby::SSHAdapter
×
43
                       else
NEW
44
                         ::RemoteRuby::TmpFileAdapter
×
45
                       end
46
    end
47

48
    def execute(locals = nil, &block)
16✔
49
      source = code_source(block)
176✔
50
      locals ||= extract_locals(block)
176✔
51

52
      result = execute_code(source, **locals)
176✔
53

54
      assign_locals(locals.keys, result[:locals], block)
176✔
55

56
      result[:result]
176✔
57
    end
58

59
    private
16✔
60

61
    attr_reader :params, :adapter_klass, :use_cache, :save_cache, :cache_dir,
16✔
62
                :in_stream, :out_stream, :err_stream, :plugins, :text_mode, :code_dump_dir
63

64
    def assign_locals(local_names, values, block)
16✔
65
      local_names.each do |local|
176✔
66
        next unless values.key?(local)
112✔
67

68
        block.binding.local_variable_set(local, values[local])
112✔
69
      end
70
    end
71

72
    def extract_locals(block)
16✔
73
      extractor =
74
        ::RemoteRuby::LocalsExtractor.new(block, ignore_types: self.class)
112✔
75
      extractor.locals
112✔
76
    end
77

78
    def code_source(block)
16✔
79
      source_extractor = ::RemoteRuby::SourceExtractor.new
176✔
80
      source_extractor.extract(&block)
176✔
81
    end
82

83
    def context_hash(code_hash)
16✔
84
      Digest::MD5.hexdigest(
80✔
85
        self.class.name +
86
        adapter_klass.name.to_s +
87
        params.to_s +
88
        code_hash
89
      )
90
    end
91

92
    def cache_path(code_hash)
16✔
93
      hsh = context_hash(code_hash)
80✔
94
      File.join(cache_dir, hsh)
80✔
95
    end
96

97
    def cache_exists?(code_hash)
16✔
98
      hsh = cache_path(code_hash)
32✔
99
      File.exist?("#{hsh}.stdout") || File.exist?("#{hsh}.stderr")
32✔
100
    end
101

102
    def compiler(ruby_code, client_locals)
16✔
103
      RemoteRuby::Compiler.new(
176✔
104
        ruby_code,
105
        client_locals: client_locals,
106
        plugins: plugins
107
      )
108
    end
109

110
    def execute_code(ruby_code, client_locals = {})
16✔
111
      compiler = compiler(ruby_code, client_locals)
176✔
112

113
      dump_code(compiler.code_hash, compiler.compiled_code)
176✔
114

115
      runner = ::RemoteRuby::Runner.new(
176✔
116
        code: compiler.compiled_code,
117
        adapter: adapter(compiler.code_hash),
118
        in_stream: in_stream,
119
        out_stream: out_stream,
120
        err_stream: err_stream
121
      )
122

123
      runner.run
176✔
124
    end
125

126
    def dump_code(code_hash, code)
16✔
127
      return unless code_dump_dir
176✔
128

129
      path = File.join(code_dump_dir, "remote_ruby_#{code_hash}.rb")
×
130
      File.write(path, code)
×
131
    end
132

133
    def adapter(code_hash)
16✔
134
      res = adapter_klass.new(**params)
176✔
135

136
      cache_mode = use_cache && cache_exists?(code_hash)
176✔
137

138
      res = if cache_mode
176✔
139
              cache_adapter(code_hash, res.connection_name)
16✔
140
            elsif save_cache
160✔
141
              caching_adapter(res, code_hash)
32✔
142
            else
143
              res
128✔
144
            end
145

146
      wrap_text_mode(res, cache_mode)
176✔
147
    end
148

149
    def text_mode_params(adapter, cache_mode)
16✔
150
      params = ::RemoteRuby::TextModeAdapter::DEFAULT_SETTINGS.merge(
×
151
        stdout_prefix: adapter.connection_name,
152
        stderr_prefix: adapter.connection_name
153
      )
154

155
      params = params.merge(text_mode) if text_mode.is_a? Hash
×
156

157
      disable_unless_tty = params.delete(:disable_unless_tty) { |_| true }
×
158

159
      params[:stdout_prefix] = nil if disable_unless_tty && !out_stream.tty?
×
160
      params[:stderr_prefix] = nil if disable_unless_tty && !err_stream.tty?
×
161
      params[:cache_prefix] = nil unless cache_mode
×
162
      params
×
163
    end
164

165
    def wrap_text_mode(adapter, cache_mode)
16✔
166
      return adapter unless text_mode
176✔
167

168
      params = text_mode_params(adapter, cache_mode)
×
169

170
      return adapter unless params[:stdout_prefix] || params[:stderr_prefix]
×
171

172
      ::RemoteRuby::TextModeAdapter.new(adapter, **params)
×
173
    end
174

175
    def cache_adapter(code_hash, connection_name)
16✔
176
      ::RemoteRuby::CacheAdapter.new(
16✔
177
        cache_path: cache_path(code_hash),
178
        connection_name: connection_name
179
      )
180
    end
181

182
    def caching_adapter(adapter, code_hash)
16✔
183
      ::RemoteRuby::CachingAdapter.new(
32✔
184
        adapter: adapter,
185
        cache_path: cache_path(code_hash)
186
      )
187
    end
188

189
    def add_plugins(params)
16✔
190
      @plugins = ::RemoteRuby::Plugin.build_plugins(params)
176✔
191
    end
192
  end
193
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