lib/cloud_powers/helper.rb in cloud_powers-0.2.4 vs lib/cloud_powers/helper.rb in cloud_powers-0.2.5
- old
+ new
@@ -6,40 +6,116 @@
module Smash
module CloudPowers
module Helper
+ # Sets an Array of instance variables, individually to a value that a
+ # user given block returns.
+ # === @params Array
+ # * each object will be used as the name for the instance variable that
+ # your block returns
+ # === @&block (optional)
+ # * this is called for each object in the Array and is used as the value
+ # for the instance variable that is being named and created for each key
+ # === @return Array
+ # * each object will either be the result of `#instance_variable_set(key, value)`
+ # or instance_variable_get(key)
+ # === Sampel use:
+ # keys = ['foo', 'bar', 'yo']
+ #
+ # attr_map!(keys) { |key| sleep 1; "#{key}:#{Time.now.to_i}" }
+ # # => ['foo:1475434058', 'bar:1475434059', 'yo:1475434060']
+ #
+ # puts @bar
+ # # => 'bar:1475434059'
def attr_map!(keys)
- keys.map do |attr|
- key = to_i_var(attr)
+ keys.map do |key|
+ new_i_var = to_i_var(key)
value = yield key if block_given?
- instance_variable_set(key, value) unless instance_variable_get(to_i_var(key))
+ instance_variable_set(new_i_var, value) unless instance_variable_get(new_i_var)
end
end
# This is a way to find out if you are trying to work with a resource
# available to CloudPowers
# === @returns <Array>
+ # * Use `.constants` to find all the modules and classes available.
+ # Notes:
+ # TODO: make this smartly pick up all the objects, within reason and
+ # considering need, that we have access to
def available_resources
[:Task].concat(Smash::CloudPowers.constants)
end
+ # Does its best job at guessing where this method was called from, in terms
+ # of where it is located on the file system. It helps track down where a
+ # project root is etc.
def called_from
File.expand_path(File.dirname($0))
end
+ # creates a default logger
+ # Notes:
+ # * TODO: at least make this have overridable defaults
def create_logger
logger = Logger.new(STDOUT)
logger.datetime_format = '%Y-%m-%d %H:%M:%S'
logger
end
+ # Allows you to modify all keys, including nested, with a block that you pass.
+ # If no block is passed, a copy is returned.
+ # === @params
+ # * params Hash|Array
+ # === @&block (optional)
+ # * should modify the key and return that value so it can be used in the copy
+ # === @returns
+ # * a copy of the given Array or Hash, with all Hash keys modified
+ # === Sample use:
+ # hash = { 'foo' => 'v1', 'bar' => { fleep: { 'florp' => 'yo' } } }
+ # modify_keys_with(hash) { |key| key.to_sym }
+ # # => { foo: 'v1', bar: { fleep: { florp: 'yo' } } }
+ # === Notes:
+ # * see `#modify_keys_with()` for handling first-level keys
+ # * see `#pass_the_buck()` for the way nested structures are handled
+ # * case for different types taken from _MultiXML_ (multi_xml.rb)
+ # * TODO: look at optimization
+ def deep_modify_keys_with(params)
+ case params
+ when Hash
+ params.inject({}) do |carry, (k, v)|
+ carry.tap do |h|
+ if block_given?
+ key = yield k
+
+ value = if v.kind_of?(Hash)
+ deep_modify_keys_with(v) do |new_key|
+ Proc.new.call(new_key)
+ end
+ else
+ v
+ end
+
+ h[key] = value
+ else
+ h[k] = v
+ end
+ end
+ end
+ when Array
+ params.map{ |value| symbolize_keys(value) }
+ else
+ params
+ end
+ end
+
def errors
- # TODO: needs work
+ # TODO: wow...needs work
$errors ||= SmashError.instance
end
+ # Join the message and backtrace into a String with line breaks
def format_error_message(error)
begin
[error.message, error.backtrace.join("\n")].join("\n")
rescue Exception => e
# if the formatting won't work, return the original exception
@@ -56,15 +132,39 @@
# @returns: An instance of Logger, cached as @logger
def logger
@logger ||= create_logger
end
+ # Allows you to modify all first-level keys with a block that you pass.
+ # If no block is passed, a copy is returned.
+ # === @params
+ # * params Hash|Array
+ # === @&block (optional)
+ # * should modify the key and return that value so it can be used in the copy
+ # === @returns
+ # * a copy of the given Array or Hash, with all Hash keys modified
+ # === Sample use:
+ # hash = { 'foo' => 'v1', 'bar' => { fleep: { 'florp' => 'yo' } } }
+ # modify_keys_with(hash) { |k| k.to_sym }
+ # # => { :foo => 'v1', :bar => { fleep: { 'florp' => 'yo' } } }
+ # === Notes:
+ # * see `#deep_modify_keys_with()` for handling nested keys
+ # * case for different types taken from _MultiXML_ (multi_xml.rb)
+ def modify_keys_with(params)
+ params.inject({}) do |carry, (k, v)|
+ carry.tap do |h|
+ key = block_given? ? (yield k) : k
+ h[key] = v
+ end
+ end
+ end
+
# Lets you retry a piece of logic with 1 second sleep in between attempts
# until another bit of logic does what it's supposed to, kind of like
# continuing to poll something and doing something when a package is ready
# to be taken and processed.
- # @params:
+ # === @params:
# * [allowed_attempts] or Infinity(default) <Number>: The number of times
# the loop should be allowed to...well, loop, before a failed retry occurs.
# * &test <Block>: A predicate method or block of code that is callable
# is used to test if the block being retried is successful yet.
# * []
@@ -79,58 +179,94 @@
tries += 1
sleep 1
end
end
- def symbolize_keys(hash)
- hash.inject({}) { |carry, (k, v)| carry.tap { |h| h[k.to_sym] = v } }
- end
-
# Gives the path from the project root to lib/tasks[/#{file}.rb]
- # @params:
+ # === @params:
# * [file] <String>: name of a file
- # @returns:
+ # === @returns:
# * path[/file] <String>
# * If a `file` is given, it will have a '.rb' file extension
# * If no `file` is given, it will return the `#task_require_path`
def task_path(file = '')
return task_require_path if file.empty?
Pathname(__FILE__).parent.dirname + 'tasks' + to_ruby_file_name(file)
end
# Gives the path from the project root to lib/tasks[/file]
- # @params:
- # * [file] <String>: name of a file
- # @returns:
- # * path[/file] <String>
+ # === @params String (optional)
+ # * file_name name of a file
+ # === @returns:
+ # * path[/file] String
# * Neither path nor file will have a file extension
def task_require_path(file_name = '')
file = File.basename(file_name, File.extname(file_name))
Pathname(__FILE__).parent.dirname + 'tasks' + file
end
+ # Change strings into camelCase
+ # === @params var String
+ # === @returns String
+ # * givenString
def to_camel(var)
var = var.to_s unless var.kind_of? String
step_one = to_snake(var)
step_two = to_pascal(step_one)
step_two[0, 1].downcase + step_two[1..-1]
end
+ # Change strings hyphen-delimited-string
+ # === @params var String
+ # === @returns String
+ # * given-string
+ def to_hyph(var)
+ var = var.to_s unless var.kind_of? String
+
+ var.gsub(/:{2}|\//, '-').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ gsub(/\s+/, '-').
+ tr("_", "-").
+ gsub(/^\W/, '').
+ downcase
+ end
+
+ # Change strings into snake_case and add '@' at the front
+ # === @params var String
+ # === @returns String
+ # * @given_string
def to_i_var(var)
var = var.to_s unless var.kind_of? String
/^\W*@\w+/ =~ var ? to_snake(var) : "@#{to_snake(var)}"
end
+ # Change strings into PascalCase
+ # === @params var String
+ # === @returns String
+ # * givenString
def to_pascal(var)
var = var.to_s unless var.kind_of? String
var.gsub(/^(.{1})|\W.{1}|\_.{1}/) { |s| s.gsub(/[^a-z0-9]+/i, '').capitalize }
end
+ # Change strings into a ruby_file_name with extension
+ # === @params var String
+ # === @returns String
+ # * given_string.rb
+ # * includes ruby file extension
+ # * see #to_snake()
def to_ruby_file_name(name)
name[/\.rb$/].nil? ? "#{to_snake(name)}.rb" : "#{to_snake(name)}"
end
+ # Change strings into snake_case
+ # === @params var String
+ # === @returns String
+ # * given_string
+ # * will not have file extensions
+ # * see #to_ruby_file_name()
def to_snake(var)
var = var.to_s unless var.kind_of? String
var.gsub(/:{2}|\//, '_').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
@@ -138,16 +274,20 @@
gsub(/\s+/, '_').
tr("-", "_").
downcase
end
+ # This method provides a default overrideable message body for things like
+ # basic status updates.
+ # === @params Hash
+ # - instanceId:
+ # === Notes:
+ # - camel casing is used on the keys because most other languages prefer
+ # that and it's not a huge problem in ruby. Besides, there's some other
+ # handy methods in this module to get you through those issues, like
+ # `#to_snake()` and or `#symbolize_keys_with()`
def update_message_body(opts = {})
- # TODO: find better implementation of merging nested hashes
- # this should be fixed with Job #sitrep_message
- # TODO: find a way to trim this method down and get rid
- # of a lof of the repitition with these messages
- # IDEA: throw events and have a separate thread listening. the separate
- # thread could be a communication or status update thread
+ # TODO: Better implementation of merging message bodies and config needed
unless opts.kind_of? Hash
update = opts.to_s
opts[:extraInfo] = { message: update }
end
updated_extra_info = opts.delete(:extraInfo) || {}