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
@@ -1,5 +1,6 @@
1
1
  module Tilia
2
2
  module VObject
3
+ # Namespace of the ITip functionality
3
4
  module ITip
4
5
  require 'tilia/v_object/i_tip/i_tip_exception'
5
6
  require 'tilia/v_object/i_tip/same_organizer_for_all_components_exception'
@@ -37,7 +37,7 @@ module Tilia
37
37
  # server, but if you're writing an iTip application that doesn't deal with
38
38
  # CalDAV, you may want to ignore this parameter.
39
39
  #
40
- # @var bool
40
+ # @return [Boolean]
41
41
  attr_accessor :schedule_agent_server_rules
42
42
 
43
43
  # The broker will try during 'parseEvent' figure out whether the change
@@ -49,7 +49,7 @@ module Tilia
49
49
  # This list is taken from:
50
50
  # * http://tools.ietf.org/html/rfc5546#section-2.1.4
51
51
  #
52
- # @var string[]
52
+ # @return [Array<String>]
53
53
  attr_accessor :significant_change_properties
54
54
 
55
55
  # This method is used to process an incoming itip message.
@@ -78,10 +78,10 @@ module Tilia
78
78
  #
79
79
  # If the iTip message was not supported, we will always return false.
80
80
  #
81
- # @param Message itip_message
82
- # @param VCalendar existing_object
81
+ # @param [Message] itip_message
82
+ # @param [VCalendar] existing_object
83
83
  #
84
- # @return VCalendar|null
84
+ # @return [VCalendar, nil]
85
85
  def process_message(itip_message, existing_object = nil)
86
86
  # We only support events at the moment.
87
87
  return false unless itip_message.component == 'VEVENT'
@@ -118,11 +118,11 @@ module Tilia
118
118
  # people. If the user was an attendee, we need to make sure that the
119
119
  # organizer gets the 'declined' message.
120
120
  #
121
- # @param VCalendar|string calendar
122
- # @param string|array user_href
123
- # @param VCalendar|string old_calendar
121
+ # @param [VCalendar, String] calendar
122
+ # @param [String, Array<String>] user_href
123
+ # @param [VCalendar, String, nil] old_calendar
124
124
  #
125
- # @return array
125
+ # @return [Array]
126
126
  def parse_event(calendar, user_href, old_calendar = nil)
127
127
  if old_calendar
128
128
  if old_calendar.is_a?(String)
@@ -196,11 +196,11 @@ module Tilia
196
196
  else
197
197
  # This is an attendee deleting the event.
198
198
  event_info['attendees'].each do |key, attendee|
199
- if user_href.include?(attendee['href'])
200
- event_info['attendees'][key]['instances'] = {
201
- 'master' => { 'id' => 'master', 'partstat' => 'DECLINED' }
202
- }
203
- end
199
+ next unless user_href.include?(attendee['href'])
200
+
201
+ event_info['attendees'][key]['instances'] = {
202
+ 'master' => { 'id' => 'master', 'partstat' => 'DECLINED' }
203
+ }
204
204
  end
205
205
  end
206
206
 
@@ -220,7 +220,7 @@ module Tilia
220
220
  end
221
221
  end
222
222
 
223
- return []
223
+ []
224
224
  end
225
225
 
226
226
  protected
@@ -231,10 +231,10 @@ module Tilia
231
231
  # invite, or an update to an existing one.
232
232
  #
233
233
  #
234
- # @param Message itip_message
235
- # @param VCalendar existing_object
234
+ # @param [Message] itip_message
235
+ # @param [VCalendar, nil] existing_object
236
236
  #
237
- # @return VCalendar|null
237
+ # @return [VCalendar, nil]
238
238
  def process_message_request(itip_message, existing_object = nil)
239
239
  if !existing_object
240
240
  # This is a new invite, and we're just going to copy over
@@ -264,10 +264,10 @@ module Tilia
264
264
  # attendee got removed from an event, or an event got cancelled
265
265
  # altogether.
266
266
  #
267
- # @param Message itip_message
268
- # @param VCalendar existing_object
267
+ # @param [Message] itip_message
268
+ # @param [VCalendar, nil] existing_object
269
269
  #
270
- # @return VCalendar|null
270
+ # @return [VCalendar, nil]
271
271
  def process_message_cancel(itip_message, existing_object = nil)
272
272
  if !existing_object
273
273
  # The event didn't exist in the first place, so we're just
@@ -287,10 +287,10 @@ module Tilia
287
287
  # The message is a reply. This is for example an attendee telling
288
288
  # an organizer he accepted the invite, or declined it.
289
289
  #
290
- # @param Message itip_message
291
- # @param VCalendar existing_object
290
+ # @param [Message] itip_message
291
+ # @param [VCalendar, nil] existing_object
292
292
  #
293
- # @return VCalendar|null
293
+ # @return [VCalendar, nil]
294
294
  def process_message_reply(itip_message, existing_object = nil)
295
295
  # A reply can only be processed based on an existing object.
296
296
  # If the object is not available, the reply is ignored.
@@ -317,33 +317,33 @@ module Tilia
317
317
  recur_id = vevent.key?('RECURRENCE-ID') ? vevent['RECURRENCE-ID'].value : 'master'
318
318
  master_object = vevent if recur_id == 'master'
319
319
 
320
- if instances.key?(recur_id)
321
- attendee_found = false
322
- if vevent.key?('ATTENDEE')
323
- vevent['ATTENDEE'].each do |attendee|
324
- if attendee.value == itip_message.sender
325
- attendee_found = true
326
- attendee['PARTSTAT'] = instances[recur_id]
327
- attendee['SCHEDULE-STATUS'] = request_status
328
- # Un-setting the RSVP status, because we now know
329
- # that the attendee already replied.
330
- attendee.delete('RSVP')
331
- break
332
- end
333
- end
334
- end
320
+ next unless instances.key?(recur_id)
335
321
 
336
- unless attendee_found
337
- # Adding a new attendee. The iTip documentation calls this
338
- # a party crasher.
339
- attendee = vevent.add('ATTENDEE', itip_message.sender, 'PARTSTAT' => instances[recur_id])
340
- if itip_message.sender_name
341
- attendee['CN'] = itip_message.sender_name
342
- end
322
+ attendee_found = false
323
+ if vevent.key?('ATTENDEE')
324
+ vevent['ATTENDEE'].each do |attendee|
325
+ next unless attendee.value == itip_message.sender
326
+
327
+ attendee_found = true
328
+ attendee['PARTSTAT'] = instances[recur_id]
329
+ attendee['SCHEDULE-STATUS'] = request_status
330
+ # Un-setting the RSVP status, because we now know
331
+ # that the attendee already replied.
332
+ attendee.delete('RSVP')
333
+ break
343
334
  end
335
+ end
344
336
 
345
- instances.delete(recur_id)
337
+ unless attendee_found
338
+ # Adding a new attendee. The iTip documentation calls this
339
+ # a party crasher.
340
+ attendee = vevent.add('ATTENDEE', itip_message.sender, 'PARTSTAT' => instances[recur_id])
341
+ if itip_message.sender_name
342
+ attendee['CN'] = itip_message.sender_name
343
+ end
346
344
  end
345
+
346
+ instances.delete(recur_id)
347
347
  end
348
348
 
349
349
  unless master_object
@@ -380,17 +380,17 @@ module Tilia
380
380
  attendee_found = false
381
381
  if new_object.key?('ATTENDEE')
382
382
  new_object['ATTENDEE'].each do |attendee|
383
- if attendee.value == itip_message.sender
384
- attendee_found = true
385
- attendee['PARTSTAT'] = partstat
386
- break
387
- end
383
+ next unless attendee.value == itip_message.sender
384
+
385
+ attendee_found = true
386
+ attendee['PARTSTAT'] = partstat
387
+ break
388
388
  end
389
389
  end
390
390
 
391
391
  unless attendee_found
392
392
  # Adding a new attendee
393
- attendee = new_object.add('ATTENDEE', itip_message.sender, 'PARTSTAT' => partstat )
393
+ attendee = new_object.add('ATTENDEE', itip_message.sender, 'PARTSTAT' => partstat)
394
394
 
395
395
  if itip_message.sender_name
396
396
  attendee['CN'] = itip_message.sender_name
@@ -410,11 +410,11 @@ module Tilia
410
410
  # We will detect which attendees got added, which got removed and create
411
411
  # specific messages for these situations.
412
412
  #
413
- # @param VCalendar calendar
414
- # @param array event_info
415
- # @param array old_event_info
413
+ # @param [VCalendar] calendar
414
+ # @param [Hash] event_info
415
+ # @param [Hash] old_event_info
416
416
  #
417
- # @return array
417
+ # @return [Array<Message>]
418
418
  def parse_event_for_organizer(calendar, event_info, old_event_info)
419
419
  # Merging attendee lists.
420
420
  attendees = {}
@@ -478,6 +478,12 @@ module Tilia
478
478
  end
479
479
 
480
480
  event.add(calendar['VEVENT']['DTSTART'].clone)
481
+ if calendar['VEVENT'].key?('DTEND')
482
+ event.add(calendar['VEVENT']['DTEND'].clone)
483
+ elsif calendar['VEVENT'].key?('DURATION')
484
+ event.add(calendar['VEVENT']['DURATION'].clone)
485
+ end
486
+
481
487
  org = event.add('ORGANIZER', event_info['organizer'])
482
488
  if event_info['organizer_name']
483
489
  org['CN'] = event_info['organizer_name']
@@ -519,10 +525,10 @@ module Tilia
519
525
  # We need to find a list of events that the attendee
520
526
  # is not a part of to add to the list of exceptions.
521
527
  exceptions = []
522
- event_info['instances'].each do |instance_id, _vevent|
523
- unless attendee['newInstances'].key?(instance_id)
524
- exceptions << instance_id
525
- end
528
+ event_info['instances'].each do |id, _vevent|
529
+ next if attendee['newInstances'].key?(id)
530
+
531
+ exceptions << id
526
532
  end
527
533
 
528
534
  # If there were exceptions, we need to add it to an
@@ -540,15 +546,15 @@ module Tilia
540
546
  current_event['ORGANIZER'].delete('SCHEDULE-FORCE-SEND')
541
547
  current_event['ORGANIZER'].delete('SCHEDULE-STATUS')
542
548
 
543
- current_event['ATTENDEE'].each do |attendee|
544
- attendee.delete('SCHEDULE-FORCE-SEND')
545
- attendee.delete('SCHEDULE-STATUS')
549
+ current_event['ATTENDEE'].each do |event_attendee|
550
+ event_attendee.delete('SCHEDULE-FORCE-SEND')
551
+ event_attendee.delete('SCHEDULE-STATUS')
546
552
 
547
553
  # We're adding PARTSTAT=NEEDS-ACTION to ensure that
548
554
  # iOS shows an "Inbox Item"
549
- unless attendee.key?('PARTSTAT')
550
- attendee['PARTSTAT'] = 'NEEDS-ACTION'
551
- end
555
+ next if event_attendee.key?('PARTSTAT')
556
+
557
+ event_attendee['PARTSTAT'] = 'NEEDS-ACTION'
552
558
  end
553
559
  end
554
560
 
@@ -560,19 +566,19 @@ module Tilia
560
566
  messages << message
561
567
  end
562
568
 
563
- return messages
569
+ messages
564
570
  end
565
571
 
566
572
  # Parse an event update for an attendee.
567
573
  #
568
574
  # This function figures out if we need to send a reply to an organizer.
569
575
  #
570
- # @param VCalendar calendar
571
- # @param array event_info
572
- # @param array old_event_info
573
- # @param string attendee
576
+ # @param [VCalendar] calendar
577
+ # @param [Hash] event_info
578
+ # @param [Hash] old_event_info
579
+ # @param [String] attendee
574
580
  #
575
- # @return Message[]
581
+ # @return [Array<Message>]
576
582
  def parse_event_for_attendee(calendar, event_info, old_event_info, attendee)
577
583
  if schedule_agent_server_rules && event_info['organizer_schedule_agent'] == 'CLIENT'
578
584
  return []
@@ -616,16 +622,16 @@ module Tilia
616
622
  # We only need to do that though, if the master event is not declined.
617
623
  if instances.key?('master') && instances['master']['newstatus'] != 'DECLINED'
618
624
  event_info['exdate'].each do |ex_date|
619
- unless old_event_info['exdate'].include?(ex_date)
620
- if instances.key?(ex_date)
621
- instances[ex_date]['newstatus'] = 'DECLINED'
622
- else
623
- instances[ex_date] = {
624
- 'id' => ex_date,
625
- 'oldstatus' => nil,
626
- 'newstatus' => 'DECLINED'
627
- }
628
- end
625
+ next if old_event_info['exdate'].include?(ex_date)
626
+
627
+ if instances.key?(ex_date)
628
+ instances[ex_date]['newstatus'] = 'DECLINED'
629
+ else
630
+ instances[ex_date] = {
631
+ 'id' => ex_date,
632
+ 'oldstatus' => nil,
633
+ 'newstatus' => 'DECLINED'
634
+ }
629
635
  end
630
636
  end
631
637
  end
@@ -672,6 +678,12 @@ module Tilia
672
678
  instance_obj = event_info['instances'][instance['id']]
673
679
 
674
680
  event.add(instance_obj['DTSTART'].clone)
681
+ if instance_obj.key?('DTEND')
682
+ event.add(instance_obj['DTEND'].clone)
683
+ elsif instance_obj.key?('DURATION')
684
+ event.add(instance_obj['DURATION'].clone)
685
+ end
686
+
675
687
  if instance_obj.key?('SUMMARY')
676
688
  event.add('SUMMARY', instance_obj['SUMMARY'].value)
677
689
  elsif !summary.blank?
@@ -686,9 +698,9 @@ module Tilia
686
698
 
687
699
  # Treat is as a DATE field
688
700
  if instance['id'].size <= 8
689
- recur = event.add('DTSTART', dt, 'VALUE' => 'DATE')
701
+ event.add('DTSTART', dt, 'VALUE' => 'DATE')
690
702
  else
691
- recur = event.add('DTSTART', dt)
703
+ event.add('DTSTART', dt)
692
704
  end
693
705
 
694
706
  event.add('SUMMARY', summary) unless summary.blank?
@@ -698,9 +710,9 @@ module Tilia
698
710
  dt = Tilia::VObject::DateTimeParser.parse(instance['id'], event_info['timezone'])
699
711
  # Treat is as a DATE field
700
712
  if instance['id'].size <= 8
701
- recur = event.add('RECURRENCE-ID', dt, 'VALUE' => 'DATE')
713
+ event.add('RECURRENCE-ID', dt, 'VALUE' => 'DATE')
702
714
  else
703
- recur = event.add('RECURRENCE-ID', dt)
715
+ event.add('RECURRENCE-ID', dt)
704
716
  end
705
717
  end
706
718
 
@@ -736,9 +748,9 @@ module Tilia
736
748
  # 4. attendees
737
749
  # 5. instances
738
750
  #
739
- # @param VCalendar calendar
751
+ # @param [VCalendar] calendar
740
752
  #
741
- # @return array
753
+ # @return [Hash]
742
754
  def parse_event_info(calendar = nil)
743
755
  uid = nil
744
756
  organizer = nil
@@ -780,14 +792,18 @@ module Tilia
780
792
  fail Tilia::VObject::ITip::SameOrganizerForAllComponentsException, 'Every instance of the event must have the same organizer.'
781
793
  end
782
794
  end
783
- organizer_force_send =
784
- vevent['ORGANIZER'].key?('SCHEDULE-FORCE-SEND') ?
785
- vevent['ORGANIZER']['SCHEDULE-FORCE-SEND'].to_s.upcase :
786
- nil
787
- organizer_schedule_agent =
788
- vevent['ORGANIZER'].key?('SCHEDULE-AGENT') ?
789
- vevent['ORGANIZER']['SCHEDULE-AGENT'].to_s.upcase :
790
- 'SERVER'
795
+
796
+ if vevent['ORGANIZER'].key?('SCHEDULE-FORCE-SEND')
797
+ organizer_force_send = vevent['ORGANIZER']['SCHEDULE-FORCE-SEND'].to_s.upcase
798
+ else
799
+ organizer_force_send = nil
800
+ end
801
+
802
+ if vevent['ORGANIZER'].key?('SCHEDULE-AGENT')
803
+ organizer_schedule_agent = vevent['ORGANIZER']['SCHEDULE-AGENT'].to_s.upcase
804
+ else
805
+ organizer_schedule_agent = 'SERVER'
806
+ end
791
807
  end
792
808
 
793
809
  if sequence.nil? && vevent.key?('SEQUENCE')
@@ -796,7 +812,7 @@ module Tilia
796
812
 
797
813
  if vevent.key?('EXDATE')
798
814
  vevent.select('EXDATE').each do |val|
799
- exdate = exdate + val.parts
815
+ exdate += val.parts
800
816
  end
801
817
  exdate.sort!
802
818
  end
@@ -816,15 +832,17 @@ module Tilia
816
832
  next
817
833
  end
818
834
 
819
- part_stat =
820
- attendee.key?('PARTSTAT') ?
821
- attendee['PARTSTAT'].to_s.upcase :
822
- 'NEEDS-ACTION'
835
+ if attendee.key?('PARTSTAT')
836
+ part_stat = attendee['PARTSTAT'].to_s.upcase
837
+ else
838
+ part_stat = 'NEEDS-ACTION'
839
+ end
823
840
 
824
- force_send =
825
- attendee.key?('SCHEDULE-FORCE-SEND') ?
826
- attendee['SCHEDULE-FORCE-SEND'].to_s.upcase :
827
- nil
841
+ if attendee.key?('SCHEDULE-FORCE-SEND')
842
+ force_send = attendee['SCHEDULE-FORCE-SEND'].to_s.upcase
843
+ else
844
+ force_send = nil
845
+ end
828
846
 
829
847
  if attendees.key?(attendee.normalized_value)
830
848
  attendees[attendee.normalized_value]['instances'][recur_id] = {
@@ -851,17 +869,17 @@ module Tilia
851
869
  end
852
870
 
853
871
  significant_change_properties.each do |prop|
854
- if vevent.key?(prop)
855
- property_values = vevent.select(prop)
872
+ next unless vevent.key?(prop)
856
873
 
857
- significant_change_hash += prop + ':'
874
+ property_values = vevent.select(prop)
858
875
 
859
- if prop == 'EXDATE'
860
- significant_change_hash += exdate.join(',') + ';'
861
- else
862
- property_values.each do |val|
863
- significant_change_hash += val.value + ';'
864
- end
876
+ significant_change_hash += prop + ':'
877
+
878
+ if prop == 'EXDATE'
879
+ significant_change_hash += exdate.join(',') + ';'
880
+ else
881
+ property_values.each do |val|
882
+ significant_change_hash += val.value + ';'
865
883
  end
866
884
  end
867
885
  end
@@ -889,7 +907,7 @@ module Tilia
889
907
 
890
908
  public
891
909
 
892
- # TODO: document
910
+ # Sets instance variables
893
911
  def initialize
894
912
  @schedule_agent_server_rules = true
895
913
  @significant_change_properties = [