class MSFL::Converters::Operator

Constants

CONVERSIONS

Order is respected by run_conversions in otherwords run_conversions executes conversions in the order they occur in CONVERSIONS, not in the order in which they are passed into the method

conversion order is context-free

Public Instance Methods

between_to_gte_lte_recursively(obj) click to toggle source

Recursively converts all between operators to equivalent anded gte / lte it currently creates the converted operators into the implied AND format

@param obj [Object] the object to recurse through to convert all betweens to gte / ltes @return [Object] the object with betweens converted to anded gte / ltes

# File lib/msfl/converters/operator.rb, line 70
def between_to_gte_lte_recursively(obj)
  result = obj
  if obj.is_a? Hash
    obj.each do |k, v|
      if v.is_a?(Hash) && v.has_key?(:between) && v[:between].has_key?(:start) && v[:between].has_key?(:end)
        lower_bound = between_to_gte_lte_recursively v[:between][:start]
        upper_bound = between_to_gte_lte_recursively v[:between][:end]
        result[k] = { gte: lower_bound, lte: upper_bound  }
      else
        result[k] = between_to_gte_lte_recursively v
      end
    end
  elsif obj.is_a? Types::Set
    result = recurse_through_set :between_to_gte_lte_recursively, obj
  elsif obj.is_a? Array
    raise ArgumentError, "#between_to_gte_lte requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
  end
  result
end
implicit_and_to_explicit_recursively(obj, parent_key = nil) click to toggle source

Convert a Hash containing an implict and into an explicit and

TYPE 1 —

{ make: "chevy", year: 2010 }
 =>    { and: [ { make: "chevy" }, { year: 2010 }] }

TYPE 2 —

{ year: { gte: 2010, lte: 2012 } }
 => { and: [ { year: { gte: 2010 } }, { year: { lte: 2012 } } ] }

TYPE 3 —

{ make: "chevy", year: { gte: 2010, lte: 2012 } }
 => { and: [ { make: "chevy" }, { and: [ { year: { gte: 2010 } }, { year: { lte: 2012 } } ] } ] }

@param obj [Object] the Hash that is an implicit and @return [Hash] the resulting explicit hash

# File lib/msfl/converters/operator.rb, line 105
def implicit_and_to_explicit_recursively(obj, parent_key = nil)
  if obj.is_a? Hash
    result = i_to_e_rec_hash obj, parent_key
  elsif obj.is_a? MSFL::Types::Set
    result = i_to_e_set obj, parent_key
  elsif obj.is_a? Array
    raise ArgumentError, "#implicit_and_to_explicit requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
  end
  result ||= obj
end
implicit_between_to_explicit_recursively(obj, parent_key = nil) click to toggle source

{ year: { start: 2001, end: 2005 } }

=> { year: { between: { start: 2001, end: 2015 } } }
# File lib/msfl/converters/operator.rb, line 40
def implicit_between_to_explicit_recursively(obj, parent_key = nil)
  if parent_key == :between
    # The immediately ancestor key is :between, so don't do anything special with :start and :end
    result = Hash.new
    obj.each do |k, v|
      result[k] = implicit_between_to_explicit_recursively(v)
    end
  elsif obj.is_a? Hash
    # if the hash has two keys :start and :end, nest it inside a between and recurse on the values
    if obj.has_key?(:start) && obj.has_key?(:end)
      result = { between: { start: implicit_between_to_explicit_recursively(obj[:start]), end: implicit_between_to_explicit_recursively(obj[:end]) } }
    else
      result = Hash.new
      obj.each do |k, v|
        result[k] = implicit_between_to_explicit_recursively(v, k)
      end
    end
  elsif obj.is_a? Types::Set
    result = recurse_through_set :implicit_between_to_explicit_recursively, obj
  elsif obj.is_a? Array
    raise ArgumentError, "#implicit_between_to_explicit_recursively requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
  end
  result ||= obj
end
run_conversions(obj, conversions_to_run = nil) click to toggle source

Runs conversions on an object It respects the order of CONVERSIONS, not the order of elements in conversions_to_run

@param obj [Object] the object to run the conversions on @param conversions_to_run [Array<Symbol>] an array of the conversions that should be run, duplicates are ignored @return [Object] the object with the conversions applied

# File lib/msfl/converters/operator.rb, line 23
def run_conversions(obj, conversions_to_run = nil)
  conversions_to_run ||= CONVERSIONS
  unless all_conversions?(conversions_to_run)
    raise ArgumentError, "#run_conversions second argument is optional, if specified it must be an Array of Symbols"
  end
  result = obj
  CONVERSIONS.each do |conv|
    # In the order that items are in CONVERSIONS run all of the conversions_to_run
    result = send(conv, result) if conversions_to_run.include?(conv)
  end
  result
end

Private Instance Methods

all_conversions?(obj) click to toggle source

Returns true if the argument is an Array of Symbols, otherwise false

@param obj [Obj] the object to check @return [Bool] true if the argument is an Array of Symbols

# File lib/msfl/converters/operator.rb, line 231
def all_conversions?(obj)
  return false unless obj.is_a?(Array)
  obj.each do |v|
    return false unless v.is_a?(Symbol)
  end
  true
end
i_to_e_bin_op(hash, parent_key = nil) click to toggle source

Recursively handle a hash containing keys that are all binary operators

# File lib/msfl/converters/operator.rb, line 189
def i_to_e_bin_op(hash, parent_key = nil)
  # the first key an operator
  raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be operators" unless all_operators?(hash.keys)
  # all keys are operators
  first_key = hash.keys.first
  if hash.keys.count == 1
    # There's only one key so there cannot be an implied AND at this level
    if parent_key && (! binary_operators.include?(parent_key)) # this needs more testing - I'm not entirely sure if I should check for this esoteric case of immediately nested explicit ANDs inside of an implied AND
      # The parent_key argument was provided which means that the caller expects the result to be a hash of at least two levels
      # where the first level has a key of the parent_key with a value of a hash
      # first_key is passed in the recursive call
      { parent_key => { first_key => implicit_and_to_explicit_recursively(hash[first_key], first_key)}}
    else
      { first_key => implicit_and_to_explicit_recursively(hash[first_key]) }
    end
  else
    raise ArgumentError, "#implicit_and_to_explicit requires that parent_key be specified when converting operators" if parent_key.nil?
    # parent key is non nil
    and_array = []
    hash.each do |k, v|
      and_array << { parent_key => { k => implicit_and_to_explicit_recursively(v, k) } }
    end
    { and: MSFL::Types::Set.new(and_array) }
  end
end
i_to_e_log_op(hash, parent_key = nil) click to toggle source

Recursively handle a hash containing keys that are all logical operators

# File lib/msfl/converters/operator.rb, line 179
def i_to_e_log_op(hash, parent_key = nil)
  raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be logical operators" unless all_logical_operators?(hash.keys)
  result = {}
  hash.each do |key, value|
    result[key] = implicit_and_to_explicit_recursively value
  end
  result
end
i_to_e_op(hash, parent_key = nil) click to toggle source
# File lib/msfl/converters/operator.rb, line 160
def i_to_e_op(hash, parent_key = nil)
  op_key = hash.keys.first

  if binary_operators.include?(op_key)
    i_to_e_bin_op hash, parent_key
  elsif logical_operators.include?(op_key)
    i_to_e_log_op hash, parent_key
  elsif op_key == :partial
    { partial: { given: implicit_and_to_explicit_recursively(hash[:partial][:given]),
                 filter: implicit_and_to_explicit_recursively(hash[:partial][:filter]) } }
  elsif op_key == :foreign
    { foreign: { dataset: implicit_and_to_explicit_recursively(hash[:foreign][:dataset]),
                 filter: implicit_and_to_explicit_recursively(hash[:foreign][:filter]) } }
  else
    fail ArgumentError, "Unsupported hash in private method #i_to_e_rec_hash_op_key"
  end
end
i_to_e_rec_hash(hash, parent_key = nil) click to toggle source

Convert a Hash containing an implicit AND to an explicit AND This is a helper method used by implicit_and_to_explicit_recursively

@param hash [Hash] the Hash to be converted @param parent_key [Symbol] the parent key of the hash to be converted, or nil if one does not exist @return [Hash] the resulting hash with implicit ANDs converted to explicit ones

# File lib/msfl/converters/operator.rb, line 124
def i_to_e_rec_hash(hash, parent_key = nil)
  first_key = hash.keys.first
  if operator? first_key
    result = i_to_e_op hash, parent_key
  else
    # the first key is not an operator
    # if there is only one key just assign the result of calling this method recursively on the value to the result for the key
    if hash.keys.count == 1
      if hash[first_key].is_a?(Hash)
        result = implicit_and_to_explicit_recursively hash[first_key], first_key
      elsif hash[first_key].is_a? MSFL::Types::Set
        # When an implicit and occurs under an MSFL::Types::Set when the key for the value which is the set
        # is not a binary or logical operator. This doesn't happen in known current cases.
        # ex. => { foo: [ { bar: { gte: 1, lte: 5 } } ] }
        result = Hash.new
        result[first_key] = recurse_through_set :implicit_and_to_explicit_recursively, hash[first_key]
      elsif hash[first_key].is_a? Array
        raise ArgumentError, "#implicit_and_to_explicit requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
      end
    else
      raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be operators" if any_operators?(hash.keys)
      # none of the keys are operators
      and_array = []
      hash.each do |k, v|
        if v.is_a? Hash
          and_array << implicit_and_to_explicit_recursively(v, k)
        else
          and_array << { k => v }
        end
      end
      result = { and: MSFL::Types::Set.new(and_array) }
    end
  end
  result
end
i_to_e_set(set, parent_key = nil) click to toggle source
# File lib/msfl/converters/operator.rb, line 215
def i_to_e_set(set, parent_key = nil)
  recurse_through_set :implicit_and_to_explicit_recursively, set
end
recurse_through_set(method, set) click to toggle source
# File lib/msfl/converters/operator.rb, line 219
def recurse_through_set(method, set)
  result = MSFL::Types::Set.new
  set.each do |v|
    result << send(method, v)
  end
  result
end