Module: HashConditions::Core

Included in:
Matcher, Parser
Defined in:
lib/hash_conditions/core.rb

Constant Summary collapse

DEBUG =

Modular Matchers

false
PRECISION =
5
ARITMETIC_OPERATORS =
{
  '$add' => :+,
  '$substract' => :-,
  '$multiply' => :*,
  '$divide' => :/,
}

Instance Method Summary collapse

Instance Method Details

#_ext_get_module(key, condition, options) ⇒ Object



215
216
217
218
219
# File 'lib/hash_conditions/core.rb', line 215

def _ext_get_module key, condition, options
  modules.select{ |m| m.for_operation? options[:operation] }.find do | matcher |
    matcher.apply_for key, condition
  end
end

#_ext_match(condition, options) ⇒ Object



197
198
199
# File 'lib/hash_conditions/core.rb', line 197

def _ext_match condition, options
  ->(key) { _ext_get_module( key, condition, options ) != nil }
end

#_ext_parse(expression, options) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/hash_conditions/core.rb', line 201

def _ext_parse expression, options
  key, op, value = expression.values_at :key, :operator, :value
  condition = get_condition_from_expression expression

  mod = _ext_get_module key,condition, options
  parser = mod.replacement

  case parser
    when String then options[:result].call(extract_expression( parser, condition ), options)
    when Hash   then _ext_read_module( { '$eval' => [ parser, op, value ] }, options )
    when Proc   then _ext_read_module( parser.call( key, condition, options ), options )
  end
end

#_ext_read_module(parser_output, options) ⇒ Object



221
222
223
224
225
226
227
228
# File 'lib/hash_conditions/core.rb', line 221

def _ext_read_module parser_output, options
  case parser_output
    when Hash   then iterator(parser_output, options)
    when NilClass then nil
    when String then parser_output # this should be removed, since the matcher will not support it
    else raise "return data of type #{ parser_output.class } not supported"
  end
end

#add_bundle(name) ⇒ Object



29
30
31
# File 'lib/hash_conditions/core.rb', line 29

def add_bundle name
  bundles << name
end

#bundlesObject



25
26
27
# File 'lib/hash_conditions/core.rb', line 25

def bundles
  @@bundles ||= []
end

#contains_bundle(name) ⇒ Object



32
33
34
# File 'lib/hash_conditions/core.rb', line 32

def contains_bundle name
  bundles.include? name
end

#eval_expression(value) ⇒ Object



72
73
74
75
76
77
78
# File 'lib/hash_conditions/core.rb', line 72

def eval_expression value
  raise "Invalid eval expression" unless value.is_a? Array and value.length == 3

  Hash[ [ :key, :operator, :value ].zip( value ) ].tap do | exp |
    exp[ :operator ] = get_op exp[ :operator ]
  end
end

#eval_operand(hash, key, options = {}) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/hash_conditions/core.rb', line 163

def eval_operand hash, key, options = {}
  __get_values = lambda do | values |
    values.map{ |x| eval_operand hash, x, options }
  end
  case key
    when String, Symbol
      if key.to_s == '$now'
        options[:current_time] || Time.now
      else
        val = options[:is_key] ? get_key( hash, key ) : key
        re_type val
      end
    when Array
      __get_values.call( key )
    when Hash
      op, values = key.to_a.first

      case op.to_s
        when *ARITMETIC_OPERATORS.keys
          #TODO: Test feature: when applying aritmetics it forces that the values are floats
          __get_values.call( values ).each{ |v| v.to_f }.inject( ARITMETIC_OPERATORS[ op ] )
        when '$ifNull'
          __get_values.call( values ).drop_while{ |n| n.nil? }.shift
        when '$concat'
          __get_values.call( values ).join('')
        when '$concatWithSeparator'
          separator = values[0]
          __get_values.call( values[1..-1] ).join( separator )
      end
    else
      key
  end
end

#extract_expression(key, value) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/hash_conditions/core.rb', line 80

def extract_expression key, value
  result = {}
  result[:key] = key

  case value
    when String, Integer, Float
      result[:operator] = :==
      result[:value] = value
    when Array
      result[:operator] = :in
      result[:value] = value
    when Hash
      if value.length == 1
        key, value = value.to_a.first

        result[:operator] = get_op key
        result[:value] = value
      else
        case
          when value.keys.include?('$between')
            result[:operator] = :between
            result[:value] = value.values_at [ '$between', '$and' ]
        end
      end
  end

  result[:value] = re_type result[:value]

  result
end

#get_condition_from_expression(expression) ⇒ Object



111
112
113
114
115
# File 'lib/hash_conditions/core.rb', line 111

def get_condition_from_expression expression
  {}.tap do | condition |
    condition[ rev_op( expression[:operator] ) ] = expression[:value]
  end
end

#get_key(hash, key) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/hash_conditions/core.rb', line 150

def get_key hash, key
  if hash.has_key? key
    hash[ key ]
  else
    key_type = key.class
    key.to_s.split('.').inject(hash) do |ret, k|
      typed_key = key_type == Symbol ? k.to_sym : k
      raise KeyNotFound, "key #{key} not found" unless ret.has_key? typed_key
      ret = ret[ typed_key ]
    end
  end
end

#get_op(key) ⇒ Object



134
135
136
137
# File 'lib/hash_conditions/core.rb', line 134

def get_op key
  pair = ops.find{ |k, v| Array(v).include? key }
  pair && pair.first or key
end

#iterator(conditions, options = {}) ⇒ Object

Iterator



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/hash_conditions/core.rb', line 43

def iterator conditions, options = {}
  options = { glue: :and }.merge options

  result = case conditions
    when ::Hash
      conditions.map do | key, value |
        case key
          when '$and' then iterator(value, options.merge( glue: :and ))
          when '$or' then iterator(value, options.merge( glue: :or))
          when '$eval' then results_from_expression( eval_expression( value ), options )
          when _ext_match( value, options ) then _ext_parse( extract_expression(key, value) , options )
          else results_from_expression( extract_expression( key, value ), options )
        end
      end
    when ::Array
      conditions.map do | condition |
        iterator condition, options
      end
    else
      raise "Unexpected expression found: #{ conditions }"
  end

  options[:finalize].call result, options
end

#log(*args) ⇒ Object



230
231
232
# File 'lib/hash_conditions/core.rb', line 230

def log *args
  puts args.map(&:to_s).join(" ") if(DEBUG)
end

#module_match(matcher, writer = nil, operations = [], &block) ⇒ Object



36
37
38
39
# File 'lib/hash_conditions/core.rb', line 36

def module_match matcher, writer = nil, operations = [], &block
  writer = block if block
  modules << ModuleMatcher.new(matcher, writer, operations)
end

#modulesObject



17
18
19
# File 'lib/hash_conditions/core.rb', line 17

def modules
  @@modules ||= []
end

#opsObject



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/hash_conditions/core.rb', line 117

def ops
  {
    :==       => [ '$eq', '$equal' ],
    :!=       => [ '$ne', '$not_equal' ],
    :>        => [ '$gt' ],
    :<        => [ '$lt' ],
    :>=       => [ '$gte' ],
    :<=       => [ '$lte' ],
    :between  => [ '$between' ],
    :in       => [ "$in" ],
    :contains => [ "$contains" ]
  }
end

#re_type(data) ⇒ Object



139
140
141
142
143
144
145
146
147
148
# File 'lib/hash_conditions/core.rb', line 139

def re_type data
  if data.is_a? String
    case data
      when /\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/ then DateTime.parse( data ).to_time
      else data
    end
  else
    data
  end
end

#resetObject



21
22
23
# File 'lib/hash_conditions/core.rb', line 21

def reset
  @@modules = []
end

#results_from_expression(expression, options) ⇒ Object



68
69
70
# File 'lib/hash_conditions/core.rb', line 68

def results_from_expression expression, options
  options[:result].call( expression, options )
end

#rev_op(key) ⇒ Object



131
132
133
# File 'lib/hash_conditions/core.rb', line 131

def rev_op key
  ops[ key ] && ops[ key ].first or key
end