Class: Marty::BaseRule

Inherits:
Base show all
Defined in:
app/models/marty/base_rule.rb

Direct Known Subclasses

DeloreanRule

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

get_final_attrs, get_struct_attrs, make_hash, make_openstruct, mcfly_pt

Methods inherited from ActiveRecord::Base

joins, old_joins

Class Method Details

.get_matches_(_pt, attrs, params) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/marty/base_rule.rb', line 142

def get_matches_(_pt, attrs, params)
  q = select('DISTINCT ON (name) *').where(attrs)

  h = guard_info

  params.each do |k, vraw|
    use_k = (h[k] && k) ||
            (h[k + '_array'] && k + '_array') ||
            (h[k + '_range'] && k + '_range')

    next unless use_k

    param_guard_info = h[use_k]

    multi = param_guard_info[:multi]
    type = param_guard_info[:type]
    with_not = param_guard_info.fetch(:allow_not, true)

    filts = [vraw].flatten.map do |v|
      qstr = get_subq('simple_guards', use_k, multi, type, v)
    end.join(' OR ')

    condition = if with_not
                  "CASE
                   WHEN (simple_guards_options->'#{use_k}'->>'not')::boolean
                   THEN simple_guards->'#{use_k}' IS NULL OR NOT #{filts}
                   ELSE simple_guards->'#{use_k}' IS NULL OR #{filts}
                   END
                  "
                else
                  "simple_guards->'#{use_k}' IS NULL OR #{filts}"
                end

    q = q.where(condition)
  end
  # print q.to_sql
  q.order(:name)
end

.get_subq(field, subfield, multi, type, vraw) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
# File 'app/models/marty/base_rule.rb', line 129

def get_subq(field, subfield, multi, type, vraw)
  arrow = multi || ![:range, :string, :date, :datetime].include?(type) ?
            '->' : '->>'
  op = multi || type == :range ? '@>' : '='
  value0 = [:string, :date, :datetime].include?(type) ?
             ActiveRecord::Base.connection.quote(vraw) :
             type == :range ? vraw.to_f :
               "'#{vraw}'::jsonb"
  value = multi ? %Q('["%s"]') % value0[1..-2] : value0
  fieldcast = type == :range ? '::numrange' : ''
  "(#{field}#{arrow}'#{subfield}')#{fieldcast} #{op} #{value}"
end

.guard_infoObject



5
6
7
# File 'app/models/marty/base_rule.rb', line 5

def self.guard_info
  {}
end

Instance Method Details

#check(name, h) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/models/marty/base_rule.rb', line 25

def check(name, h)
  multi, type, enum, values, req = h.values_at(:multi, :type, :enum, :values,
                                               :required)
  ns = "'#{name}'"
  expmulti = multi ? 'multi' : 'single'
  errtype = :guards
  v = simple_guards[name]
  type ||= :string
  return errors[errtype] << "- Required field #{ns} is missing" if
    v.blank? && req
  return if v.blank?

  gotmulti = v.is_a?(Array) ? 'multi' : 'single'
  return errors[errtype] << "- Wrong arity for #{ns} (expected #{expmulti} "\
                            "got #{gotmulti})" if expmulti != gotmulti
  vs = [v].flatten.to_set
  vs.each do |vv|
    return errors[errtype] << "- Wrong type for #{ns}" unless
      gettypes(vv).member?(type)
  end

  return unless enum || values

  vals = enum&.values || values.to_set
  bad = (vs - vals)
  p = bad.count > 1 ? 's' : ''
  return errors[errtype] <<
         %Q(- Bad value#{p} '#{bad.to_a.join("', '")}' for #{ns}) if bad.present?
end

#chkrange(v) ⇒ Object



9
10
11
# File 'app/models/marty/base_rule.rb', line 9

def chkrange(v)
  v.match(/\A(\[|\()([0-9\.-]*),([0-9\.-]*)(\]|\))\z/)
end

#gettypes(v) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
# File 'app/models/marty/base_rule.rb', line 13

def gettypes(v)
  types = []
  types << :string if v.is_a?(String)
  types += [:int, :integer] if Integer(v) rescue nil
  types << :float if Float(v) rescue nil
  types << :date if Date.parse(v) rescue nil
  types << :datetime if DateTime.parse(v) rescue nil
  types << :range if chkrange(v) rescue nil
  types << :boolean if [true, false, 'True', 'False'].include?(v)
  types
end

#validateObject



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/models/marty/base_rule.rb', line 55

def validate
  self.class.guard_info.each { |name, h| check(name, h) }

  grids.each do |vn, gn|
    return errors[:grids] << "- Bad grid name '#{gn}' for '#{vn}'" unless
      gn.blank? || Marty::DataGrid.lookup('infinity', gn)
  end

  cg_err = computed_guards.delete('~~ERROR~~')
  errors[:computed] <<
    "- Error in rule '#{name}' field 'computed_guards': #{cg_err.capitalize}" if cg_err

  res_err = results.delete('~~ERROR~~')
  errors[:computed] <<
    "- Error in rule '#{name}' field 'results': #{res_err.capitalize}" if res_err
end