tilia-vobject 4.0.0.pre.alpha5 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.sabre.md +59 -2
  3. data/Gemfile +1 -8
  4. data/Gemfile.lock +18 -12
  5. data/LICENSE +1 -1
  6. data/LICENSE.sabre +1 -1
  7. data/lib/tilia/v_object.rb +1 -0
  8. data/lib/tilia/v_object/birthday_calendar_generator.rb +11 -11
  9. data/lib/tilia/v_object/cli.rb +39 -38
  10. data/lib/tilia/v_object/component.rb +47 -50
  11. data/lib/tilia/v_object/component/available.rb +4 -37
  12. data/lib/tilia/v_object/component/v_alarm.rb +6 -18
  13. data/lib/tilia/v_object/component/v_availability.rb +6 -39
  14. data/lib/tilia/v_object/component/v_calendar.rb +66 -98
  15. data/lib/tilia/v_object/component/v_card.rb +42 -69
  16. data/lib/tilia/v_object/component/v_event.rb +5 -17
  17. data/lib/tilia/v_object/component/v_free_busy.rb +4 -16
  18. data/lib/tilia/v_object/component/v_journal.rb +16 -16
  19. data/lib/tilia/v_object/component/v_time_zone.rb +2 -14
  20. data/lib/tilia/v_object/component/v_todo.rb +17 -36
  21. data/lib/tilia/v_object/date_time_parser.rb +24 -24
  22. data/lib/tilia/v_object/document.rb +25 -25
  23. data/lib/tilia/v_object/free_busy_data.rb +7 -7
  24. data/lib/tilia/v_object/free_busy_generator.rb +73 -75
  25. data/lib/tilia/v_object/i_tip.rb +1 -0
  26. data/lib/tilia/v_object/i_tip/broker.rb +134 -116
  27. data/lib/tilia/v_object/i_tip/i_tip_exception.rb +1 -1
  28. data/lib/tilia/v_object/i_tip/message.rb +13 -13
  29. data/lib/tilia/v_object/invalid_data_exception.rb +8 -0
  30. data/lib/tilia/v_object/node.rb +22 -27
  31. data/lib/tilia/v_object/parameter.rb +22 -22
  32. data/lib/tilia/v_object/parse_exception.rb +1 -1
  33. data/lib/tilia/v_object/parser.rb +1 -0
  34. data/lib/tilia/v_object/parser/json.rb +9 -19
  35. data/lib/tilia/v_object/parser/mime_dir.rb +73 -39
  36. data/lib/tilia/v_object/parser/parser.rb +9 -14
  37. data/lib/tilia/v_object/parser/xml.rb +33 -50
  38. data/lib/tilia/v_object/parser/xml/element.rb +1 -0
  39. data/lib/tilia/v_object/parser/xml/element/key_value.rb +2 -2
  40. data/lib/tilia/v_object/property.rb +52 -52
  41. data/lib/tilia/v_object/property/binary.rb +10 -10
  42. data/lib/tilia/v_object/property/boolean.rb +6 -6
  43. data/lib/tilia/v_object/property/flat_text.rb +3 -3
  44. data/lib/tilia/v_object/property/float_value.rb +10 -10
  45. data/lib/tilia/v_object/property/i_calendar.rb +1 -0
  46. data/lib/tilia/v_object/property/i_calendar/cal_address.rb +3 -3
  47. data/lib/tilia/v_object/property/i_calendar/date_time.rb +29 -57
  48. data/lib/tilia/v_object/property/i_calendar/duration.rb +6 -6
  49. data/lib/tilia/v_object/property/i_calendar/period.rb +10 -10
  50. data/lib/tilia/v_object/property/i_calendar/recur.rb +16 -24
  51. data/lib/tilia/v_object/property/integer_value.rb +8 -8
  52. data/lib/tilia/v_object/property/text.rb +21 -36
  53. data/lib/tilia/v_object/property/time.rb +29 -6
  54. data/lib/tilia/v_object/property/unknown.rb +2 -2
  55. data/lib/tilia/v_object/property/uri.rb +24 -5
  56. data/lib/tilia/v_object/property/utc_offset.rb +5 -5
  57. data/lib/tilia/v_object/property/v_card.rb +1 -0
  58. data/lib/tilia/v_object/property/v_card/date.rb +3 -3
  59. data/lib/tilia/v_object/property/v_card/date_and_or_time.rb +30 -42
  60. data/lib/tilia/v_object/property/v_card/date_time.rb +1 -1
  61. data/lib/tilia/v_object/property/v_card/language_tag.rb +4 -4
  62. data/lib/tilia/v_object/property/v_card/time_stamp.rb +5 -5
  63. data/lib/tilia/v_object/reader.rb +12 -11
  64. data/lib/tilia/v_object/recur.rb +2 -0
  65. data/lib/tilia/v_object/recur/event_iterator.rb +30 -27
  66. data/lib/tilia/v_object/recur/max_instances_exceeded_exception.rb +10 -0
  67. data/lib/tilia/v_object/recur/no_instances_exception.rb +1 -1
  68. data/lib/tilia/v_object/recur/r_date_iterator.rb +15 -41
  69. data/lib/tilia/v_object/recur/r_rule_iterator.rb +23 -156
  70. data/lib/tilia/v_object/settings.rb +16 -0
  71. data/lib/tilia/v_object/splitter.rb +1 -0
  72. data/lib/tilia/v_object/splitter/i_calendar.rb +5 -5
  73. data/lib/tilia/v_object/splitter/splitter_interface.rb +2 -2
  74. data/lib/tilia/v_object/splitter/v_card.rb +5 -5
  75. data/lib/tilia/v_object/string_util.rb +25 -9
  76. data/lib/tilia/v_object/time_zone_data.rb +1 -0
  77. data/lib/tilia/v_object/time_zone_util.rb +6 -6
  78. data/lib/tilia/v_object/uuid_util.rb +3 -3
  79. data/lib/tilia/v_object/v_card_converter.rb +24 -21
  80. data/lib/tilia/v_object/version.rb +1 -1
  81. data/lib/tilia/v_object/writer.rb +7 -7
  82. data/test/test_helper.rb +3 -3
  83. data/test/v_object/birthday_calendar_generator_test.rb +22 -0
  84. data/test/v_object/component/v_alarm_test.rb +3 -1
  85. data/test/v_object/component/v_calendar_test.rb +40 -4
  86. data/test/v_object/component/v_card_test.rb +1 -1
  87. data/test/v_object/component_test.rb +5 -3
  88. data/test/v_object/date_time_parser_test.rb +15 -5
  89. data/test/v_object/free_busy_generator_test.rb +5 -5
  90. data/test/v_object/i_tip/broker_attendee_reply_test.rb +19 -0
  91. data/test/v_object/i_tip/broker_delete_event_test.rb +146 -2
  92. data/test/v_object/i_tip/broker_new_event_test.rb +21 -0
  93. data/test/v_object/i_tip/broker_process_reply_test.rb +10 -0
  94. data/test/v_object/i_tip/broker_tester.rb +1 -1
  95. data/test/v_object/i_tip/broker_update_event_test.rb +36 -0
  96. data/test/v_object/issue259_test.rb +24 -0
  97. data/test/v_object/issue40_test.rb +3 -1
  98. data/test/v_object/j_cal_test.rb +15 -12
  99. data/test/v_object/parser/json_test.rb +5 -5
  100. data/test/v_object/parser/mime_dir_test.rb +109 -0
  101. data/test/v_object/property/binary_test.rb +4 -2
  102. data/test/v_object/property/i_calendar/date_time_test.rb +3 -1
  103. data/test/v_object/property/uri_test.rb +23 -0
  104. data/test/v_object/recur/event_iterator/by_month_in_daily_test.rb +1 -1
  105. data/test/v_object/recur/event_iterator/by_set_pos_hang_test.rb +1 -1
  106. data/test/v_object/recur/event_iterator/expand_floating_times_test.rb +9 -9
  107. data/test/v_object/recur/event_iterator/handle_r_date_expand_test.rb +51 -0
  108. data/test/v_object/recur/event_iterator/incorrect_expand_test.rb +3 -5
  109. data/test/v_object/recur/event_iterator/infinite_loop_problem_test.rb +3 -1
  110. data/test/v_object/{issue26_test.rb → recur/event_iterator/issue26_test.rb} +3 -1
  111. data/test/v_object/recur/event_iterator/main_test.rb +9 -3
  112. data/test/v_object/recur/event_iterator/max_instances_test.rb +38 -0
  113. data/test/v_object/recur/event_iterator/missing_overridden_test.rb +3 -5
  114. data/test/v_object/recur/event_iterator/no_instances_test.rb +5 -3
  115. data/test/v_object/recur/event_iterator/override_first_event_test.rb +9 -9
  116. data/test/v_object/recur/r_date_iterator_test.rb +18 -0
  117. data/test/v_object/recur/r_rule_iterator_test.rb +4 -4
  118. data/test/v_object/splitter/i_calendar_test.rb +27 -7
  119. data/test/v_object/splitter/v_card_test.rb +5 -3
  120. data/test/v_object/test_case.rb +14 -4
  121. data/tilia-vobject.gemspec +2 -2
  122. metadata +17 -11
@@ -21,12 +21,12 @@ module Tilia
21
21
  #
22
22
  # This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
23
23
  #
24
- # @var string
24
+ # @return [String]
25
25
  attr_accessor :name
26
26
 
27
27
  # A list of properties and/or sub-components.
28
28
  #
29
- # @var array
29
+ # @return [array]
30
30
  # RUBY: attr_accessor :children
31
31
 
32
32
  # Creates a new component.
@@ -39,12 +39,12 @@ module Tilia
39
39
  # an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
40
40
  # ensure that this does not happen, set defaults to false.
41
41
  #
42
- # @param Document root
43
- # @param string name such as VCALENDAR, VEVENT.
44
- # @param array children
45
- # @param bool defaults
42
+ # @param [Document] root
43
+ # @param [String] name such as VCALENDAR, VEVENT.
44
+ # @param [array] children
45
+ # @param [Boolean] defaults
46
46
  #
47
- # @return void
47
+ # @return [void]
48
48
  def initialize(root, name, children = {}, defaults = true)
49
49
  @children = {}
50
50
  @name = name.to_s.upcase
@@ -104,17 +104,16 @@ module Tilia
104
104
  # add(name, array children = []) // Adds a new component
105
105
  # by name.
106
106
  #
107
- # @return Node
108
- def add(a1, a2 = nil, a3 = nil)
109
- if a1.is_a?(Node)
110
- unless a2.nil?
107
+ # @return [Node]
108
+ def add(*arguments)
109
+ if arguments[0].is_a?(Node)
110
+ if arguments[1]
111
111
  fail ArgumentError, 'The second argument must not be specified, when passing a VObject Node'
112
112
  end
113
- a1.parent = self
114
- new_node = a1
115
- elsif a1.is_a?(String)
116
- new_node = @root.create(a1, a2, a3)
117
- new_node.parent = self
113
+ arguments[0].parent = self
114
+ new_node = arguments[0]
115
+ elsif arguments[0].is_a?(String)
116
+ new_node = @root.create(*arguments)
118
117
  else
119
118
  fail ArgumentError, 'The first argument must either be a Node or a string'
120
119
  end
@@ -136,8 +135,8 @@ module Tilia
136
135
  # pass an instance of a property or component, in which case only that
137
136
  # exact item will be removed.
138
137
  #
139
- # @param string|Property|Component item
140
- # @return void
138
+ # @param [String|Property|Component] item
139
+ # @return [void]
141
140
  def remove(item)
142
141
  if item.is_a?(String)
143
142
  # If there's no dot in the name, it's an exact property name and
@@ -168,7 +167,7 @@ module Tilia
168
167
  # Returns a flat list of all the properties and components in this
169
168
  # component.
170
169
  #
171
- # @return array
170
+ # @return [array]
172
171
  def children
173
172
  result = []
174
173
  @children.each do |_, child_group|
@@ -181,7 +180,7 @@ module Tilia
181
180
  # This method only returns a list of sub-components. Properties are
182
181
  # ignored.
183
182
  #
184
- # @return array
183
+ # @return [array]
185
184
  def components
186
185
  result = []
187
186
 
@@ -203,8 +202,8 @@ module Tilia
203
202
  # string ("HOME.EMAIL"). If you want to search on a specific property that
204
203
  # has not been assigned a group, specify ".EMAIL".
205
204
  #
206
- # @param string name
207
- # @return array
205
+ # @param [String] name
206
+ # @return [array]
208
207
  def select(name)
209
208
  group = nil
210
209
  name = name.upcase
@@ -243,7 +242,7 @@ module Tilia
243
242
 
244
243
  # Turns the object back into a serialized blob.
245
244
  #
246
- # @return string
245
+ # @return [String]
247
246
  def serialize
248
247
  str = "BEGIN:#{@name}\r\n"
249
248
 
@@ -256,10 +255,10 @@ module Tilia
256
255
  # space to accomodate elements. The key is added to the score to
257
256
  # preserve the original relative order of elements.
258
257
  #
259
- # @param int key
260
- # @param array array
258
+ # @param [Fixnum] key
259
+ # @param [array] array
261
260
  #
262
- # @return int
261
+ # @return [Fixnum]
263
262
  sort_score = lambda do |key, array|
264
263
  key = array.index(key)
265
264
  if array[key].is_a?(Component)
@@ -305,7 +304,7 @@ module Tilia
305
304
  # This method returns an array, with the representation as it should be
306
305
  # encoded in JSON. This is used to create jCard or jCal documents.
307
306
  #
308
- # @return array
307
+ # @return [array]
309
308
  def json_serialize
310
309
  components = []
311
310
  properties = []
@@ -330,9 +329,9 @@ module Tilia
330
329
  # This method serializes the data into XML. This is used to create xCard or
331
330
  # xCal documents.
332
331
  #
333
- # @param Xml\Writer writer XML writer.
332
+ # @param [Xml\Writer] writer XML writer.
334
333
  #
335
- # @return void
334
+ # @return [void]
336
335
  def xml_serialize(writer)
337
336
  components = []
338
337
  properties = []
@@ -372,7 +371,7 @@ module Tilia
372
371
 
373
372
  # This method should return a list of default property values.
374
373
  #
375
- # @return array
374
+ # @return [array]
376
375
  def defaults
377
376
  []
378
377
  end
@@ -388,9 +387,9 @@ module Tilia
388
387
  #
389
388
  # event = calendar->VEVENT
390
389
  #
391
- # @param string name
390
+ # @param [String] name
392
391
  #
393
- # @return Property
392
+ # @return [Property]
394
393
  def [](name)
395
394
  return super(name) if name.is_a?(Fixnum)
396
395
 
@@ -404,7 +403,7 @@ module Tilia
404
403
  return nil
405
404
  else
406
405
  first_match = matches.first
407
- # @var first_match Property
406
+ # @return [first_match] Property
408
407
  first_match.iterator = ElementList.new(matches.to_a)
409
408
  return first_match
410
409
  end
@@ -412,9 +411,9 @@ module Tilia
412
411
 
413
412
  # This method checks if a sub-element with the specified name exists.
414
413
  #
415
- # @param string name
414
+ # @param [String] name
416
415
  #
417
- # @return bool
416
+ # @return [Boolean]
418
417
  def key?(name)
419
418
  matches = select(name)
420
419
  matches.any?
@@ -428,10 +427,10 @@ module Tilia
428
427
  # If the item already exists, it will be removed. If you want to add
429
428
  # a new item with the same name, always use the add method.
430
429
  #
431
- # @param string name
432
- # @param mixed value
430
+ # @param [String] name
431
+ # @param value
433
432
  #
434
- # @return void
433
+ # @return [void]
435
434
  def []=(name, value)
436
435
  return super(name, value) if name.is_a?(Fixnum)
437
436
  name = name.upcase
@@ -448,9 +447,9 @@ module Tilia
448
447
  # Removes all properties and components within this component with the
449
448
  # specified name.
450
449
  #
451
- # @param string name
450
+ # @param [String] name
452
451
  #
453
- # @return void
452
+ # @return [void]
454
453
  def delete(name)
455
454
  return super(name) if name.is_a?(Fixnum)
456
455
  remove(name)
@@ -459,7 +458,7 @@ module Tilia
459
458
  # This method is automatically called when the object is cloned.
460
459
  # Specifically, this will ensure all child elements are also cloned.
461
460
  #
462
- # @return void
461
+ # @return [void]
463
462
  def initialize_copy(_original)
464
463
  new_children = {}
465
464
  @children.each do |child_name, child_group|
@@ -492,7 +491,7 @@ module Tilia
492
491
  # See the VEVENT implementation for getValidationRules for a more complex
493
492
  # example.
494
493
  #
495
- # @var array
494
+ # @return [array]
496
495
  def validation_rules
497
496
  []
498
497
  end
@@ -516,9 +515,9 @@ module Tilia
516
515
  # 2 - A warning.
517
516
  # 3 - An error.
518
517
  #
519
- # @param int options
518
+ # @param [Fixnum] options
520
519
  #
521
- # @return array
520
+ # @return [array]
522
521
  def validate(options = 0)
523
522
  rules = validation_rules
524
523
  defaults = self.defaults
@@ -551,7 +550,7 @@ module Tilia
551
550
  when '1'
552
551
  if !property_counters.key?(prop_name) || property_counters[prop_name] != 1
553
552
  repaired = false
554
- add(prop_name, defaults[prop_name]) if options & self.class::REPAIR > 0 && defaults.key?(prop_name)
553
+ add(prop_name, defaults[prop_name]) if options & REPAIR > 0 && defaults.key?(prop_name)
555
554
 
556
555
  messages << {
557
556
  'level' => repaired ? 1 : 3,
@@ -587,14 +586,12 @@ module Tilia
587
586
  # It's intended to remove all circular references, so PHP can easily clean
588
587
  # it up.
589
588
  #
590
- # @return void
589
+ # @return [void]
591
590
  def destroy
592
591
  super
593
592
 
594
- @children.each do |child_name, child_group|
595
- child_group.each do |child|
596
- child.destroy
597
- end
593
+ @children.each do |_child_name, child_group|
594
+ child_group.each(&:destroy)
598
595
  end
599
596
 
600
597
  @children = {}
@@ -15,10 +15,10 @@ module Tilia
15
15
  # If either the start or end is 'unbounded' its value will be null
16
16
  # instead.
17
17
  #
18
- # @return array
18
+ # @return [Array<Time, nil>]
19
19
  def effective_start_end
20
20
  effective_start = self['DTSTART'].date_time
21
- if key? 'DTEND'
21
+ if key?('DTEND')
22
22
  effective_end = self['DTEND'].date_time
23
23
  else
24
24
  effective_end = effective_start + DateTimeParser.parse_duration(self['DURATION'])
@@ -27,19 +27,7 @@ module Tilia
27
27
  [effective_start, effective_end]
28
28
  end
29
29
 
30
- # A simple list of validation rules.
31
- #
32
- # This is simply a list of properties, and how many times they either
33
- # must or must not appear.
34
- #
35
- # Possible values per property:
36
- # * 0 - Must not appear.
37
- # * 1 - Must appear exactly once.
38
- # * + - Must appear at least once.
39
- # * * - Can appear any number of times.
40
- # * ? - May appear, but not more than once.
41
- #
42
- # @var array
30
+ # (see Component#validation_rules)
43
31
  def validation_rules
44
32
  {
45
33
  'UID' => 1,
@@ -66,28 +54,7 @@ module Tilia
66
54
  }
67
55
  end
68
56
 
69
- # Validates the node for correctness.
70
- #
71
- # The following options are supported:
72
- # Node::REPAIR - May attempt to automatically repair the problem.
73
- # Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
74
- # Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
75
- #
76
- # This method returns an array with detected problems.
77
- # Every element has the following properties:
78
- #
79
- # * level - problem level.
80
- # * message - A human-readable string describing the issue.
81
- # * node - A reference to the problematic node.
82
- #
83
- # The level means:
84
- # 1 - The issue was repaired (only happens if REPAIR was turned on).
85
- # 2 - A warning.
86
- # 3 - An error.
87
- #
88
- # @param int options
89
- #
90
- # @return array
57
+ # (see Component#validate)
91
58
  def validate(options = 0)
92
59
  result = super(options)
93
60
 
@@ -9,7 +9,7 @@ module Tilia
9
9
  #
10
10
  # This ignores repeated alarm, only the first trigger is returned.
11
11
  #
12
- # @return DateTimeImmutable
12
+ # @return [Time]
13
13
  def effective_trigger_time
14
14
  trigger = self['TRIGGER']
15
15
  if !trigger.key?('VALUE') || trigger['VALUE'].to_s.upcase == 'DURATION'
@@ -32,7 +32,7 @@ module Tilia
32
32
  elsif parent_component.name == 'VEVENT'
33
33
  end_prop = 'DTEND'
34
34
  else
35
- fail 'time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'
35
+ fail InvalidDataException, 'time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'
36
36
  end
37
37
 
38
38
  if parent_component.key?(end_prop)
@@ -61,10 +61,10 @@ module Tilia
61
61
  # The rules used to determine if an event falls within the specified
62
62
  # time-range is based on the CalDAV specification.
63
63
  #
64
- # @param DateTime start
65
- # @param DateTime ending
64
+ # @param [Time] start
65
+ # @param [Time] ending
66
66
  #
67
- # @return bool
67
+ # @return [Boolean]
68
68
  def in_time_range?(start, ending)
69
69
  effective_trigger = effective_trigger_time
70
70
 
@@ -86,19 +86,7 @@ module Tilia
86
86
  end
87
87
  end
88
88
 
89
- # A simple list of validation rules.
90
- #
91
- # This is simply a list of properties, and how many times they either
92
- # must or must not appear.
93
- #
94
- # Possible values per property:
95
- # * 0 - Must not appear.
96
- # * 1 - Must appear exactly once.
97
- # * + - Must appear at least once.
98
- # * * - Can appear any number of times.
99
- # * ? - May appear, but not more than once.
100
- #
101
- # @var array
89
+ # (see Component#validation_rules)
102
90
  def validation_rules
103
91
  {
104
92
  'ACTION' => 1,
@@ -14,10 +14,10 @@ module Tilia
14
14
  #
15
15
  # https://tools.ietf.org/html/draft-daboo-calendar-availability-05#section-3.1
16
16
  #
17
- # @param DateTimeInterface start
18
- # @param DateTimeInterface ending
17
+ # @param [Time] start
18
+ # @param [Time] ending
19
19
  #
20
- # @return bool
20
+ # @return [Boolean]
21
21
  def in_time_range?(start, ending)
22
22
  (effective_start, effective_end) = effective_start_end
23
23
  (effective_start.nil? || start < effective_end) &&
@@ -33,7 +33,7 @@ module Tilia
33
33
  # If either the start or end is 'unbounded' its value will be null
34
34
  # instead.
35
35
  #
36
- # @return array
36
+ # @return [Array<Time, nil>]
37
37
  def effective_start_end
38
38
  effective_start = nil
39
39
  effective_end = nil
@@ -49,19 +49,7 @@ module Tilia
49
49
  [effective_start, effective_end]
50
50
  end
51
51
 
52
- # A simple list of validation rules.
53
- #
54
- # This is simply a list of properties, and how many times they either
55
- # must or must not appear.
56
- #
57
- # Possible values per property:
58
- # * 0 - Must not appear.
59
- # * 1 - Must appear exactly once.
60
- # * + - Must appear at least once.
61
- # * * - Can appear any number of times.
62
- # * ? - May appear, but not more than once.
63
- #
64
- # @var array
52
+ # (see Component#validation_rules)
65
53
  def validation_rules
66
54
  {
67
55
  'UID' => 1,
@@ -87,28 +75,7 @@ module Tilia
87
75
  }
88
76
  end
89
77
 
90
- # Validates the node for correctness.
91
- #
92
- # The following options are supported:
93
- # Node::REPAIR - May attempt to automatically repair the problem.
94
- # Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
95
- # Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
96
- #
97
- # This method returns an array with detected problems.
98
- # Every element has the following properties:
99
- #
100
- # * level - problem level.
101
- # * message - A human-readable string describing the issue.
102
- # * node - A reference to the problematic node.
103
- #
104
- # The level means:
105
- # 1 - The issue was repaired (only happens if REPAIR was turned on).
106
- # 2 - A warning.
107
- # 3 - An error.
108
- #
109
- # @param int options
110
- #
111
- # @return array
78
+ # (see Component#validate)
112
79
  def validate(options = 0)
113
80
  result = super(options)
114
81
 
@@ -9,13 +9,14 @@ module Tilia
9
9
  #
10
10
  # This should be 'VCALENDAR' or 'VCARD'.
11
11
  #
12
- # @var string
12
+ # @return [String]
13
13
  @default_name = 'VCALENDAR'
14
14
 
15
15
  # This is a list of components, and which classes they should map to.
16
16
  #
17
- # @var array
17
+ # @return [Hash]
18
18
  @component_map = {
19
+ 'VCALENDAR' => Component::VCalendar,
19
20
  'VALARM' => Component::VAlarm,
20
21
  'VEVENT' => Component::VEvent,
21
22
  'VFREEBUSY' => Component::VFreeBusy,
@@ -28,7 +29,7 @@ module Tilia
28
29
 
29
30
  # List of value-types, and which classes they map to.
30
31
  #
31
- # @var array
32
+ # @return [Hash]
32
33
  @value_map = {
33
34
  'BINARY' => Property::Binary,
34
35
  'BOOLEAN' => Property::Boolean,
@@ -49,7 +50,7 @@ module Tilia
49
50
 
50
51
  # List of properties, and which classes they map to.
51
52
  #
52
- # @var array
53
+ # @return [Hash]
53
54
  @property_map = {
54
55
  # Calendar properties
55
56
  'CALSCALE' => Property::FlatText,
@@ -128,9 +129,9 @@ module Tilia
128
129
 
129
130
  # Returns the current document type.
130
131
  #
131
- # @return int
132
+ # @return [Fixnum]
132
133
  def document_type
133
- self.class::ICALENDAR20
134
+ ICALENDAR20
134
135
  end
135
136
 
136
137
  # Returns a list of all 'base components'. For instance, if an Event has
@@ -139,9 +140,9 @@ module Tilia
139
140
  #
140
141
  # VTIMEZONE components will always be excluded.
141
142
  #
142
- # @param string component_name filter by component name
143
+ # @param [String] component_name filter by component name
143
144
  #
144
- # @return VObject\Component[]
145
+ # @return [Array<Component>]
145
146
  def base_components(component_name = nil)
146
147
  is_base_component = lambda do |component|
147
148
  return false unless component.is_a?(Component)
@@ -178,9 +179,9 @@ module Tilia
178
179
  #
179
180
  # If there is no such component, null will be returned.
180
181
  #
181
- # @param string component_name filter by component name
182
+ # @param [String] component_name filter by component name
182
183
  #
183
- # @return VObject\Component|null
184
+ # @return [Component, nil]
184
185
  def base_component(component_name = nil)
185
186
  is_base_component = lambda do |component|
186
187
  return false unless component.is_a?(Component)
@@ -205,6 +206,9 @@ module Tilia
205
206
  nil
206
207
  end
207
208
 
209
+ # Expand all events in this VCalendar object and return a new VCalendar
210
+ # with the expanded events.
211
+ #
208
212
  # If this calendar object, has events with recurrence rules, this method
209
213
  # can be used to expand the event into multiple sub-events.
210
214
  #
@@ -215,43 +219,59 @@ module Tilia
215
219
  # In addition, this method will cause timezone information to be stripped,
216
220
  # and normalized to UTC.
217
221
  #
218
- # This method will alter the VCalendar. This cannot be reversed.
219
- #
220
- # This functionality is specifically used by the CalDAV standard. It is
221
- # possible for clients to request expand events, if they are rather simple
222
- # clients and do not have the possibility to calculate recurrences.
222
+ # @param [Time] start
223
+ # @param [Time] end
224
+ # @param [ActiveSupport::TimeZone, nil] time_zone reference timezone for floating dates and
225
+ # times.
223
226
  #
224
- # @param DateTimeInterface start
225
- # @param DateTimeInterface end
226
- # @param DateTimeZone time_zone reference timezone for floating dates and
227
- # times.
228
- #
229
- # @return void
227
+ # @return [VCalendar]
230
228
  def expand(start, ending, time_zone = nil)
231
- new_events = []
232
-
233
- time_zone = ActiveSupport::TimeZone.new('UTC') unless time_zone
234
-
235
- # An array of events. Events are indexed by UID. Each item in this
236
- # array is a list of one or more events that match the UID.
229
+ new_children = []
237
230
  recurring_events = {}
238
231
 
239
- select('VEVENT').each do |vevent|
240
- uid = vevent['UID'].to_s
241
- fail 'Event did not have a UID!' if uid.blank?
232
+ time_zone = ActiveSupport::TimeZone.new('UTC') unless time_zone
242
233
 
243
- if vevent.key?('RECURRENCE-ID') || vevent.key?('RRULE')
244
- if recurring_events.key?(uid)
245
- recurring_events[uid] << vevent
246
- else
247
- recurring_events[uid] = [vevent]
234
+ strip_timezones = lambda do |component|
235
+ component.children.each do |component_child|
236
+ if component_child.is_a?(Property::ICalendar::DateTime) && component_child.time?
237
+ dt = component_child.date_times(time_zone)
238
+
239
+ # We only need to update the first timezone, because
240
+ # setDateTimes will match all other timezones to the
241
+ # first.
242
+ dt[0] = dt[0].in_time_zone(ActiveSupport::TimeZone.new('UTC'))
243
+ component_child.date_times = dt
244
+ elsif component_child.is_a?(Component)
245
+ strip_timezones.call(component_child)
248
246
  end
249
- next
250
247
  end
251
248
 
252
- unless vevent.key?('RRULE')
253
- new_events << vevent if vevent.in_time_range?(start, ending)
254
- next
249
+ component
250
+ end
251
+
252
+ children.each do |child|
253
+ if child.is_a?(Property) && child.name != 'PRODID'
254
+ # We explictly want to ignore PRODID, because we want to
255
+ # overwrite it with our own.
256
+ new_children << child.clone
257
+ elsif child.is_a?(Component) && child.name != 'VTIMEZONE'
258
+ # We're also stripping all VTIMEZONE objects because we're
259
+ # converting everything to UTC.
260
+
261
+ if child.name == 'VEVENT' && (child.key?('RECURRENCE-ID') || child.key?('RRULE') || child.key?('RDATE'))
262
+ # Handle these a bit later.
263
+ uid = child['UID'].to_s
264
+
265
+ fail InvalidDataException, 'Every VEVENT object must have a UID property' if uid.blank?
266
+
267
+ if recurring_events.key?(uid)
268
+ recurring_events[uid] << child.clone
269
+ else
270
+ recurring_events[uid] = [child.clone]
271
+ end
272
+ elsif child.name == 'VEVENT' && child.in_time_range?(start, ending)
273
+ new_children << strip_timezones.call(child.clone)
274
+ end
255
275
  end
256
276
  end
257
277
 
@@ -268,38 +288,19 @@ module Tilia
268
288
  it.fast_forward(start)
269
289
 
270
290
  while it.valid && it.dt_start < ending
271
- new_events << it.event_object if it.dt_end > start
291
+ new_children << strip_timezones.call(it.event_object) if it.dt_end > start
272
292
  it.next
273
293
  end
274
294
  end
275
295
 
276
- # Wiping out all old VEVENT objects
277
- delete('VEVENT')
278
-
279
- # Setting all properties to UTC time.
280
- new_events.each do |new_event|
281
- new_event.children.each do |child|
282
- next unless child.is_a?(Property::ICalendar::DateTime) && child.time?
283
- dt = child.date_times(time_zone)
284
- # We only need to update the first timezone, because
285
- # setDateTimes will match all other timezones to the
286
- # first.
287
- dt[0] = dt[0].in_time_zone(ActiveSupport::TimeZone.new('UTC'))
288
- child.date_times = dt
289
- end
290
-
291
- add(new_event)
292
- end
293
-
294
- # Removing all VTIMEZONE components
295
- delete('VTIMEZONE')
296
+ self.class.new(new_children)
296
297
  end
297
298
 
298
299
  protected
299
300
 
300
301
  # This method should return a list of default property values.
301
302
  #
302
- # @return array
303
+ # @return [Hash]
303
304
  def defaults
304
305
  {
305
306
  'VERSION' => '2.0',
@@ -310,19 +311,7 @@ module Tilia
310
311
 
311
312
  public
312
313
 
313
- # A simple list of validation rules.
314
- #
315
- # This is simply a list of properties, and how many times they either
316
- # must or must not appear.
317
- #
318
- # Possible values per property:
319
- # * 0 - Must not appear.
320
- # * 1 - Must appear exactly once.
321
- # * + - Must appear at least once.
322
- # * * - Can appear any number of times.
323
- # * ? - May appear, but not more than once.
324
- #
325
- # @var array
314
+ # (see Component#validation_rules)
326
315
  def validation_rules
327
316
  {
328
317
  'PRODID' => 1,
@@ -333,28 +322,7 @@ module Tilia
333
322
  }
334
323
  end
335
324
 
336
- # Validates the node for correctness.
337
- #
338
- # The following options are supported:
339
- # Node::REPAIR - May attempt to automatically repair the problem.
340
- # Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
341
- # Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
342
- #
343
- # This method returns an array with detected problems.
344
- # Every element has the following properties:
345
- #
346
- # * level - problem level.
347
- # * message - A human-readable string describing the issue.
348
- # * node - A reference to the problematic node.
349
- #
350
- # The level means:
351
- # 1 - The issue was repaired (only happens if REPAIR was turned on).
352
- # 2 - A warning.
353
- # 3 - An error.
354
- #
355
- # @param int options
356
- #
357
- # @return array
325
+ # (see Component#validate)
358
326
  def validate(options = 0)
359
327
  warnings = super(options)
360
328
 
@@ -410,7 +378,7 @@ module Tilia
410
378
  }
411
379
  end
412
380
 
413
- if options & self.class::PROFILE_CALDAV > 0
381
+ if options & PROFILE_CALDAV > 0
414
382
  if uid_list.size > 1
415
383
  warnings << {
416
384
  'level' => 3,
@@ -450,7 +418,7 @@ module Tilia
450
418
 
451
419
  # Returns all components with a specific UID value.
452
420
  #
453
- # @return array
421
+ # @return [array]
454
422
  def by_uid(uid)
455
423
  components.select do |item|
456
424
  item_uid = item.select('UID')