Module: Virginity::Query

Included in:
DirectoryInformation
Defined in:
lib/virginity/dir_info/query.rb

Overview

Helper methods to perform queries on lines in a DirectoryInformation instance

query format: [group.][name][:value]

examples:

  • “FN” searches for FN-fields

  • “FN:John Smith” searches for an FN-field with the value “John Smith”

  • “:John Smith” searches for any field with the value “John Smith”

  • “item1.:John Smith” searches for any field with the value “John Smith” and group item1

Defined Under Namespace

Classes: InvalidQuery

Constant Summary collapse

GROUP =
/#{Bnf::NAME}\./
NAME =
/#{Bnf::NAME}/
SEMICOLON =
/\;/
COLON =
/\:/
COMMA =
/,/
EQUALS =
/=/
KEY =
/#{Bnf::NAME}/
PARAM_NAME =
/((X|x)\-)?(\w|\-)+/
PARAM_VALUE =
/#{Bnf::PVALUE}/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.decode_query(query) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/virginity/dir_info/query.rb', line 22

def self.decode_query(query)
  scanner = StringScanner.new(query)
  # does the query start with a name that ends in a dot?
  if group = scanner.scan(GROUP)
    group.chomp!(".")
  end
  name = scanner.scan(NAME) # could be nil
  params = params(scanner)
  value = nil
  if scanner.skip(COLON)
    value = scanner.rest
  end
  [group, name, params, value]
end

.params(scanner) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/virginity/dir_info/query.rb', line 42

def self.params(scanner)
  return nil unless scanner.skip(SEMICOLON)
  params = []
  until scanner.check(COLON) or scanner.eos?  # <--- check of end of string! and *check* for colon
    key = scanner.scan(PARAM_NAME)
    raise InvalidQuery unless scanner.skip(EQUALS)
    begin
      if value = scanner.scan(PARAM_VALUE)
        params << Param.new(key, Param::decode_value(value))
      end
      break if scanner.skip(SEMICOLON) # after a semicolon, we expect key=(value)+
    end until scanner.skip(COMMA).nil? # a comma indicates another value (TYPE=HOME,WORK)
  end
  params
end

Instance Method Details

#find_first(query = {}) ⇒ Object



136
137
138
139
140
# File 'lib/virginity/dir_info/query.rb', line 136

def find_first(query = {})
  lines.detect do |line|
    query.all? { |q,v| line_matches_query?(line, q, v) }
  end
end

#first_match(query = "") ⇒ Object

return the first match for query q. By definition this is equivalent to query(q).first but it is faster.



70
71
72
73
74
75
76
77
78
79
# File 'lib/virginity/dir_info/query.rb', line 70

def first_match(query = "")
  group, name, params, value = Query::decode_query(query)
  lines.detect do |line|
    # to_s is used here to match a nil-group to the empty-group-query: "."
    (group.nil? || group == line.group.to_s) &&
    (params.nil? || params.all? { |p| line.params.include?(p) }) &&
    (name.nil? || line.has_name?(name)) &&
    (value.nil? || value == line.raw_value)
  end
end

#line_matches_query?(line, q, v) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/virginity/dir_info/query.rb', line 87

def line_matches_query?(line, q, v)
  raise ArgumentError, "query cannot be nil { #{q.inspect} => #{v.inspect} }?" if v.nil?
  case q
  when :name
    line.has_name?(v)
  when :raw_value
    v == line.raw_value
  when :text
    return false unless line.respond_to? :text
    v == line.text
  when :sha1
    return false unless line.respond_to? :sha1
    v == line.sha1
  when :group
    v == line.group
  when :values
    raise ArgumentError, "expected an array of values { #{q.inspect} => #{v.inspect} }?" unless v.is_a? Array
    if line.respond_to? :values
      if line.respond_to? :components # stuctured text, values are ordered and the lenght of the array is guaranteed to be correct.
        line.values == v
      else
        (line.values - v).empty?
      end
    else
      false
    end
  when :has_param
    # true if one param matches the array we feed it
    line.params.include? Param.new(v.first, v.last)
  when :has_param_with_key
    # true if one param matches the array we feed it
    line.params.any? { |p| p.has_key?(v) }
  when :has_type
    # true if one param matches the array we feed it
    line.params.include? Param.new('TYPE', v)
  when :params
    # all params match v, and no param is not matching
    raise NotImplementedError
  else
    raise ArgumentError, "what do you expect me to do with { #{q.inspect} => #{v.inspect} }?"
  end
end

#lines_with_name(name = "") ⇒ Object



81
82
83
84
85
# File 'lib/virginity/dir_info/query.rb', line 81

def lines_with_name(name = "")
  lines.select do |line|
    line.has_name?(name)
  end
end

#query(query = "") ⇒ Object Also known as: /



58
59
60
61
62
63
64
65
66
67
# File 'lib/virginity/dir_info/query.rb', line 58

def query(query = "")
  group, name, params, value = Query::decode_query(query)
  lines.select do |line|
    # to_s is used here to match a nil-group to the empty-group-query: "."
    (group.nil? || group == line.group.to_s) &&
    (params.nil? || params.all? { |p| line.params.include? p }) &&
    (name.nil? || line.has_name?(name)) &&
    (value.nil? || value == line.raw_value)
  end
end

#where(query = {}) ⇒ Object



130
131
132
133
134
# File 'lib/virginity/dir_info/query.rb', line 130

def where(query = {})
  lines.select do |line|
    query.all? { |q,v| line_matches_query?(line, q, v) }
  end
end