Class: MSFL::Converters::Operator

Inherits:
Object
  • Object
show all
Includes:
Validators::Definitions::HashKey
Defined in:
lib/msfl/converters/operator.rb

Constant Summary collapse

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

[
    :implicit_between_to_explicit_recursively,
    :between_to_gte_lte_recursively,
    :implicit_and_to_explicit_recursively
]

Instance Method Summary collapse

Methods included from Validators::Definitions::HashKey

#all_logical_operators?, #all_operators?, #any_operators?, #binary_operators, #hash_key_operators, #logical_operators, #valid_hash_key?, #valid_hash_keys

Instance Method Details

#between_to_gte_lte_recursively(obj) ⇒ Object

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

Parameters:

  • obj (Object)

    the object to recurse through to convert all betweens to gte / ltes

Returns:

  • (Object)

    the object with betweens converted to anded gte / ltes



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 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) ⇒ Hash

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 } } ] } ] }

Parameters:

  • obj (Object)

    the Hash that is an implicit and

Returns:

  • (Hash)

    the resulting explicit hash



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
143
144
145
146
147
# File 'lib/msfl/converters/operator.rb', line 105

def implicit_and_to_explicit_recursively(obj, parent_key = nil)
  if obj.is_a? Hash
    first_key = obj.keys.first
    if binary_operators.include?(first_key)
      result = i_to_e_bin_op obj, parent_key
    elsif logical_operators.include?(first_key)
      result = i_to_e_log_op obj, 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 obj.keys.count == 1
        if obj[first_key].is_a?(Hash)
          result = implicit_and_to_explicit_recursively obj[first_key], first_key
        elsif obj[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, obj[first_key]
        elsif obj[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?(obj.keys)
        # none of the keys are operators
        and_array = []
        obj.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
  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) ⇒ Object

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

=> { year: { between: { start: 2001, end: 2015 } } }


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 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) ⇒ Object

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

Parameters:

  • obj (Object)

    the object to run the conversions on

  • conversions_to_run (Array<Symbol>) (defaults to: nil)

    an array of the conversions that should be run, duplicates are ignored

Returns:

  • (Object)

    the object with the conversions applied



23
24
25
26
27
28
29
30
31
32
33
34
# 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