Module: Lich::Util

Includes:
Enumerable
Defined in:
documented/update.rb,
documented/util/opts.rb,
documented/util/util.rb,
documented/magic-info.rb,
documented/util/textstripper.rb,
documented/util/login_helpers.rb,
documented/util/memoryreleaser.rb,
documented/common/update/file_writer.rb,
documented/common/update/script_sync.rb,
documented/common/update/custom_repos.rb,
documented/common/update/file_updater.rb,
documented/common/update/github_client.rb,
documented/common/update/status_reporter.rb,
documented/common/update/tracked_scripts.rb,
documented/common/update/branch_installer.rb,
documented/common/update/channel_resolver.rb,
documented/common/update/snapshot_manager.rb,
documented/common/update/release_installer.rb

Overview

Provides utility methods for the Lich project.

See Also:

Defined Under Namespace

Modules: LoginHelpers, Magicinfo, MemoryReleaser, TextStripper, Update Classes: Opts

Class Method Summary collapse

Class Method Details

.anon_hook(prefix = '') ⇒ String

Generates a unique anonymous hook name based on the current time and a random number.

Parameters:

  • prefix (String) (defaults to: '')

    an optional prefix for the hook name.

Returns:

  • (String)

    the generated anonymous hook name.



57
58
59
60
# File 'documented/util/util.rb', line 57

def self.anon_hook(prefix = '')
  now = Time.now
  "Util::#{prefix}-#{now}-#{Random.rand(10000)}"
end

.deep_freeze(obj) ⇒ Object

Recursively freezes an object and its contents.

Parameters:

  • obj (Object)

    the object to freeze.

Returns:

  • (Object)

    the frozen object.



279
280
281
282
283
284
285
286
287
288
289
290
# File 'documented/util/util.rb', line 279

def self.deep_freeze(obj)
  case obj
  when Hash
    obj.each do |k, v|
      deep_freeze(k)
      deep_freeze(v)
    end
  when Array
    obj.each { |el| deep_freeze(el) }
  end
  obj.freeze
end

.install_gem_requirements(gems_to_install, user_install: false) ⇒ Object

Installs the specified Ruby gems and requires them if specified.

Parameters:

  • gems_to_install (Hash)

    a hash of gem names and whether to require them.

  • user_install (Boolean) (defaults to: false)

    whether to install gems for the user (default: false).

Raises:

  • (ArgumentError)

    if gems_to_install is not a Hash or has invalid keys/values.



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'documented/util/util.rb', line 223

def self.install_gem_requirements(gems_to_install, user_install: false)
  raise ArgumentError, "install_gem_requirements must be passed a Hash" unless gems_to_install.is_a?(Hash)
  require "rubygems"
  require "rubygems/dependency_installer"
  installer = Gem::DependencyInstaller.new({ :user_install => user_install, :document => nil })
  installed_gems = Gem::Specification.map { |gem| gem.name }.sort.uniq
  failed_gems = []

  gems_to_install.each do |gem_name, should_require|
    unless gem_name.is_a?(String) && (should_require.is_a?(TrueClass) || should_require.is_a?(FalseClass))
      raise ArgumentError, "install_gem_requirements must be passed a Hash with String key and TrueClass/FalseClass as value"
    end
    begin
      unless installed_gems.include?(gem_name)
        respond("--- Lich: Installing missing ruby gem '#{gem_name}' now, please wait!") if defined?(Script)
        Lich.log("--- Lich: Installing missing ruby gem '#{gem_name}' now, please wait!")
        result = installer.install(gem_name)
        Gem.clear_paths
        Gem::Specification.reset
        Gem::Specification.find_by_name(gem_name).activate
        Lich.log("RubyGem Installer Result: #{result.inspect}")
        unless Gem::Specification.map { |gem| gem.name }.sort.uniq.include?(gem_name)
          Lich.log("RubyGems failed, attempting system method instead!")
          result = system(File.join(RbConfig::CONFIG['bindir'], 'gem'), 'install', gem_name)
          Lich.log("SYSTEM Call Result: #{result.inspect}")
          Gem.clear_paths
          Gem::Specification.reset
          Gem::Specification.find_by_name(gem_name).activate
        end
        respond("--- Lich: Done installing '#{gem_name}' gem!") if defined?(Script)
        Lich.log("--- Lich: Done installing '#{gem_name}' gem!")
      end
      require gem_name if should_require
    rescue LoadError, StandardError
      respond("--- Lich: error: Failed to install/require Ruby gem: #{gem_name}") if defined?(Script)
      respond("--- Lich: error: #{$!}") if defined?(Script)
      Lich.log("installed_gems.include?(#{gem_name}): #{installed_gems.include?(gem_name)} - #{installed_gems.find_all { |gem| gem == gem_name }.inspect}")
      Lich.log("error: Failed to install/require Ruby gem: #{gem_name}")
      Lich.log("error: #{$!}")
      failed_gems.push(gem_name)
    end
  end
  unless failed_gems.empty?
    if defined?(Script.current.name) && Script.current.name != "unknown"
      raise("Please install the failed gems: #{failed_gems.join(', ')} manually to run #{$lich_char}#{Script.current.name}")
    else
      raise("Please install the failed gems: #{failed_gems.join(', ')} manually to continue.")
    end
  end
end

.issue_command(command, start_pattern, end_pattern = /<prompt/, include_end: true, timeout: 5, silent: nil, usexml: true, quiet: false, use_fput: true) ⇒ Array<String>

Issues a command and captures the output based on the provided patterns.

Parameters:

  • command (String)

    the command to execute.

  • start_pattern (Regexp)

    the pattern to identify the start of the output.

  • end_pattern (Regexp) (defaults to: /<prompt/)

    the pattern to identify the end of the output (default: /<prompt/).

  • include_end (Boolean) (defaults to: true)

    whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    the maximum time to wait for the command to complete (default: 5).

  • silent (Boolean, nil) (defaults to: nil)

    whether to suppress output (default: nil).

  • usexml (Boolean) (defaults to: true)

    whether to use XML output (default: true).

  • quiet (Boolean) (defaults to: false)

    whether to suppress output during processing (default: false).

  • use_fput (Boolean) (defaults to: true)

    whether to use fput instead of put (default: true).

Returns:

  • (Array<String>)

    the lines of output captured from the command.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'documented/util/util.rb', line 74

def self.issue_command(command, start_pattern, end_pattern = /<prompt/, include_end: true, timeout: 5, silent: nil, usexml: true, quiet: false, use_fput: true)
  result = []
  name = self.anon_hook
  filter = false
  ignore_end = end_pattern.eql?(:ignore)

  save_script_silent = Script.current.silent
  save_want_downstream = Script.current.want_downstream
  save_want_downstream_xml = Script.current.want_downstream_xml

  Script.current.silent = silent if !silent.nil?
  Script.current.want_downstream = !usexml
  Script.current.want_downstream_xml = usexml

  begin
    Timeout::timeout(timeout, Interrupt) {
      DownstreamHook.add(name, proc { |line|
        if filter
          if ignore_end || line =~ end_pattern
            DownstreamHook.remove(name)
            filter = false
            if quiet && !ignore_end
              next(nil)
            else
              line
            end
          else
            if quiet
              next(nil)
            else
              line
            end
          end
        elsif line =~ start_pattern
          filter = true
          if quiet
            next(nil)
          else
            line
          end
        else
          line
        end
      })
      use_fput ? fput(command) : put(command)

      until (line = get) =~ start_pattern; end
      result << line.rstrip
      unless ignore_end
        until (line = get) =~ end_pattern
          result << line.rstrip
        end
      end
      unless ignore_end
        if include_end
          result << line.rstrip
        end
      end
    }
  rescue Interrupt
    nil
  ensure
    DownstreamHook.remove(name)
    Script.current.silent = save_script_silent if !silent.nil?
    Script.current.want_downstream = save_want_downstream
    Script.current.want_downstream_xml = save_want_downstream_xml
  end
  return result
end

.normalize_lookup(effect, val) ⇒ Boolean

Normalizes the lookup for effects based on the provided value.

Parameters:

  • effect (String)

    the name of the effect to look up.

  • val (String, Integer, Symbol)

    the value to normalize and check.

Returns:

  • (Boolean)

    true if the normalized value exists, false otherwise.

Raises:

  • (RuntimeError)

    if the lookup case is invalid.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'documented/util/util.rb', line 19

def self.normalize_lookup(effect, val)
  caller_type = "Effects::#{effect}"
  case val
  when String
    (eval caller_type).to_h.transform_keys(&:to_s).transform_keys(&:downcase).include?(val.downcase.gsub('_', ' '))
  when Integer
    #      seek = mappings.fetch(val, nil)
    (eval caller_type).active?(val)
  when Symbol
    (eval caller_type).to_h.transform_keys(&:to_s).transform_keys(&:downcase).include?(val.to_s.downcase.gsub('_', ' '))
  else
    fail "invalid lookup case #{val.class.name}"
  end
end

.normalize_name(name) ⇒ String

Normalizes a given name by converting it to a lowercase string and replacing or removing certain characters.

The normalization process handles the following cases:

  • Converts spaces and hyphens to underscores.
  • Removes colons and apostrophes.
  • Converts symbols to strings. Normalizes a given name by converting it to a lowercase string and replacing or removing certain characters.

Parameters:

  • name (String)

    the name to normalize.

Returns:

  • (String)

    the normalized name.



44
45
46
47
48
49
50
51
# File 'documented/util/util.rb', line 44

def self.normalize_name(name)
  normal_name = name.to_s.downcase
  normal_name.gsub!(' ', '_') if name =~ (/\s/)
  normal_name.gsub!('-', '_') if name =~ (/-/)
  normal_name.gsub!(":", '') if name =~ (/:/)
  normal_name.gsub!("'", '') if name =~ (/'/)
  normal_name
end

.quiet_command(command, start_pattern, end_pattern, include_end = true, timeout = 5, silent = true) ⇒ Array<String>

Issues a command quietly and captures the output.

Parameters:

  • command (String)

    the command to execute.

  • start_pattern (Regexp)

    the pattern to identify the start of the output.

  • end_pattern (Regexp)

    the pattern to identify the end of the output.

  • include_end (Boolean) (defaults to: true)

    whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    the maximum time to wait for the command to complete (default: 5).

  • silent (Boolean) (defaults to: true)

    whether to suppress output (default: true).

Returns:

  • (Array<String>)

    the lines of output captured from the command.



166
167
168
# File 'documented/util/util.rb', line 166

def self.quiet_command(command, start_pattern, end_pattern, include_end = true, timeout = 5, silent = true)
  return issue_command(command, start_pattern, end_pattern, include_end: include_end, timeout: timeout, silent: silent, usexml: false, quiet: true)
end

.quiet_command_xml(command, start_pattern, end_pattern = /<prompt/, include_end = true, timeout = 5, silent = true) ⇒ Array<String>

Issues a command quietly and captures the output in XML format.

Parameters:

  • command (String)

    the command to execute.

  • start_pattern (Regexp)

    the pattern to identify the start of the output.

  • end_pattern (Regexp) (defaults to: /<prompt/)

    the pattern to identify the end of the output (default: /<prompt/).

  • include_end (Boolean) (defaults to: true)

    whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    the maximum time to wait for the command to complete (default: 5).

  • silent (Boolean) (defaults to: true)

    whether to suppress output (default: true).

Returns:

  • (Array<String>)

    the lines of output captured from the command.



153
154
155
# File 'documented/util/util.rb', line 153

def self.quiet_command_xml(command, start_pattern, end_pattern = /<prompt/, include_end = true, timeout = 5, silent = true)
  return issue_command(command, start_pattern, end_pattern, include_end: include_end, timeout: timeout, silent: silent, usexml: true, quiet: true)
end

.silver_count(timeout = 3) ⇒ Integer

Retrieves the count of silver from the output of a command.

Parameters:

  • timeout (Integer) (defaults to: 3)

    the maximum time to wait for the command to complete (default: 3).

Returns:

  • (Integer)

    the count of silver.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'documented/util/util.rb', line 174

def self.silver_count(timeout = 3)
  silence_me unless (undo_silence = silence_me)
  result = ''
  name = self.anon_hook
  filter = false

  start_pattern = /^\s*Name\:/
  end_pattern = /^\s*Mana\:\s+\-?[0-9]+\s+Silver\:\s+([0-9,]+)/
  ttl = Time.now + timeout
  begin
    # main thread
    DownstreamHook.add(name, proc { |line|
      if filter
        if line =~ end_pattern
          result = $1.dup
          DownstreamHook.remove(name)
          filter = false
        else
          next(nil)
        end
      elsif line =~ start_pattern
        filter = true
        next(nil)
      else
        line
      end
    })
    # script thread
    fput 'info'
    loop {
      # non-blocking check, this allows us to
      # check the time even when the buffer is empty
      line = get?
      break if line && line =~ end_pattern
      break if Time.now > ttl
      sleep(0.01) # prevent a tight-loop
    }
  ensure
    DownstreamHook.remove(name)
    silence_me if undo_silence
  end
  return result.gsub(',', '').to_i
end