Module: Kitchen::Util
- Defined in:
- lib/kitchen/util.rb
Overview
Stateless utility methods used in different contexts. Essentially a mini PassiveSupport library.
Constant Summary collapse
- MASK =
The text substituted for a masked value.
"******".freeze
- QUOTED_VALUE =
A double-quoted string value, allowing for backslash escapes so that a quote inside the value does not end the match early.
/"(?:\\.|[^"\\])*"/.source
Class Attribute Summary collapse
-
.mutex_chdir ⇒ Mutex
A mutex used to serialize the process-global Dir.chdir.
Class Method Summary collapse
-
.camel_case(a_string) ⇒ String
Returns a CamelCase version of a snake_case string.
-
.command_exists?(cmd) ⇒ Boolean
Check if a cmd exists on the PATH.
-
.duration(total) ⇒ String
Returns a formatted string representing a duration in seconds.
-
.from_logger_level(const) ⇒ Symbol
Returns the symbol representation of a logging level for a given standard library Logger::Severity constant.
-
.list_directory(path, include_dot: false, recurse: false) ⇒ Object
Lists the contents of the given directory.
-
.mask_values(string_to_mask, keys) ⇒ String
Returns a string with masked values for specified parameters.
-
.outdent(string) ⇒ String
Returns a copy of a string with each line outdented by the indentation level of its first line.
-
.outdent!(string) ⇒ String
Modifies the given string to strip leading whitespace on each line, the amount which is calculated by using the first line of text.
-
.safe_glob(path, pattern, *flags) ⇒ Object
Similar to Dir.glob.
-
.shell_helpers ⇒ String
Returns a set of Bourne Shell (AKA /bin/sh) compatible helper functions.
-
.snake_case(a_string) ⇒ String
Returns a snake_case version of a CamelCase string.
-
.stringified_hash(obj) ⇒ Object
Returns a new Hash with all key values coerced to strings.
-
.symbolized_hash(obj) ⇒ Object
Returns a new Hash with all key values coerced to symbols.
-
.to_logger_level(symbol) ⇒ Integer
Returns the standard library Logger level constants for a given symbol representation.
-
.wrap_command(cmd) ⇒ String
Generates a command (or series of commands) wrapped so that it can be invoked on a remote instance or locally.
Class Attribute Details
.mutex_chdir ⇒ Mutex
Returns a mutex used to serialize the process-global Dir.chdir.
29 30 31 |
# File 'lib/kitchen/util.rb', line 29 def mutex_chdir @mutex_chdir end |
Class Method Details
.camel_case(a_string) ⇒ String
Returns a CamelCase version of a snake_case string.
273 274 275 |
# File 'lib/kitchen/util.rb', line 273 def self.camel_case(a_string) Thor::Util.camel_case(a_string) end |
.command_exists?(cmd) ⇒ Boolean
Check if a cmd exists on the PATH
286 287 288 289 290 291 292 293 |
# File 'lib/kitchen/util.rb', line 286 def self.command_exists?(cmd) paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ] paths.each do |path| filename = File.join(path, cmd) return filename if File.executable?(filename) end false end |
.duration(total) ⇒ String
Returns a formatted string representing a duration in seconds.
137 138 139 140 141 142 |
# File 'lib/kitchen/util.rb', line 137 def self.duration(total) total = 0 if total.nil? minutes = (total / 60).to_i seconds = (total - (minutes * 60)) format("(%dm%.2fs)", minutes, seconds) end |
.from_logger_level(const) ⇒ Symbol
Returns the symbol representation of a logging level for a given standard library Logger::Severity constant.
58 59 60 61 62 63 64 65 66 |
# File 'lib/kitchen/util.rb', line 58 def self.from_logger_level(const) case const when Logger::DEBUG then :debug when Logger::INFO then :info when Logger::WARN then :warn when Logger::ERROR then :error else :fatal end end |
.list_directory(path, include_dot: false, recurse: false) ⇒ Object
You should prefer this method to using Dir.glob directly. The reason is
Dir.chdir is applied to the process, thus it is not thread-safe
Lists the contents of the given directory. path will be prepended to the list returned. '.' and '..' are never returned.
because Dir.glob behaves strangely on Windows. It won't accept '' and doesn't like fake directories (C:Documents and Settings) It also does not do any sort of error checking, so things one would expect to fail just return an empty list
and must be synchronized.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/kitchen/util.rb', line 221 def self.list_directory(path, include_dot: false, recurse: false) # Things (such as tests) are relying on this to not blow up if # the directory does not exist return [] unless Dir.exist?(path) mutex_chdir.synchronize do Dir.chdir(path) do glob_pattern = if recurse "**/*" else "*" end flags = if include_dot [File::FNM_DOTMATCH] else [] end Dir.glob(glob_pattern, *flags) .reject { |f| [".", ".."].include?(f) } .map { |f| File.join(path, f) } end end end |
.mask_values(string_to_mask, keys) ⇒ String
Returns a string with masked values for specified parameters.
Hashes are commonly interpolated into these strings via Hash#inspect, whose rendering has changed over time, so every spelling a key/value pair has had is matched:
:key=>"value" symbol key, Ruby <= 3.3
"key"=>"value" string key, Ruby <= 3.3
key: "value" symbol key, Ruby >= 3.4
"key" => "value" string key, Ruby >= 3.4
123 124 125 126 127 128 129 130 131 |
# File 'lib/kitchen/util.rb', line 123 def self.mask_values(string_to_mask, keys) keys.reduce(string_to_mask.to_s) do |masked, key| key = ::Regexp.escape(key.to_s) masked.gsub( /(?<assignment>(?::#{key}|"#{key}")\s*=>\s*|(?<!\w)#{key}:\s*)#{QUOTED_VALUE}/, %{\\k<assignment>"#{MASK}"} ) end end |
.outdent(string) ⇒ String
Returns a copy of a string with each line outdented by the indentation level of its first line.
Prefer this over outdent! when the input may be a string literal: Ruby 4 freezes literals, and mutating one raises a FrozenError.
190 191 192 |
# File 'lib/kitchen/util.rb', line 190 def self.outdent(string) string.gsub(/^ {#{string.index(/[^ ]/)}}/, "") end |
.outdent!(string) ⇒ String
Modifies the given string to strip leading whitespace on each line, the amount which is calculated by using the first line of text.
174 175 176 |
# File 'lib/kitchen/util.rb', line 174 def self.outdent!(string) string.gsub!(/^ {#{string.index(/[^ ]/)}}/, "") end |
.safe_glob(path, pattern, *flags) ⇒ Object
Dir.chdir is applied to the process, thus it is not thread-safe
Similar to Dir.glob.
The difference is this function forces you to specify where to glob from. You should glob from the path closest to what you want. The reason for this is because if you have symlinks on windows of any kind, Dir.glob will not traverse them.
and must be synchronized.
259 260 261 262 263 264 265 266 267 |
# File 'lib/kitchen/util.rb', line 259 def self.safe_glob(path, pattern, *flags) return [] unless Dir.exist?(path) mutex_chdir.synchronize do Dir.chdir(path) do Dir.glob(pattern, *flags).map { |f| File.join(path, f) } end end end |
.shell_helpers ⇒ String
Returns a set of Bourne Shell (AKA /bin/sh) compatible helper functions. This function is usually called inline in a string that will be executed remotely on a test instance.
199 200 201 202 203 |
# File 'lib/kitchen/util.rb', line 199 def self.shell_helpers File.read(File.join( File.dirname(__FILE__), %w{.. .. support download_helpers.sh} )) end |
.snake_case(a_string) ⇒ String
Returns a snake_case version of a CamelCase string.
281 282 283 |
# File 'lib/kitchen/util.rb', line 281 def self.snake_case(a_string) Thor::Util.snake_case(a_string) end |
.stringified_hash(obj) ⇒ Object
Returns a new Hash with all key values coerced to strings. All keys within a Hash are coerced by calling #to_s and hashes with arrays and other hashes are traversed.
92 93 94 95 96 97 98 99 100 |
# File 'lib/kitchen/util.rb', line 92 def self.stringified_hash(obj) if obj.is_a?(Hash) obj.inject({}) { |h, (k, v)| h[k.to_s] = stringified_hash(v); h } elsif obj.is_a?(Array) obj.inject([]) { |a, e| a << stringified_hash(e); a } else obj end end |
.symbolized_hash(obj) ⇒ Object
Returns a new Hash with all key values coerced to symbols. All keys within a Hash are coerced by calling #to_sym and hashes within arrays and other hashes are traversed.
75 76 77 78 79 80 81 82 83 |
# File 'lib/kitchen/util.rb', line 75 def self.symbolized_hash(obj) if obj.is_a?(Hash) obj.inject({}) { |h, (k, v)| h[k.to_sym] = symbolized_hash(v); h } elsif obj.is_a?(Array) obj.inject([]) { |a, e| a << symbolized_hash(e); a } else obj end end |
.to_logger_level(symbol) ⇒ Integer
Returns the standard library Logger level constants for a given symbol representation.
45 46 47 48 49 |
# File 'lib/kitchen/util.rb', line 45 def self.to_logger_level(symbol) return nil unless i{debug info warn error fatal}.include?(symbol) Logger.const_get(symbol.to_s.upcase) end |
.wrap_command(cmd) ⇒ String
Generates a command (or series of commands) wrapped so that it can be invoked on a remote instance or locally.
This method uses the Bourne shell (/bin/sh) to maximize the chance of cross platform portability on Unixlike systems.
152 153 154 155 156 157 158 |
# File 'lib/kitchen/util.rb', line 152 def self.wrap_command(cmd) cmd = "false" if cmd.nil? cmd = "true" if cmd.to_s.empty? cmd = cmd.sub(/\n\Z/, "") if /\n\Z/.match?(cmd) "sh -c '\n#{cmd}\n'" end |