module Eco::Language

Public Class Methods

test_values_at() click to toggle source
# File lib/eco/language/values_at.rb, line 142
def self.test_values_at
  h = {'pops' => 'aghr!', pops: 5, 'nops' => 'nope!'}
  a = Test::A.new('hello!')
  b = Test::B.new('bye')
  b.c = 'cheese'

  pp Handy.values_at(h, 'nops', :pops)
  pp Handy.values_at(a, 'rops', 'mops', rops: 'EVERY', xops: ['Dear', xop: 'SOMETHING BETTER'])
  pp Handy.values_at(a, 'mops', 'gops', rops: 'EVERY THING')

  pp Handy.values_at_dot(b, 'c', 'bofs' => ['stranberry', v2: 'combines'], 'a.rops': 'DOTTED CALL')
  pp "+" * 60
  pp Handy.values_at_dot(b, 'c', 'bofs' => ['stranberry', v2: 'combines'], debug: true, 'a.rops': 'DOTTED CALL')
  pp "+" * 60
end

Public Instance Methods

curry(method) click to toggle source
# File lib/eco/language/curry.rb, line 12
def curry(method)
  -> (*args, **kargs) {
    required  = method.parameters.select { |type, _| type == :req }
    krequired = method.parameters.select { |type, _| type == :keyreq }
    all_args = (required.length <= args.length)
    all_keys = krequired.all? { |_, name| kargs.has_key?(name) }
    if all_args && all_keys
      final_args = (args + kargs.map {|k,v| {k => v} })
      method.call(*final_args)
    else
      -> (*args_, **kargs_) { curry(method)[*args, *args_, **kargs, **kargs_] }
    end
  }
end
get_accessor(object, attr, accessors: []) click to toggle source

adapted from: stackoverflow.com/a/16908153/4352306

# File lib/eco/language/values_at.rb, line 35
def get_accessor(object, attr, accessors: [])
  accessors.push( ->(obj, att) { object.method(att) } )
  #accessors.push( ->(obj, att) { object[att] } )
  accessors.reduce(nil) { |chosen, acc| chosen ? chosen :
      (acc[object, attr] && acc rescue nil) || chosen }
  #accessors.reduce(nil) { |chosen, acc|
  #    puts "attr: #{attr} => current: #{acc}"
  #    chosen ? chosen : (acc[object, attr] && acc rescue nil) || chosen
  #    puts "attr: #{attr} => after check - chosen: #{chosen}"
  #    chosen
  #  }
end
hash_transform(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 25
def hash_transform(hash, mode = HashTransformModifier.new)
  hash = (mode.clone?) ? Marshal.load(Marshal.dump(hash)) : hash
  hash.keys.each do |key|
    value = (mode.keys?) ? hash.delete(key) : hash[key]
    value = (value.is_a?(Hash) && mode.recurse?) ? hash_transform(value, mode) : value
    value = sym_str_transform(value, mode) if mode.values?
    key   = sym_str_transform(key,   mode) if mode.keys?
    hash[key] = value
  end
  hash
end
match?(value, at, mode = MatchModifier.new, depth: 0) click to toggle source
# File lib/eco/language/match.rb, line 4
def match?(value, at, mode = MatchModifier.new, depth: 0)
  out_match  = ->(v) { match?(v,    at, mode, depth: depth + 1)         }
  in_match   = ->(a) { match?(value, a, mode, depth: depth + 1)         }
  indent_msg = ->(m) { puts ("  " * depth) + m if mode.debug?   }

  case
  when mode.reverse?
    indent_msg.call("reverse value #{value} <--> at: #{at}")
    match?(at   , value, mode.new.reset_reverse, depth: depth + 1)
  when mode.pattern?
    indent_msg.call("at to pattern: #{at}")
    at = mode.to_regex(at)
    match?(value,    at, mode.new.reset_pattern, depth: depth + 1)
  when value.is_a?(Array)
    indent_msg.call("array #{value} value.#{mode.any?? "any?" : "all?"} match(v_item, at) at: #{at}")
    return value.any?(&out_match) if mode.any?
    value.all?(&out_match) # defaults to EVERY
  when at.is_a?(Array)
    indent_msg.call("array #{at} at.#{mode.and?? "all?" : "any?"} match(value, at_item). value: #{value}")
    return at.all?(&in_match) if mode.and?
    at.any?(&in_match) # defaullts to OR
  when at.is_a?(Regexp)
    indent_msg.call("(#{at.inspect}) at.match?(value); value: #{value}")
    at.match?(value)
  when value.is_a?(Regexp)
    indent_msg.call("(#{value.inspect}) value.match?(at); at: #{at}")
    value.match?(at)
  else # final compare
    indent_msg.call("-- final -- mode: #{mode.to_a}; value: #{value}; at: #{at}")
    m = (value == at) ||
      (mode.insensitive? && at&.downcase == value&.downcase)
    (mode.not?) ? !m : m
  end
end
required_parameters(method) click to toggle source
# File lib/eco/language/curry.rb, line 4
def required_parameters(method)
  method.parameters.select { |type, _| type == :req || :keyreq}
end
required_parameters?(method) click to toggle source
# File lib/eco/language/curry.rb, line 8
def required_parameters?(method)
  required_parameters(method).length > 0
end
stringify_hash(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 62
def stringify_hash(hash, mode = HashTransformModifier.new)
  mode = mode.new.stringify.keys.values
  hash_transform(hash, mode)
end
stringify_keys(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 42
def stringify_keys(hash, mode = HashTransformModifier.new)
  mode = mode.new.stringify.keys
  hash_transform(hash, mode)
end
stringify_values(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 52
def stringify_values(hash, mode = HashTransformModifier.new)
  mode = mode.new.stringify.values
  hash_transform(hash, mode)
end
sym_str_transform(value, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 4
def sym_str_transform(value, mode = HashTransformModifier.new)
  v = value
  if value && (value.is_a?(Symbol) || value.is_a?(String))
    if mode.symbolize? && !value.is_a?(Symbol)
      colon = mode.colon? && !value.include?(" ") && (
        starts = value.start_with?(":") ||
        ends   = value.end_with?(":")
      )
      if !mode.colon? || colon
        value = value.slice(1, value.length) if colon && starts
        value = value.slice(0, -1) if colon && ends
        v = (value.to_sym rescue value) || value
      end
    elsif !value.is_a?(String) # stringify
      v = (value.to_s rescue value) || value
      v = ":" + v if mode.colon?
    end
  end
  v
end
symbolize_hash(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 57
def symbolize_hash(hash, mode = HashTransformModifier.new)
  mode = mode.new.symbolize.keys.values
  hash_transform(hash, mode)
end
symbolize_keys(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 37
def symbolize_keys(hash, mode = HashTransformModifier.new)
  mode = mode.new.symbolize.keys
  hash_transform(hash, mode)
end
symbolize_values(hash, mode = HashTransformModifier.new) click to toggle source
# File lib/eco/language/hash_transform.rb, line 47
def symbolize_values(hash, mode = HashTransformModifier.new)
  mode = mode.new.symbolize.values
  hash_transform(hash, mode)
end
to_key(pr) click to toggle source
# File lib/eco/language/values_at.rb, line 4
def to_key(pr)
  pr.is_a?(Hash) ? pr.keys.first : pr
end
to_value(pr) click to toggle source
# File lib/eco/language/values_at.rb, line 8
def to_value(pr)
  pr.is_a?(Hash) ? pr.values.first : pr
end
values_at(object, *attrs, **kattrs) click to toggle source
# File lib/eco/language/values_at.rb, line 12
def values_at(object, *attrs, **kattrs)
  all_params = attrs + kattrs.map {|k,v| {k => v} }
  return object.values_at(*all_params) if object.is_a?(Hash)
  all_keys     = all_params.map        { |pr|   to_key(pr)  }
  arguments    = all_params.each_with_index.map { |pr, i| pr.values[0] if pr.is_a?(Hash)}
  #puts "all_keys: #{all_keys}; arguments: #{arguments}"
  values = all_keys.each_with_index.map do |k, i|
    a = k; v = []
    v = arguments[i] if all_params[i].is_a?(Hash) # with params
    attr_method = get_accessor(object, a)[object, a] rescue nil
    #puts "attr: #{a} - method: #{attr_method}"
    next if !attr_method
    args = v.is_a?(Array) ? v : [].push(v)
    attr_method = curry(attr_method) if required_parameters?(attr_method)
    # final call to the method
    #puts "args: #{args}"
    value = attr_method[*args] # rescue nil
    value = nil if value.is_a?(Proc)
    value
  end
end
values_at_dot(object, *attrs, curry: false, debug: false, **kattrs) click to toggle source

enables dot notation to do object nesting access

# File lib/eco/language/values_at.rb, line 49
def values_at_dot (object, *attrs, curry: false, debug: false, **kattrs)
  all_params = attrs + kattrs.map {|k,v| {k => v} }
  return object.values_at(*all_params) if object.is_a?(Hash)

  to_s    = ->(k)  { ((k.to_s   rescue k) || key)        }
  to_sym  = ->(k)  { ((k.to_sym rescue k) || key)        }
  dotted  = ->(k)  { to_s[k].include?('.')               }
  pdotted = ->(pr) { dotted[to_key(pr)]                  }
  haccess = ->(dp) { # prepare call
    k       = dp
    k, v    = [dp.keys.first, dp.values.first] if (hash = dp.is_a?(Hash))
    kf, kr  = [ (ss = to_s[k].split('.')).first , ss.slice(1)]
    nxt_cll = hash ? {kr => v} : kr
    {k => {'a' => kf, 'v' => nxt_cll }}
  }
  all_keys     = all_params.map        { |pr|   to_key(pr)           }
  hash_params  = all_params.reduce({}) { |h,pr| h.merge!(pr) if pr.is_a?(Hash); h }
  dot_params   = all_params.select     { |pr|   pdotted[pr]          }
  dot_keys     = dot_params.map        { |dp|   to_key(dp)           }
  dot_wrappers = dot_params.reduce({}) { |h,dp| h.merge(haccess[dp]) }

  if debug
    pp "object     : #{object}"
    pp "all_params: #{all_params}"
    pp "dot_keys   : #{dot_keys}"
  end

  values = all_keys.map do |k|
    a = k; v = []
    puts "key: #{k}" if debug
    case true
    when dot_keys.include?(k) # dotted (recursive)
      dot = dot_wrappers[k]
      a, v = dot.values_at('a', 'v')
    when hash_params.key?(k) # no dotted, with params
      v = hash_params[k]
    end

    attr_method = get_accessor(object, a)[object, a] rescue nil
    next if !attr_method

    args = v.is_a?(Array) ? v : [].push(v)
    attr_method = curry(attr_method) if required_parameters?(attr_method)
    if dot_keys.include?(k) # recurse (dotted)
      args.push({ curry: curry, debug: debug })
      value = values_at(attr_method.call, *args).first
    else # final call to the method
      value = attr_method[*args] # rescue nil
      puts "value: #{value}" if debug
    end
    value = nil if !curry && value.is_a?(Proc)
    value
  end

end