protocol-caldav 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/setup"
4
- require "scampi"
5
-
6
3
  require 'rexml/document'
7
4
  require "protocol/caldav"
8
5
 
@@ -16,37 +13,44 @@ module Protocol
16
13
  module_function
17
14
 
18
15
  def parse_calendar(xml_string)
19
- return nil if xml_string.nil? || xml_string.strip.empty?
20
-
21
- doc = REXML::Document.new(xml_string)
22
- ns = { 'c' => CALDAV_NS }
16
+ if xml_string.nil? || xml_string.strip.empty?
17
+ nil
18
+ else
19
+ doc = REXML::Document.new(xml_string)
20
+ ns = { 'c' => CALDAV_NS }
23
21
 
24
- filter_el = REXML::XPath.first(doc, './/c:filter', ns)
25
- return nil unless filter_el
22
+ filter_el = REXML::XPath.first(doc, './/c:filter', ns)
26
23
 
27
- comp_el = REXML::XPath.first(filter_el, 'c:comp-filter', ns)
28
- return nil unless comp_el
24
+ if filter_el
25
+ comp_el = REXML::XPath.first(filter_el, 'c:comp-filter', ns)
29
26
 
30
- parse_comp_filter(comp_el, ns)
27
+ if comp_el
28
+ parse_comp_filter(comp_el, ns)
29
+ end
30
+ end
31
+ end
31
32
  rescue REXML::ParseException => e
32
33
  raise ParseError, "Malformed XML: #{e.message}"
33
34
  end
34
35
 
35
36
  def parse_addressbook(xml_string)
36
- return nil if xml_string.nil? || xml_string.strip.empty?
37
+ if xml_string.nil? || xml_string.strip.empty?
38
+ nil
39
+ else
40
+ doc = REXML::Document.new(xml_string)
41
+ ns = { 'cr' => CARDDAV_NS }
37
42
 
38
- doc = REXML::Document.new(xml_string)
39
- ns = { 'cr' => CARDDAV_NS }
43
+ filter_el = REXML::XPath.first(doc, './/cr:filter', ns)
40
44
 
41
- filter_el = REXML::XPath.first(doc, './/cr:filter', ns)
42
- return nil unless filter_el
45
+ if filter_el
46
+ test = filter_el.attributes['test'] || 'anyof'
47
+ prop_filters = REXML::XPath.match(filter_el, 'cr:prop-filter', ns).map do |el|
48
+ parse_addressbook_prop_filter(el, ns)
49
+ end
43
50
 
44
- test = filter_el.attributes['test'] || 'anyof'
45
- prop_filters = REXML::XPath.match(filter_el, 'cr:prop-filter', ns).map do |el|
46
- parse_addressbook_prop_filter(el, ns)
51
+ Addressbook::Filter.new(test: test, prop_filters: prop_filters)
52
+ end
47
53
  end
48
-
49
- Addressbook::Filter.new(test: test, prop_filters: prop_filters)
50
54
  rescue REXML::ParseException => e
51
55
  raise ParseError, "Malformed XML: #{e.message}"
52
56
  end
@@ -55,7 +59,9 @@ module Protocol
55
59
 
56
60
  def parse_comp_filter(el, ns)
57
61
  name = el.attributes['name']
58
- raise ParseError, "comp-filter missing required 'name' attribute" unless name
62
+ unless name
63
+ raise ParseError, "comp-filter missing required 'name' attribute"
64
+ end
59
65
 
60
66
  is_not_defined = REXML::XPath.first(el, 'c:is-not-defined', ns) != nil
61
67
  time_range = parse_time_range(REXML::XPath.first(el, 'c:time-range', ns))
@@ -69,17 +75,19 @@ module Protocol
69
75
  end
70
76
 
71
77
  Calendar::CompFilter.new(
72
- name: name,
78
+ name: name,
73
79
  is_not_defined: is_not_defined,
74
- time_range: time_range,
75
- prop_filters: prop_filters,
76
- comp_filters: comp_filters
80
+ time_range: time_range,
81
+ prop_filters: prop_filters,
82
+ comp_filters: comp_filters,
77
83
  )
78
84
  end
79
85
 
80
86
  def parse_prop_filter(el, ns)
81
87
  name = el.attributes['name']
82
- raise ParseError, "prop-filter missing required 'name' attribute" unless name
88
+ unless name
89
+ raise ParseError, "prop-filter missing required 'name' attribute"
90
+ end
83
91
 
84
92
  is_not_defined = REXML::XPath.first(el, 'c:is-not-defined', ns) != nil
85
93
  time_range = parse_time_range(REXML::XPath.first(el, 'c:time-range', ns))
@@ -90,11 +98,11 @@ module Protocol
90
98
  end
91
99
 
92
100
  Calendar::PropFilter.new(
93
- name: name,
101
+ name: name,
94
102
  is_not_defined: is_not_defined,
95
- time_range: time_range,
96
- text_match: text_match,
97
- param_filters: param_filters
103
+ time_range: time_range,
104
+ text_match: text_match,
105
+ param_filters: param_filters,
98
106
  )
99
107
  end
100
108
 
@@ -107,37 +115,45 @@ module Protocol
107
115
  end
108
116
 
109
117
  def parse_text_match(el)
110
- return nil unless el
111
-
112
- Calendar::TextMatch.new(
113
- value: el.text&.strip || '',
114
- collation: el.attributes['collation'] || 'i;ascii-casemap',
115
- match_type: el.attributes['match-type'] || 'contains',
116
- negate_condition: el.attributes['negate-condition'] == 'yes'
117
- )
118
+ if el
119
+ Calendar::TextMatch.new(
120
+ value: el.text&.strip || '',
121
+ collation: el.attributes['collation'] || 'i;ascii-casemap',
122
+ match_type: el.attributes['match-type'] || 'contains',
123
+ negate_condition: el.attributes['negate-condition'] == 'yes',
124
+ )
125
+ else
126
+ nil
127
+ end
118
128
  end
119
129
 
120
130
  def parse_time_range(el)
121
- return nil unless el
122
-
123
- Calendar::TimeRange.new(
124
- start_time: el.attributes['start'],
125
- end_time: el.attributes['end']
126
- )
131
+ if el
132
+ Calendar::TimeRange.new(
133
+ start_time: el.attributes['start'],
134
+ end_time: el.attributes['end'],
135
+ )
136
+ else
137
+ nil
138
+ end
127
139
  end
128
140
 
129
141
  def parse_addressbook_prop_filter(el, ns)
130
142
  name = el.attributes['name']
131
- raise ParseError, "prop-filter missing required 'name' attribute" unless name
143
+ unless name
144
+ raise ParseError, "prop-filter missing required 'name' attribute"
145
+ end
132
146
 
133
147
  is_not_defined = REXML::XPath.first(el, 'cr:is-not-defined', ns) != nil
134
148
  text_match_el = REXML::XPath.first(el, 'cr:text-match', ns)
135
- text_match = if text_match_el
136
- Addressbook::TextMatch.new(
137
- value: text_match_el.text&.strip || '',
138
- collation: text_match_el.attributes['collation'] || 'i;ascii-casemap',
139
- match_type: text_match_el.attributes['match-type'] || 'contains',
140
- negate_condition: text_match_el.attributes['negate-condition'] == 'yes'
149
+ text_match = nil
150
+
151
+ if text_match_el
152
+ text_match = Addressbook::TextMatch.new(
153
+ value: text_match_el.text&.strip || '',
154
+ collation: text_match_el.attributes['collation'] || 'i;ascii-casemap',
155
+ match_type: text_match_el.attributes['match-type'] || 'contains',
156
+ negate_condition: text_match_el.attributes['negate-condition'] == 'yes',
141
157
  )
142
158
  end
143
159
 
@@ -145,27 +161,33 @@ module Protocol
145
161
  pf_name = pf_el.attributes['name']
146
162
  pf_is_not_defined = REXML::XPath.first(pf_el, 'cr:is-not-defined', ns) != nil
147
163
  pf_text_match_el = REXML::XPath.first(pf_el, 'cr:text-match', ns)
148
- pf_text_match = if pf_text_match_el
149
- Addressbook::TextMatch.new(
150
- value: pf_text_match_el.text&.strip || '',
151
- collation: pf_text_match_el.attributes['collation'] || 'i;ascii-casemap',
152
- match_type: pf_text_match_el.attributes['match-type'] || 'contains',
153
- negate_condition: pf_text_match_el.attributes['negate-condition'] == 'yes'
164
+ pf_text_match = nil
165
+
166
+ if pf_text_match_el
167
+ pf_text_match = Addressbook::TextMatch.new(
168
+ value: pf_text_match_el.text&.strip || '',
169
+ collation: pf_text_match_el.attributes['collation'] || 'i;ascii-casemap',
170
+ match_type: pf_text_match_el.attributes['match-type'] || 'contains',
171
+ negate_condition: pf_text_match_el.attributes['negate-condition'] == 'yes',
154
172
  )
155
173
  end
156
174
  Addressbook::ParamFilter.new(name: pf_name, is_not_defined: pf_is_not_defined, text_match: pf_text_match)
157
175
  end
158
176
 
159
177
  Addressbook::PropFilter.new(
160
- name: name,
178
+ name: name,
161
179
  is_not_defined: is_not_defined,
162
- text_match: text_match,
163
- param_filters: param_filters
180
+ text_match: text_match,
181
+ param_filters: param_filters,
164
182
  )
165
183
  end
166
184
 
167
- private_class_method :parse_comp_filter, :parse_prop_filter, :parse_param_filter,
168
- :parse_text_match, :parse_time_range, :parse_addressbook_prop_filter
185
+ private_class_method :parse_comp_filter,
186
+ :parse_prop_filter,
187
+ :parse_param_filter,
188
+ :parse_text_match,
189
+ :parse_time_range,
190
+ :parse_addressbook_prop_filter
169
191
  end
170
192
  end
171
193
 
@@ -173,219 +195,218 @@ module Protocol
173
195
  end
174
196
  end
175
197
 
198
+ __END__
176
199
 
177
- test do
178
- describe "Protocol::Caldav::Filter::Parser" do
179
- describe "calendar parser" do
180
- it "parses a single comp-filter" do
181
- xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter name="VCALENDAR"/></c:filter>'
182
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
183
- f.name.should.equal "VCALENDAR"
184
- end
200
+ describe "Protocol::Caldav::Filter::Parser" do
201
+ describe "calendar parser" do
202
+ it "parses a single comp-filter" do
203
+ xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter name="VCALENDAR"/></c:filter>'
204
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
205
+ f.name.should.equal "VCALENDAR"
206
+ end
185
207
 
186
- it "parses nested comp-filters" do
187
- xml = <<~XML
188
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
189
- <c:comp-filter name="VCALENDAR">
190
- <c:comp-filter name="VEVENT"/>
191
- </c:comp-filter>
192
- </c:filter>
193
- XML
194
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
195
- f.comp_filters.length.should.equal 1
196
- f.comp_filters[0].name.should.equal "VEVENT"
197
- end
208
+ it "parses nested comp-filters" do
209
+ xml = <<~XML
210
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
211
+ <c:comp-filter name="VCALENDAR">
212
+ <c:comp-filter name="VEVENT"/>
213
+ </c:comp-filter>
214
+ </c:filter>
215
+ XML
216
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
217
+ f.comp_filters.length.should.equal 1
218
+ f.comp_filters[0].name.should.equal "VEVENT"
219
+ end
198
220
 
199
- it "parses is-not-defined" do
200
- xml = <<~XML
201
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
202
- <c:comp-filter name="VCALENDAR">
203
- <c:comp-filter name="VTODO"><c:is-not-defined/></c:comp-filter>
204
- </c:comp-filter>
205
- </c:filter>
206
- XML
207
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
208
- f.comp_filters[0].is_not_defined.should.equal true
209
- end
221
+ it "parses is-not-defined" do
222
+ xml = <<~XML
223
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
224
+ <c:comp-filter name="VCALENDAR">
225
+ <c:comp-filter name="VTODO"><c:is-not-defined/></c:comp-filter>
226
+ </c:comp-filter>
227
+ </c:filter>
228
+ XML
229
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
230
+ f.comp_filters[0].is_not_defined.should.equal true
231
+ end
210
232
 
211
- it "parses prop-filter with no children as defined-only check" do
212
- xml = <<~XML
213
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
214
- <c:comp-filter name="VCALENDAR">
215
- <c:prop-filter name="SUMMARY"/>
216
- </c:comp-filter>
217
- </c:filter>
218
- XML
219
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
220
- f.prop_filters.length.should.equal 1
221
- f.prop_filters[0].name.should.equal "SUMMARY"
222
- f.prop_filters[0].text_match.should.be.nil
223
- end
233
+ it "parses prop-filter with no children as defined-only check" do
234
+ xml = <<~XML
235
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
236
+ <c:comp-filter name="VCALENDAR">
237
+ <c:prop-filter name="SUMMARY"/>
238
+ </c:comp-filter>
239
+ </c:filter>
240
+ XML
241
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
242
+ f.prop_filters.length.should.equal 1
243
+ f.prop_filters[0].name.should.equal "SUMMARY"
244
+ f.prop_filters[0].text_match.should.be.nil
245
+ end
224
246
 
225
- it "parses text-match with default collation and match-type" do
226
- xml = <<~XML
227
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
228
- <c:comp-filter name="VCALENDAR">
229
- <c:prop-filter name="SUMMARY">
230
- <c:text-match>Meeting</c:text-match>
231
- </c:prop-filter>
232
- </c:comp-filter>
233
- </c:filter>
234
- XML
235
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
236
- tm = f.prop_filters[0].text_match
237
- tm.value.should.equal "Meeting"
238
- tm.collation.should.equal "i;ascii-casemap"
239
- tm.match_type.should.equal "contains"
240
- end
247
+ it "parses text-match with default collation and match-type" do
248
+ xml = <<~XML
249
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
250
+ <c:comp-filter name="VCALENDAR">
251
+ <c:prop-filter name="SUMMARY">
252
+ <c:text-match>Meeting</c:text-match>
253
+ </c:prop-filter>
254
+ </c:comp-filter>
255
+ </c:filter>
256
+ XML
257
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
258
+ tm = f.prop_filters[0].text_match
259
+ tm.value.should.equal "Meeting"
260
+ tm.collation.should.equal "i;ascii-casemap"
261
+ tm.match_type.should.equal "contains"
262
+ end
241
263
 
242
- it "parses text-match with explicit attributes" do
243
- xml = <<~XML
244
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
245
- <c:comp-filter name="VCALENDAR">
246
- <c:prop-filter name="SUMMARY">
247
- <c:text-match collation="i;octet" match-type="equals">X</c:text-match>
248
- </c:prop-filter>
249
- </c:comp-filter>
250
- </c:filter>
251
- XML
252
- tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
253
- tm.collation.should.equal "i;octet"
254
- tm.match_type.should.equal "equals"
255
- end
264
+ it "parses text-match with explicit attributes" do
265
+ xml = <<~XML
266
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
267
+ <c:comp-filter name="VCALENDAR">
268
+ <c:prop-filter name="SUMMARY">
269
+ <c:text-match collation="i;octet" match-type="equals">X</c:text-match>
270
+ </c:prop-filter>
271
+ </c:comp-filter>
272
+ </c:filter>
273
+ XML
274
+ tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
275
+ tm.collation.should.equal "i;octet"
276
+ tm.match_type.should.equal "equals"
277
+ end
256
278
 
257
- it "parses negate-condition" do
258
- xml = <<~XML
259
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
260
- <c:comp-filter name="VCALENDAR">
261
- <c:prop-filter name="SUMMARY">
262
- <c:text-match negate-condition="yes">X</c:text-match>
263
- </c:prop-filter>
264
- </c:comp-filter>
265
- </c:filter>
266
- XML
267
- tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
268
- tm.negate_condition.should.equal true
269
- end
279
+ it "parses negate-condition" do
280
+ xml = <<~XML
281
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
282
+ <c:comp-filter name="VCALENDAR">
283
+ <c:prop-filter name="SUMMARY">
284
+ <c:text-match negate-condition="yes">X</c:text-match>
285
+ </c:prop-filter>
286
+ </c:comp-filter>
287
+ </c:filter>
288
+ XML
289
+ tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
290
+ tm.negate_condition.should.equal true
291
+ end
270
292
 
271
- it "parses time-range with start and end" do
272
- xml = <<~XML
273
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
274
- <c:comp-filter name="VCALENDAR">
275
- <c:comp-filter name="VEVENT">
276
- <c:time-range start="20260101T000000Z" end="20260201T000000Z"/>
277
- </c:comp-filter>
293
+ it "parses time-range with start and end" do
294
+ xml = <<~XML
295
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
296
+ <c:comp-filter name="VCALENDAR">
297
+ <c:comp-filter name="VEVENT">
298
+ <c:time-range start="20260101T000000Z" end="20260201T000000Z"/>
278
299
  </c:comp-filter>
279
- </c:filter>
280
- XML
281
- tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
282
- tr.start_time.should.equal "20260101T000000Z"
283
- tr.end_time.should.equal "20260201T000000Z"
284
- end
300
+ </c:comp-filter>
301
+ </c:filter>
302
+ XML
303
+ tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
304
+ tr.start_time.should.equal "20260101T000000Z"
305
+ tr.end_time.should.equal "20260201T000000Z"
306
+ end
285
307
 
286
- it "parses time-range with only start" do
287
- xml = <<~XML
288
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
289
- <c:comp-filter name="VCALENDAR">
290
- <c:comp-filter name="VEVENT">
291
- <c:time-range start="20260101T000000Z"/>
292
- </c:comp-filter>
308
+ it "parses time-range with only start" do
309
+ xml = <<~XML
310
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
311
+ <c:comp-filter name="VCALENDAR">
312
+ <c:comp-filter name="VEVENT">
313
+ <c:time-range start="20260101T000000Z"/>
293
314
  </c:comp-filter>
294
- </c:filter>
295
- XML
296
- tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
297
- tr.start_time.should.equal "20260101T000000Z"
298
- tr.end_time.should.be.nil
299
- end
315
+ </c:comp-filter>
316
+ </c:filter>
317
+ XML
318
+ tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
319
+ tr.start_time.should.equal "20260101T000000Z"
320
+ tr.end_time.should.be.nil
321
+ end
300
322
 
301
- it "returns nil for missing filter element" do
302
- Protocol::Caldav::Filter::Parser.parse_calendar('<d:propfind xmlns:d="DAV:"/>').should.be.nil
303
- end
323
+ it "returns nil for missing filter element" do
324
+ Protocol::Caldav::Filter::Parser.parse_calendar('<d:propfind xmlns:d="DAV:"/>').should.be.nil
325
+ end
304
326
 
305
- it "returns nil for empty filter" do
306
- Protocol::Caldav::Filter::Parser.parse_calendar('<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"/>').should.be.nil
307
- end
327
+ it "returns nil for empty filter" do
328
+ Protocol::Caldav::Filter::Parser.parse_calendar('<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"/>').should.be.nil
329
+ end
308
330
 
309
- it "returns nil for nil input" do
310
- Protocol::Caldav::Filter::Parser.parse_calendar(nil).should.be.nil
311
- end
331
+ it "returns nil for nil input" do
332
+ Protocol::Caldav::Filter::Parser.parse_calendar(nil).should.be.nil
333
+ end
312
334
 
313
- it "accepts any namespace prefix bound to caldav NS" do
314
- xml = '<cal:filter xmlns:cal="urn:ietf:params:xml:ns:caldav"><cal:comp-filter name="VCALENDAR"/></cal:filter>'
315
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
316
- f.name.should.equal "VCALENDAR"
317
- end
335
+ it "accepts any namespace prefix bound to caldav NS" do
336
+ xml = '<cal:filter xmlns:cal="urn:ietf:params:xml:ns:caldav"><cal:comp-filter name="VCALENDAR"/></cal:filter>'
337
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
338
+ f.name.should.equal "VCALENDAR"
339
+ end
318
340
 
319
- it "parses param-filter inside prop-filter" do
320
- xml = <<~XML
321
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
322
- <c:comp-filter name="VCALENDAR">
323
- <c:comp-filter name="VEVENT">
324
- <c:prop-filter name="ATTENDEE">
325
- <c:param-filter name="PARTSTAT">
326
- <c:text-match>ACCEPTED</c:text-match>
327
- </c:param-filter>
328
- </c:prop-filter>
329
- </c:comp-filter>
341
+ it "parses param-filter inside prop-filter" do
342
+ xml = <<~XML
343
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
344
+ <c:comp-filter name="VCALENDAR">
345
+ <c:comp-filter name="VEVENT">
346
+ <c:prop-filter name="ATTENDEE">
347
+ <c:param-filter name="PARTSTAT">
348
+ <c:text-match>ACCEPTED</c:text-match>
349
+ </c:param-filter>
350
+ </c:prop-filter>
330
351
  </c:comp-filter>
331
- </c:filter>
332
- XML
333
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
334
- pf = f.comp_filters[0].prop_filters[0]
335
- pf.name.should.equal "ATTENDEE"
336
- pf.param_filters.length.should.equal 1
337
- pf.param_filters[0].name.should.equal "PARTSTAT"
338
- pf.param_filters[0].text_match.value.should.equal "ACCEPTED"
339
- end
352
+ </c:comp-filter>
353
+ </c:filter>
354
+ XML
355
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
356
+ pf = f.comp_filters[0].prop_filters[0]
357
+ pf.name.should.equal "ATTENDEE"
358
+ pf.param_filters.length.should.equal 1
359
+ pf.param_filters[0].name.should.equal "PARTSTAT"
360
+ pf.param_filters[0].text_match.value.should.equal "ACCEPTED"
361
+ end
340
362
 
341
- it "parses param-filter with is-not-defined" do
342
- xml = <<~XML
343
- <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
344
- <c:comp-filter name="VCALENDAR">
345
- <c:comp-filter name="VEVENT">
346
- <c:prop-filter name="ATTENDEE">
347
- <c:param-filter name="PARTSTAT">
348
- <c:is-not-defined/>
349
- </c:param-filter>
350
- </c:prop-filter>
351
- </c:comp-filter>
363
+ it "parses param-filter with is-not-defined" do
364
+ xml = <<~XML
365
+ <c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
366
+ <c:comp-filter name="VCALENDAR">
367
+ <c:comp-filter name="VEVENT">
368
+ <c:prop-filter name="ATTENDEE">
369
+ <c:param-filter name="PARTSTAT">
370
+ <c:is-not-defined/>
371
+ </c:param-filter>
372
+ </c:prop-filter>
352
373
  </c:comp-filter>
353
- </c:filter>
354
- XML
355
- f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
356
- pf = f.comp_filters[0].prop_filters[0].param_filters[0]
357
- pf.is_not_defined.should.equal true
358
- end
374
+ </c:comp-filter>
375
+ </c:filter>
376
+ XML
377
+ f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
378
+ pf = f.comp_filters[0].prop_filters[0].param_filters[0]
379
+ pf.is_not_defined.should.equal true
380
+ end
359
381
 
360
- it "raises on comp-filter with no name attribute" do
361
- xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter/></c:filter>'
362
- lambda { Protocol::Caldav::Filter::Parser.parse_calendar(xml) }.should.raise Protocol::Caldav::ParseError
363
- end
382
+ it "raises on comp-filter with no name attribute" do
383
+ xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter/></c:filter>'
384
+ lambda { Protocol::Caldav::Filter::Parser.parse_calendar(xml) }.should.raise Protocol::Caldav::ParseError
364
385
  end
386
+ end
365
387
 
366
- describe "addressbook parser" do
367
- it "parses a prop-filter" do
368
- xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
369
- f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
370
- f.prop_filters.length.should.equal 1
371
- f.prop_filters[0].name.should.equal "FN"
372
- end
388
+ describe "addressbook parser" do
389
+ it "parses a prop-filter" do
390
+ xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
391
+ f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
392
+ f.prop_filters.length.should.equal 1
393
+ f.prop_filters[0].name.should.equal "FN"
394
+ end
373
395
 
374
- it "parses test attribute on filter" do
375
- xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav" test="allof"><cr:prop-filter name="FN"/></cr:filter>'
376
- f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
377
- f.test.should.equal "allof"
378
- end
396
+ it "parses test attribute on filter" do
397
+ xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav" test="allof"><cr:prop-filter name="FN"/></cr:filter>'
398
+ f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
399
+ f.test.should.equal "allof"
400
+ end
379
401
 
380
- it "defaults test to anyof" do
381
- xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
382
- f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
383
- f.test.should.equal "anyof"
384
- end
402
+ it "defaults test to anyof" do
403
+ xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
404
+ f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
405
+ f.test.should.equal "anyof"
406
+ end
385
407
 
386
- it "returns nil for nil input" do
387
- Protocol::Caldav::Filter::Parser.parse_addressbook(nil).should.be.nil
388
- end
408
+ it "returns nil for nil input" do
409
+ Protocol::Caldav::Filter::Parser.parse_addressbook(nil).should.be.nil
389
410
  end
390
411
  end
391
412
  end