Module: CommandSearch::Normalizer

Defined in:
lib/command_search/normalizer.rb

Class Method Summary collapse

Class Method Details

.cast_bool!(field, node) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/command_search/normalizer.rb', line 7

def cast_bool!(field, node)
  type = field.is_a?(Hash) ? field[:type] : field
  if type == Boolean
    return if field.is_a?(Hash) && field[:general_search] && !node[:value][/\Atrue\Z|\Afalse\Z/i]
    node[:type] = Boolean
    node[:value] = !!node[:value][0][/t/i]
    return
  end
  return unless field.is_a?(Hash) && field[:allow_existence_boolean]
  return unless node[:type] == :str && node[:value][/\Atrue\Z|\Afalse\Z/i]
  node[:type] = :existence
  node[:value] = !!node[:value][0][/t/i]
end

.cast_numeric!(node) ⇒ Object



61
62
63
64
# File 'lib/command_search/normalizer.rb', line 61

def cast_numeric!(node)
  return unless node[:type] == :number
  node[:value] = node[:value].to_f
end

.cast_regex!(node) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/command_search/normalizer.rb', line 48

def cast_regex!(node)
  type = node[:type]
  raw = node[:value]
  return unless raw.is_a?(String)
  return if node[:value] == ''
  str = Regexp.escape(raw)
  return node[:value] = /#{str}/i unless type == :quote
  return node[:value] = /\b#{str}\b/ unless raw[/(\A\W)|(\W\Z)/]
  border_a = '(^|[^:+\w])'
  border_b = '($|[^:+\w])'
  node[:value] = Regexp.new(border_a + str + border_b)
end

.cast_time!(node) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/command_search/normalizer.rb', line 21

def cast_time!(node)
  search_node = node[:value][1]
  search_node[:type] = Time
  str = search_node[:value]
  if str == str.to_i.to_s
    search_node[:value] = [Time.new(str), Time.new(str.to_i + 1)]
  else
    time_str = str.tr('._-', ' ')
    times = Chronic.parse(time_str, { guess: nil })
    times ||= Chronic.parse(str, { guess: nil })
    if times
      search_node[:value] = [times.first, times.last]
    else
      search_node[:value] = nil
      return
    end
  end
  return unless node[:type] == :compare
  op = node[:nest_op]
  if op == '<' || op == '>='
    search_node[:value] = search_node[:value].first
  else
    search_node[:value] = search_node[:value].last
    search_node[:value] -= 1
  end
end

.clean_comparison!(node, fields) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/command_search/normalizer.rb', line 66

def clean_comparison!(node, fields)
  val = node[:value]
  return unless fields[val[1][:value].to_sym]
  if fields[val[0][:value].to_sym]
    node[:compare_across_fields] = true
    return
  end
  flip_ops = { '<' => '>', '>' => '<', '<=' => '>=', '>=' => '<=' }
  node[:nest_op] = flip_ops[node[:nest_op]]
  node[:value].reverse!
end

.dealias_key(key, fields) ⇒ Object



78
79
80
81
# File 'lib/command_search/normalizer.rb', line 78

def dealias_key(key, fields)
  key = fields[key.to_sym] while fields[key.to_sym].is_a?(Symbol)
  key
end

.normalize!(ast, fields, cast_all = true) ⇒ Object



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
# File 'lib/command_search/normalizer.rb', line 117

def normalize!(ast, fields, cast_all = true)
  ast.map! do |node|
    if node[:type] == :and || node[:type] == :or || node[:type] == :not
      normalize!(node[:value], fields, cast_all)
      next node
    end
    if node[:type] == :colon || node[:type] == :compare
      clean_comparison!(node, fields) if node[:type] == :compare
      key = dealias_key(node[:value][0][:value], fields)
      node[:value][0][:value] = key.to_s
      unless fields[key.to_sym] || fields[key.to_s]
        str_values = "#{key}#{node[:nest_op]}#{node[:value][1][:value]}"
        node = { type: :str, value: str_values }
      end
    end
    if node[:type] == :str || node[:type] == :quote || node[:type] == :number
      node = split_general_fields(node, fields)
    end
    if node[:type] == :or
      node[:value].each { |x| type_cast!(x, fields, cast_all) }
    else
      type_cast!(node, fields, cast_all)
    end
    node
  end
end

.split_general_fields(node, fields) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/command_search/normalizer.rb', line 83

def split_general_fields(node, fields)
  general_fields = fields.select { |k, v| v.is_a?(Hash) && v[:general_search] }.keys
  general_fields = ['__CommandSearch_dummy_key__'] if general_fields.empty?
  new_val = general_fields.map! do |field|
    {
      type: :colon,
      value: [
        { value: field.to_s },
        { value: node[:value], type: node[:type] }
      ]
    }
  end
  return new_val.first if new_val.count < 2
  { type: :or, value: new_val }
end

.type_cast!(node, fields, cast_all) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/command_search/normalizer.rb', line 99

def type_cast!(node, fields, cast_all)
  (key_node, search_node) = node[:value]
  key = key_node[:value]
  field = fields[key.to_sym] || fields[key.to_s]
  return unless field
  type = field.is_a?(Class) ? field : field[:type]
  type = Numeric if type == Integer
  key_node[:field_type] = type
  cast_bool!(field, search_node)
  return cast_time!(node) if [Time, Date, DateTime].include?(type)
  return cast_numeric!(search_node) if Numeric == type
  if cast_all
    cast_regex!(search_node)
  else
    search_node[:type] = :str if search_node[:type] == :number
  end
end