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

Nu-hin / remote_ruby / 13423829479

19 Feb 2025 10:45PM UTC coverage: 94.237% (-0.02%) from 94.253%
13423829479

push

github

Nu-hin
Add logger to gemspec

1161 of 1232 relevant lines covered (94.24%)

21.08 hits per line

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

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

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

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

13
module RemoteRuby
4✔
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
4✔
17
    # rubocop:disable Metrics/CyclomaticComplexity
18
    # rubocop:disable Metrics/PerceivedComplexity
19
    def initialize(**params)
4✔
20
      add_flavours(params)
44✔
21
      @use_cache = params.delete(:use_cache)         || false
44✔
22
      @save_cache = params.delete(:save_cache)       || false
44✔
23
      @cache_dir = params.delete(:cache_dir)         || File.join(Dir.pwd, 'cache')
44✔
24
      @in_stream = params.delete(:in_stream)         || $stdin
44✔
25
      @out_stream = params.delete(:out_stream)       || $stdout
44✔
26
      @err_stream = params.delete(:err_stream)       || $stderr
44✔
27
      @adapter_klass = params.delete(:adapter)       || ::RemoteRuby::SSHAdapter
44✔
28
      @text_mode = params.delete(:text_mode)         || nil
44✔
29
      @code_dump_dir = params.delete(:code_dump_dir) || nil
44✔
30
      @params = params
44✔
31

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

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

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

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

45
      result[:result]
44✔
46
    end
47

48
    private
4✔
49

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

53
    def assign_locals(local_names, values, block)
4✔
54
      local_names.each do |local|
44✔
55
        next unless values.key?(local)
28✔
56

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

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

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

72
    def context_hash(code_hash)
4✔
73
      Digest::MD5.hexdigest(
20✔
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)
4✔
82
      hsh = context_hash(code_hash)
20✔
83
      File.join(cache_dir, hsh)
20✔
84
    end
85

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

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

99
    def execute_code(ruby_code, client_locals = {})
4✔
100
      compiler = compiler(ruby_code, client_locals)
44✔
101

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

104
      runner = ::RemoteRuby::Runner.new(
44✔
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
44✔
113
    end
114

115
    def dump_code(code_hash, code)
4✔
116
      return unless code_dump_dir
44✔
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)
4✔
123
      res = adapter_klass.new(**params)
44✔
124

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

127
      res = if cache_mode
44✔
128
              cache_adapter(code_hash, res.connection_name)
4✔
129
            elsif save_cache
40✔
130
              caching_adapter(res, code_hash)
8✔
131
            else
132
              res
32✔
133
            end
134

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

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

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

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

149
      params[:disable_stdout_prefixing] = true if disable_unless_tty && !out_stream.tty?
×
150
      params[:disable_stderr_prefixing] = true if disable_unless_tty && !err_stream.tty?
×
151
      params
×
152
    end
153

154
    def wrap_text_mode(adapter, cache_mode)
4✔
155
      return adapter unless text_mode
44✔
156

157
      params = text_mode_params(adapter, cache_mode)
×
158

159
      return adapter if params[:disable_stdout_prefixing] && params[:disable_stderr_prefixing]
×
160

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

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

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

178
    def add_flavours(params)
4✔
179
      @flavours = ::RemoteRuby::Flavour.build_flavours(params)
44✔
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