rubyredrick-ri_cal 0.0.2

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.
Files changed (123) hide show
  1. data/History.txt +3 -0
  2. data/Manifest.txt +122 -0
  3. data/README.txt +271 -0
  4. data/Rakefile +31 -0
  5. data/bin/ri_cal +8 -0
  6. data/component_attributes/alarm.yml +10 -0
  7. data/component_attributes/calendar.yml +4 -0
  8. data/component_attributes/component_property_defs.yml +180 -0
  9. data/component_attributes/event.yml +45 -0
  10. data/component_attributes/freebusy.yml +16 -0
  11. data/component_attributes/journal.yml +35 -0
  12. data/component_attributes/timezone.yml +3 -0
  13. data/component_attributes/timezone_period.yml +11 -0
  14. data/component_attributes/todo.yml +46 -0
  15. data/copyrights.txt +2 -0
  16. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  17. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  18. data/docs/incrementers.txt +7 -0
  19. data/docs/rfc2445.pdf +0 -0
  20. data/lib/ri_cal/component/alarm.rb +22 -0
  21. data/lib/ri_cal/component/calendar.rb +199 -0
  22. data/lib/ri_cal/component/event.rb +30 -0
  23. data/lib/ri_cal/component/freebusy.rb +19 -0
  24. data/lib/ri_cal/component/journal.rb +22 -0
  25. data/lib/ri_cal/component/t_z_info_timezone.rb +124 -0
  26. data/lib/ri_cal/component/timezone/daylight_period.rb +26 -0
  27. data/lib/ri_cal/component/timezone/standard_period.rb +24 -0
  28. data/lib/ri_cal/component/timezone/timezone_period.rb +54 -0
  29. data/lib/ri_cal/component/timezone.rb +193 -0
  30. data/lib/ri_cal/component/todo.rb +26 -0
  31. data/lib/ri_cal/component.rb +224 -0
  32. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  33. data/lib/ri_cal/core_extensions/array.rb +7 -0
  34. data/lib/ri_cal/core_extensions/date/conversions.rb +27 -0
  35. data/lib/ri_cal/core_extensions/date.rb +13 -0
  36. data/lib/ri_cal/core_extensions/date_time/conversions.rb +27 -0
  37. data/lib/ri_cal/core_extensions/date_time.rb +13 -0
  38. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  39. data/lib/ri_cal/core_extensions/object.rb +8 -0
  40. data/lib/ri_cal/core_extensions/string/conversions.rb +32 -0
  41. data/lib/ri_cal/core_extensions/string.rb +8 -0
  42. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  43. data/lib/ri_cal/core_extensions/time/conversions.rb +27 -0
  44. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +88 -0
  45. data/lib/ri_cal/core_extensions/time.rb +11 -0
  46. data/lib/ri_cal/core_extensions.rb +6 -0
  47. data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
  48. data/lib/ri_cal/occurrence_enumerator.rb +172 -0
  49. data/lib/ri_cal/parser.rb +138 -0
  50. data/lib/ri_cal/properties/alarm.rb +390 -0
  51. data/lib/ri_cal/properties/calendar.rb +164 -0
  52. data/lib/ri_cal/properties/event.rb +1526 -0
  53. data/lib/ri_cal/properties/freebusy.rb +594 -0
  54. data/lib/ri_cal/properties/journal.rb +1240 -0
  55. data/lib/ri_cal/properties/timezone.rb +151 -0
  56. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  57. data/lib/ri_cal/properties/todo.rb +1562 -0
  58. data/lib/ri_cal/property_value/array.rb +19 -0
  59. data/lib/ri_cal/property_value/cal_address.rb +12 -0
  60. data/lib/ri_cal/property_value/date.rb +119 -0
  61. data/lib/ri_cal/property_value/date_time/additive_methods.rb +43 -0
  62. data/lib/ri_cal/property_value/date_time/time_machine.rb +180 -0
  63. data/lib/ri_cal/property_value/date_time/timezone_support.rb +65 -0
  64. data/lib/ri_cal/property_value/date_time.rb +324 -0
  65. data/lib/ri_cal/property_value/duration.rb +106 -0
  66. data/lib/ri_cal/property_value/geo.rb +12 -0
  67. data/lib/ri_cal/property_value/integer.rb +13 -0
  68. data/lib/ri_cal/property_value/occurrence_list.rb +82 -0
  69. data/lib/ri_cal/property_value/period.rb +63 -0
  70. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +98 -0
  71. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +77 -0
  72. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +149 -0
  73. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +54 -0
  74. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +32 -0
  75. data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +794 -0
  76. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +132 -0
  77. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +61 -0
  78. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +34 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +50 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +126 -0
  81. data/lib/ri_cal/property_value/recurrence_rule.rb +146 -0
  82. data/lib/ri_cal/property_value/text.rb +41 -0
  83. data/lib/ri_cal/property_value/uri.rb +12 -0
  84. data/lib/ri_cal/property_value/utc_offset.rb +34 -0
  85. data/lib/ri_cal/property_value.rb +110 -0
  86. data/lib/ri_cal/required_timezones.rb +56 -0
  87. data/lib/ri_cal/time_with_floating_timezone.rb +59 -0
  88. data/lib/ri_cal.rb +134 -0
  89. data/ri_cal.gemspec +47 -0
  90. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  91. data/script/console +10 -0
  92. data/script/destroy +14 -0
  93. data/script/generate +14 -0
  94. data/script/txt2html +71 -0
  95. data/spec/ri_cal/component/alarm_spec.rb +13 -0
  96. data/spec/ri_cal/component/calendar_spec.rb +55 -0
  97. data/spec/ri_cal/component/event_spec.rb +157 -0
  98. data/spec/ri_cal/component/freebusy_spec.rb +13 -0
  99. data/spec/ri_cal/component/journal_spec.rb +13 -0
  100. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +37 -0
  101. data/spec/ri_cal/component/timezone_spec.rb +155 -0
  102. data/spec/ri_cal/component/todo_spec.rb +61 -0
  103. data/spec/ri_cal/component_spec.rb +212 -0
  104. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +189 -0
  105. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +46 -0
  106. data/spec/ri_cal/occurrence_enumerator_spec.rb +218 -0
  107. data/spec/ri_cal/parser_spec.rb +304 -0
  108. data/spec/ri_cal/property_value/date_spec.rb +22 -0
  109. data/spec/ri_cal/property_value/date_time_spec.rb +448 -0
  110. data/spec/ri_cal/property_value/duration_spec.rb +80 -0
  111. data/spec/ri_cal/property_value/period_spec.rb +50 -0
  112. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +22 -0
  113. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1815 -0
  114. data/spec/ri_cal/property_value/text_spec.rb +14 -0
  115. data/spec/ri_cal/property_value/utc_offset_spec.rb +49 -0
  116. data/spec/ri_cal/property_value_spec.rb +111 -0
  117. data/spec/ri_cal/required_timezones_spec.rb +68 -0
  118. data/spec/ri_cal_spec.rb +54 -0
  119. data/spec/spec.opts +4 -0
  120. data/spec/spec_helper.rb +24 -0
  121. data/tasks/ri_cal.rake +403 -0
  122. data/tasks/spec.rake +35 -0
  123. metadata +201 -0
@@ -0,0 +1,132 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ # Instances of RecurringDay are used to represent values in BYDAY recurrence rule parts
8
+ #
9
+ class RecurringDay # :nodoc:
10
+
11
+ attr_reader :wday, :index, :rrule
12
+
13
+ DayNames = %w{SU MO TU WE TH FR SA} unless defined? DayNames
14
+ day_nums = {}
15
+ unless defined? DayNums
16
+ DayNames.each_with_index { |name, i| day_nums[name] = i }
17
+ DayNums = day_nums
18
+ end
19
+
20
+ attr_reader :source, :scope
21
+ def initialize(source, rrule, scope = :monthly)
22
+ @source = source
23
+ @rrule = rrule
24
+ @scope = scope
25
+ wd_match = source.match(/([+-]?\d*)(SU|MO|TU|WE|TH|FR|SA)/)
26
+ if wd_match
27
+ @day, @ordinal = wd_match[2], wd_match[1]
28
+ @wday = DayNums[@day]
29
+ @index = (@ordinal == "") ? nil : @ordinal.to_i
30
+ end
31
+ end
32
+
33
+ def valid?
34
+ !@day.nil?
35
+ end
36
+
37
+ def ==(another)
38
+ self.class === another && to_a = another.to_a
39
+ end
40
+
41
+ def to_a
42
+ [@day, @ordinal]
43
+ end
44
+
45
+ # return a list id for a given time to allow the enumerator to cache lists
46
+ def list_id(time)
47
+ case @scope
48
+ when :yearly
49
+ time.year
50
+ when :monthly
51
+ (time.year * 100) + time.month
52
+ when :weekly
53
+ time.at_start_of_week_with_wkst(rrule.wkst_day).jd
54
+ end
55
+ end
56
+
57
+ # return a list of times which match the time parameter within the scope of the RecurringDay
58
+ def matches_for(time)
59
+ case @scope
60
+ when :yearly
61
+ yearly_matches_for(time)
62
+ when :monthly
63
+ monthly_matches_for(time)
64
+ when :weekly
65
+ weekly_matches_for(time)
66
+ else
67
+ walkback = caller.grep(/recurrence/i)
68
+ raise "Logic error!#{@scope.inspect}\n #{walkback.join("\n ")}"
69
+ end
70
+ end
71
+
72
+ def yearly_matches_for(time)
73
+ if @ordinal == ""
74
+ t = time.nth_wday_in_year(1, wday)
75
+ result = []
76
+ year = time.year
77
+ while t.year == year
78
+ result << t
79
+ t = t.advance(:week => 1)
80
+ end
81
+ result
82
+ else
83
+ [time.nth_wday_in_year(@ordinal.to_i, wday)]
84
+ end
85
+ end
86
+
87
+ def monthly_matches_for(time)
88
+ if @ordinal == ""
89
+ t = time.nth_wday_in_month(1, wday)
90
+ result = []
91
+ month = time.month
92
+ while t.month == month
93
+ result << t
94
+ t = t.advance(:days => 7)
95
+ end
96
+ result
97
+ else
98
+ result = [time.nth_wday_in_month(index, wday)]
99
+ result
100
+ end
101
+ end
102
+
103
+ def weekly_matches_for(time)
104
+ date = time.start_of_week_with_wkst(rrule.wkst_day)
105
+ date += 1 while date.wday != wday
106
+ [time.change(:year => date.year, :month => date.month, :day => date.day)]
107
+ end
108
+
109
+ def to_s
110
+ "#{@ordinal}#{@day}"
111
+ end
112
+
113
+ def ordinal_match(date_or_time)
114
+ if @ordinal == "" || @scope == :weekly
115
+ true
116
+ else
117
+ if @scope == :yearly
118
+ date_or_time.nth_wday_in_year?(index, wday)
119
+ else
120
+ date_or_time.nth_wday_in_month?(index, wday)
121
+ end
122
+ end
123
+ end
124
+
125
+ # Determine if a particular date, time, or date_time is included in the recurrence
126
+ def include?(date_or_time)
127
+ date_or_time.wday == wday && ordinal_match(date_or_time)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,61 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ # Instances of RecurringMonthDay represent BYMONTHDAY parts in recurrence rules
8
+ class RecurringMonthDay < NumberedSpan # :nodoc:
9
+
10
+ def last
11
+ 31
12
+ end
13
+
14
+ # return a list id for a given time to allow the enumerator to cache lists
15
+ def list_id(time)
16
+ time.month
17
+ end
18
+
19
+ # return a list of times which match the time parameter within the scope of the RecurringDay
20
+ def matches_for(time)
21
+ [time.change(:day => 1).advance(:days => target_for(time)- 1)]
22
+ end
23
+
24
+ # return a list id for a given time to allow the enumerator to cache lists
25
+ def list_id(time)
26
+ time.month
27
+ end
28
+
29
+ # return a list of times which match the time parameter within the scope of the RecurringDay
30
+ def matches_for(time)
31
+ [time.change(:day => 1).advance(:days => target_for(time)- 1)]
32
+ end
33
+
34
+ def target_date_time_for(date_time)
35
+ matches_for(date_time)[0]
36
+ end
37
+
38
+ # return a list of times which match the time parameter within the scope of the RecurringDay
39
+ def matches_for(time)
40
+ [time.change(:day => 1).advance(:days => target_for(time)- 1)]
41
+ end
42
+
43
+ def target_date_time_for(date_time)
44
+ matches_for(date_time)[0]
45
+ end
46
+
47
+ def target_for(date_or_time)
48
+ if @source > 0
49
+ @source
50
+ else
51
+ date_or_time.days_in_month + @source + 1
52
+ end
53
+ end
54
+
55
+ def include?(date_or_time)
56
+ date_or_time.mday == target_for(date_or_time)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ class RecurringNumberedWeek < NumberedSpan # :nodoc:
8
+ def last
9
+ 53
10
+ end
11
+
12
+ def rule_wkst
13
+ @rule && rule.wkst_day
14
+ end
15
+
16
+ def default_wkst
17
+ rule_wkst || 1
18
+ end
19
+
20
+ def adjusted_iso_weeknum(date_or_time)
21
+ if @source > 0
22
+ @source
23
+ else
24
+ date_or_time.iso_weeks_in_year(wkst) + @source + 1
25
+ end
26
+ end
27
+
28
+ def include?(date_or_time, wkst=default_wkst)
29
+ date_or_time.iso_week_num(wkst) == adjusted_iso_weeknum(date_or_time)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ class RecurringYearDay < NumberedSpan # :nodoc:
8
+
9
+ def last
10
+ 366
11
+ end
12
+
13
+ def leap_year?(year)
14
+ year % 4 == 0 && (year % 400 == 0 || year % 100 != 0)
15
+ end
16
+
17
+
18
+ def length_of_year(year)
19
+ leap_year?(year) ? 366 : 365
20
+ end
21
+
22
+ # return a list id for a given time to allow the enumerator to cache lists
23
+ def list_id(time)
24
+ time.year
25
+ end
26
+
27
+ # return a list of times which match the time parameter within the scope of the RecurringDay
28
+ def matches_for(time)
29
+ [time.change(:month => 1, :day => 1).advance(:days => target_for(time)- 1)]
30
+ end
31
+
32
+ def target_date_time_for(date_time)
33
+ matches_for(date_time)[0]
34
+ end
35
+
36
+ def target_for(date_or_time)
37
+ if @source > 0
38
+ @source
39
+ else
40
+ length_of_year(date_or_time.year) + @source + 1
41
+ end
42
+ end
43
+
44
+ def include?(date_or_time)
45
+ date_or_time.yday == target_for(date_or_time)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,126 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ module Validations #:nodoc:
8
+ # Validate that the parameters of the reciever conform to RFC 2445
9
+ # If errors are found they will be added to the receivers errors
10
+ #
11
+ # Whenever any of the parameters are set, e.g. with:
12
+ # recurrence_rule.count = 2
13
+ # the errors will be reset
14
+ def valid?
15
+ validate if @errors.nil?
16
+ errors.empty?
17
+ end
18
+
19
+ # Return any errors found during validation
20
+ # See #valid?
21
+ def errors
22
+ @errors ||= []
23
+ end
24
+
25
+ def reset_errors # :nodoc:
26
+ @errors = nil
27
+ end
28
+
29
+ # Used by #valid? to validate that the parameters of the reciever conform to RFC 2445
30
+ # If errors are found they will be added to the receivers errors
31
+ #
32
+ # Whenever any of the parameters are set, e.g. with:
33
+ # recurrence_rule.count = 2
34
+ # the errors will be reset
35
+ def validate
36
+ @errors = []
37
+ validate_termination
38
+ validate_freq
39
+ validate_interval
40
+ validate_int_by_list(:bysecond, (0..59))
41
+ validate_int_by_list(:byminute, (0..59))
42
+ validate_int_by_list(:byhour, (0..23))
43
+ validate_int_by_list(:bymonth, (1..12))
44
+ validate_bysetpos
45
+ validate_byday_list
46
+ validate_bymonthday_list
47
+ validate_byyearday_list
48
+ validate_byweekno_list
49
+ validate_wkst
50
+ end
51
+
52
+ def validate_termination
53
+ errors << "COUNT and UNTIL cannot both be specified" if @count && @until
54
+ end
55
+
56
+ def validate_freq
57
+ if @freq
58
+ unless %w{
59
+ SECONDLY MINUTELY HOURLY DAILY
60
+ WEEKLY MONTHLY YEARLY
61
+ }.include?(@freq.upcase)
62
+ errors << "Invalid frequency '#{@freq}'"
63
+ end
64
+ else
65
+ errors << "RecurrenceRule must have a value for FREQ"
66
+ end
67
+ end
68
+
69
+ def validate_interval
70
+ if @interval
71
+ errors << "interval must be a positive integer" unless @interval > 0
72
+ end
73
+ end
74
+
75
+ def validate_wkst
76
+ errors << "#{wkst.inspect} is invalid for wkst" unless %w{MO TU WE TH FR SA SU}.include?(wkst)
77
+ end
78
+
79
+ def validate_int_by_list(which, test)
80
+ vals = by_list[which] || []
81
+ vals.each do |val|
82
+ errors << "#{val} is invalid for #{which}" unless test === val
83
+ end
84
+ end
85
+
86
+ def validate_bysetpos
87
+ vals = by_list[:bysetpos] || []
88
+ vals.each do |val|
89
+ errors << "#{val} is invalid for bysetpos" unless (-366..-1) === val || (1..366) === val
90
+ end
91
+ unless vals.empty?
92
+ errors << "bysetpos cannot be used without another by_xxx rule part" unless by_list.length > 1
93
+ end
94
+ end
95
+
96
+ def validate_byday_list
97
+ days = by_list[:byday] || []
98
+ days.each do |day|
99
+ errors << "#{day.source.inspect} is not a valid day" unless day.valid?
100
+ end
101
+ end
102
+
103
+ def validate_bymonthday_list
104
+ days = by_list[:bymonthday] || []
105
+ days.each do |day|
106
+ errors << "#{day.source.inspect} is not a valid month day" unless day.valid?
107
+ end
108
+ end
109
+
110
+ def validate_byyearday_list
111
+ days = by_list[:byyearday] || []
112
+ days.each do |day|
113
+ errors << "#{day.source.inspect} is not a valid year day" unless day.valid?
114
+ end
115
+ end
116
+
117
+ def validate_byweekno_list
118
+ days = by_list[:byweekno] || []
119
+ days.each do |day|
120
+ errors << "#{day.source.inspect} is not a valid week number" unless day.valid?
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,146 @@
1
+ Dir[File.dirname(__FILE__) + "/recurrence_rule/*.rb"].sort.each do |path|
2
+ require path
3
+ end
4
+
5
+ module RiCal
6
+ class PropertyValue
7
+ #- ©2009 Rick DeNatale
8
+ #- All rights reserved. Refer to the file README.txt for the license
9
+ #
10
+ # RiCal::PropertyValue::RecurrenceRule represents an icalendar Recurrence Rule property value
11
+ # which is defined in
12
+ # rfc 2445 section 4.3.10 pp 40-45
13
+ class RecurrenceRule < PropertyValue
14
+
15
+ def initialize(parent, value_hash) # :nodoc:
16
+ @by_list_hash = {}
17
+ super
18
+ init_by_lists
19
+ @by_list_hash = nil
20
+ end
21
+
22
+ def self.convert(parent, value) #:nodoc:
23
+ if String === value
24
+ result = new(parent, :value => value)
25
+ else
26
+ result = new(parent, value)
27
+ end
28
+ result
29
+ end
30
+
31
+ include Validations
32
+ include InitializationMethods
33
+ include EnumerationSupportMethods
34
+
35
+ # The integer count value of the receiver, or nil
36
+ attr_reader :count
37
+ # The DATE-TIME value of until limit of the receiver, or nil
38
+ attr_reader :until
39
+
40
+ def value=(string) # :nodoc:
41
+ if string
42
+ @value = string
43
+ dup_hash = {}
44
+ string.split(";").each do |value_part|
45
+ initialize_from_value_part(value_part, dup_hash)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Set the frequency of the recurrence rule
51
+ # freq_value:: a String which should be in %w[SECONDLY MINUTELY HOURLY DAILY WEEKLY MONTHLY YEARLY]
52
+ #
53
+ # This method resets the receivers list of errors
54
+ def freq=(freq_value)
55
+ reset_errors
56
+ @freq = freq_value
57
+ end
58
+
59
+ # return the frequency of the rule which will be a string
60
+ def freq
61
+ @freq.upcase
62
+ end
63
+
64
+ # return the starting week day for the recurrence rule, which for a valid instance will be one of
65
+ # "SU", "MO", "TU", "WE", "TH", "FR", or "SA"
66
+ def wkst
67
+ @wkst || 'MO'
68
+ end
69
+
70
+ def wkst_day # :nodoc:
71
+ @wkst_day ||= (%w{SU MO TU WE FR SA}.index(wkst) || 1)
72
+ end
73
+
74
+ # Set the starting week day for the recurrence rule, which should be one of
75
+ # "SU", "MO", "TU", "WE", "TH", "FR", or "SA" for the instance to be valid.
76
+ # The parameter is however case-insensitive.
77
+ #
78
+ # This method resets the receivers list of errors
79
+ def wkst=(value)
80
+ reset_errors
81
+ @wkst = value
82
+ @wkst_day = nil
83
+ end
84
+
85
+ # Set the count parameter of the recurrence rule, the count value will be converted to an integer using to_i
86
+ #
87
+ # This method resets the receivers list of errors
88
+
89
+ def count=(count_value)
90
+ reset_errors
91
+ @count = count_value
92
+ @until = nil unless @count.nil? || @by_list_hash
93
+ end
94
+
95
+ # Set the until parameter of the recurrence rule
96
+ #
97
+ # until_value:: the value to be set, this may be either a string in RFC 2446 Date or DateTime value format
98
+ # Or a Date, Time, DateTime, RiCal::PropertyValue::Date, or RiCal::PropertyValue::DateTime
99
+ #
100
+ def until=(until_value)
101
+ reset_errors
102
+ @until = until_value && until_value.to_ri_cal_date_or_date_time_value
103
+ @count = nil unless @count.nil? || @by_list_hash
104
+ end
105
+
106
+ # return the INTERVAL parameter of the recurrence rule
107
+ # This returns an Integer
108
+ def interval
109
+ @interval ||= 1
110
+ end
111
+
112
+ # Set the INTERVAL parameter of the recurrence rule
113
+ #
114
+ # interval_value:: an Integer
115
+ #
116
+ # This method resets the receivers list of errors
117
+ def interval=(interval_value)
118
+ reset_errors
119
+ @interval = interval_value
120
+ end
121
+
122
+ def value
123
+ @value || to_ical
124
+ end
125
+
126
+ # Return a string containing the RFC 2445 representation of the recurrence rule
127
+ def to_ical
128
+ result = ["FREQ=#{freq}"]
129
+ result << "INTERVAL=#{interval}" unless interval == 1
130
+ result << "COUNT=#{count}" if count
131
+ result << "UNTIL=#{self.until.value}" if self.until
132
+ %w{bysecond byminute byhour byday bymonthday byyearday byweekno bymonth bysetpos}.each do |by_part|
133
+ val = by_list[by_part.to_sym]
134
+ result << "#{by_part.upcase}=#{[val].flatten.join(',')}" if val
135
+ end
136
+ result << "WKST=#{wkst}" unless wkst == "MO"
137
+ result.join(";")
138
+ end
139
+
140
+ # Predicate to determine if the receiver generates a bounded or infinite set of occurrences
141
+ def bounded?
142
+ @count || @until
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,41 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::Text represents an icalendar Text property value
7
+ # which is defined in
8
+ # rfc 2445 section 4.3.11 pp 45-46
9
+ class Text < PropertyValue
10
+
11
+ # Return the string value of the receiver
12
+ def ruby_value
13
+ if value
14
+ value.gsub(/\\[;,nN\\]/) {|match|
15
+ case match[1,1]
16
+ when /[,;\\]/
17
+ match[1,1]
18
+ when 'n', 'N'
19
+ "\n"
20
+ else
21
+ match
22
+ end
23
+ }
24
+ else
25
+ nil
26
+ end
27
+ end
28
+
29
+ def self.convert(parent, string) #:nodoc:
30
+ ical_str = string.gsub(/\n|,|;/) {|match|
31
+ if match == "\n"
32
+ '\n'
33
+ else
34
+ "\\#{match}"
35
+ end
36
+ }
37
+ self.new(parent, :value => ical_str)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::Uri represents an icalendar Uri property value
7
+ # which is defined in
8
+ # rfc 2445 section 4.8.4.6 p 110
9
+ class Uri < PropertyValue
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class UtcOffset < PropertyValue # :nodoc:
7
+ attr_accessor :sign, :hours, :minutes, :seconds
8
+
9
+ def value=(string)
10
+ @value = string
11
+ parse_match = /([+-])(\d\d)(\d\d)(\d\d)?/.match(string)
12
+ if parse_match
13
+ @sign = parse_match[1] == "+" ? 1 : -1
14
+ @hours = parse_match[2].to_i
15
+ @minutes = parse_match[3].to_i
16
+ @seconds = parse_match[4].to_i || 0
17
+ end
18
+ end
19
+
20
+ def to_seconds
21
+ @sign * ((((hours*60) + minutes) * 60) + seconds)
22
+ end
23
+
24
+ def add_to_date_time_value(date_time_value)
25
+ date_time_value.advance(:hours => sign * hours, :minutes => sign * minutes, :seconds => sign * minutes)
26
+ end
27
+
28
+ def subtract_from_date_time_value(date_time_value)
29
+ signum = -1 * sign
30
+ date_time_value.advance(:hours => signum * hours, :minutes => signum * minutes, :seconds => signum * minutes)
31
+ end
32
+ end
33
+ end
34
+ end