icalendar 0.96.4 → 0.97

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/README +66 -53
  2. data/Rakefile +33 -24
  3. data/docs/api/classes/Array.html +9 -11
  4. data/docs/api/classes/Array.src/M000002.html +18 -0
  5. data/docs/api/classes/Date.html +2 -22
  6. data/docs/api/classes/Date.src/M000004.html +29 -0
  7. data/docs/api/classes/DateTime.html +2 -43
  8. data/docs/api/classes/DateTime.src/M000005.html +50 -0
  9. data/docs/api/classes/Fixnum.html +2 -11
  10. data/docs/api/classes/Fixnum.src/M000011.html +18 -0
  11. data/docs/api/classes/Float.html +2 -11
  12. data/docs/api/classes/Float.src/M000003.html +18 -0
  13. data/docs/api/classes/HashAttrs.html +15 -60
  14. data/docs/api/classes/HashAttrs.src/M000068.html +26 -0
  15. data/docs/api/classes/HashAttrs.src/M000069.html +27 -0
  16. data/docs/api/classes/HashAttrs.src/M000070.html +19 -0
  17. data/docs/api/classes/Icalendar/Alarm.html +5 -15
  18. data/docs/api/classes/Icalendar/Alarm.src/M000020.html +22 -0
  19. data/docs/api/classes/Icalendar/Base.html +41 -0
  20. data/docs/api/classes/Icalendar/Base.src/M000022.html +18 -0
  21. data/docs/api/classes/Icalendar/Base.src/M000023.html +18 -0
  22. data/docs/api/classes/Icalendar/Calendar.html +116 -116
  23. data/docs/api/classes/Icalendar/Calendar.src/M000054.html +23 -0
  24. data/docs/api/classes/Icalendar/Calendar.src/M000055.html +23 -0
  25. data/docs/api/classes/Icalendar/Calendar.src/M000056.html +18 -0
  26. data/docs/api/classes/Icalendar/Calendar.src/M000057.html +23 -0
  27. data/docs/api/classes/Icalendar/Calendar.src/M000058.html +18 -0
  28. data/docs/api/classes/Icalendar/Calendar.src/M000059.html +23 -0
  29. data/docs/api/classes/Icalendar/Calendar.src/M000060.html +18 -0
  30. data/docs/api/classes/Icalendar/Calendar.src/M000061.html +23 -0
  31. data/docs/api/classes/Icalendar/Calendar.src/M000062.html +18 -0
  32. data/docs/api/classes/Icalendar/Calendar.src/M000063.html +23 -0
  33. data/docs/api/classes/Icalendar/Calendar.src/M000064.html +19 -0
  34. data/docs/api/classes/Icalendar/Component.html +200 -367
  35. data/docs/api/classes/Icalendar/Component.src/M000029.html +22 -0
  36. data/docs/api/classes/Icalendar/Component.src/M000030.html +24 -0
  37. data/docs/api/classes/Icalendar/Component.src/M000035.html +22 -0
  38. data/docs/api/classes/Icalendar/Component.src/M000040.html +18 -0
  39. data/docs/api/classes/Icalendar/Component.src/M000041.html +24 -0
  40. data/docs/api/classes/Icalendar/Component.src/M000042.html +28 -0
  41. data/docs/api/classes/Icalendar/Component.src/M000043.html +37 -0
  42. data/docs/api/classes/Icalendar/Component.src/M000044.html +37 -0
  43. data/docs/api/classes/Icalendar/Component.src/M000045.html +18 -0
  44. data/docs/api/classes/Icalendar/Component.src/M000046.html +18 -0
  45. data/docs/api/classes/Icalendar/Component.src/M000047.html +18 -0
  46. data/docs/api/classes/Icalendar/Component.src/M000048.html +26 -0
  47. data/docs/api/classes/Icalendar/Component.src/M000049.html +29 -0
  48. data/docs/api/classes/Icalendar/Component.src/M000050.html +22 -0
  49. data/docs/api/classes/Icalendar/DateProp.html +2 -12
  50. data/docs/api/classes/Icalendar/DateProp.src/M000013.html +19 -0
  51. data/docs/api/classes/Icalendar/DateProp/ClassMethods.html +2 -59
  52. data/docs/api/classes/Icalendar/DateProp/ClassMethods.src/M000014.html +66 -0
  53. data/docs/api/classes/Icalendar/Daylight.html +5 -14
  54. data/docs/api/classes/Icalendar/Daylight.src/M000051.html +18 -0
  55. data/docs/api/classes/Icalendar/Event.html +30 -37
  56. data/docs/api/classes/Icalendar/Event.src/M000052.html +23 -0
  57. data/docs/api/classes/Icalendar/Event.src/M000053.html +23 -0
  58. data/docs/api/classes/Icalendar/Freebusy.html +5 -14
  59. data/docs/api/classes/Icalendar/Freebusy.src/M000065.html +21 -0
  60. data/docs/api/classes/Icalendar/Geo.html +192 -0
  61. data/docs/api/classes/Icalendar/Geo.src/M000024.html +19 -0
  62. data/docs/api/classes/Icalendar/Geo.src/M000025.html +18 -0
  63. data/docs/api/classes/Icalendar/Journal.html +2 -11
  64. data/docs/api/classes/Icalendar/Journal.src/M000016.html +22 -0
  65. data/docs/api/classes/Icalendar/Parameter.html +5 -23
  66. data/docs/api/classes/Icalendar/Parameter.src/M000066.html +27 -0
  67. data/docs/api/classes/Icalendar/Parser.html +36 -212
  68. data/docs/api/classes/Icalendar/Parser.src/M000017.html +32 -0
  69. data/docs/api/classes/Icalendar/Parser.src/M000018.html +43 -0
  70. data/docs/api/classes/Icalendar/Parser.src/M000019.html +33 -0
  71. data/docs/api/classes/Icalendar/Standard.html +2 -11
  72. data/docs/api/classes/Icalendar/Standard.src/M000015.html +18 -0
  73. data/docs/api/classes/Icalendar/Timezone.html +16 -48
  74. data/docs/api/classes/Icalendar/Timezone.src/M000026.html +19 -0
  75. data/docs/api/classes/Icalendar/Timezone.src/M000027.html +22 -0
  76. data/docs/api/classes/Icalendar/Timezone.src/M000028.html +18 -0
  77. data/docs/api/classes/Icalendar/Todo.html +5 -14
  78. data/docs/api/classes/Icalendar/Todo.src/M000021.html +22 -0
  79. data/docs/api/classes/Object.html +10 -59
  80. data/docs/api/classes/Object.src/M000006.html +20 -0
  81. data/docs/api/classes/Object.src/M000007.html +18 -0
  82. data/docs/api/classes/Object.src/M000008.html +20 -0
  83. data/docs/api/classes/Object.src/M000009.html +18 -0
  84. data/docs/api/classes/Object.src/M000010.html +18 -0
  85. data/docs/api/classes/String.html +2 -11
  86. data/docs/api/classes/String.src/M000012.html +18 -0
  87. data/docs/api/classes/Time.html +2 -26
  88. data/docs/api/classes/Time.src/M000001.html +37 -0
  89. data/docs/api/classes/URI/Generic.html +5 -14
  90. data/docs/api/classes/URI/Generic.src/M000067.html +18 -0
  91. data/docs/api/created.rid +1 -1
  92. data/docs/api/files/COPYING.html +1 -1
  93. data/docs/api/files/GPL.html +1 -1
  94. data/docs/api/files/README.html +76 -64
  95. data/docs/api/files/lib/hash_attrs_rb.html +1 -1
  96. data/docs/api/files/lib/icalendar/base_rb.html +1 -1
  97. data/docs/api/files/lib/icalendar/calendar_rb.html +1 -1
  98. data/docs/api/files/lib/icalendar/component/alarm_rb.html +1 -1
  99. data/docs/api/files/lib/icalendar/component/event_rb.html +1 -1
  100. data/docs/api/files/lib/icalendar/component/freebusy_rb.html +1 -1
  101. data/docs/api/files/lib/icalendar/component/journal_rb.html +1 -1
  102. data/docs/api/files/lib/icalendar/component/timezone_rb.html +1 -1
  103. data/docs/api/files/lib/icalendar/component/todo_rb.html +1 -1
  104. data/docs/api/files/lib/icalendar/component_rb.html +8 -1
  105. data/docs/api/files/lib/icalendar/conversions_rb.html +1 -1
  106. data/docs/api/files/lib/icalendar/helpers_rb.html +1 -1
  107. data/docs/api/files/lib/icalendar/parameter_rb.html +1 -1
  108. data/docs/api/files/lib/icalendar/parser_rb.html +2 -1
  109. data/docs/api/files/lib/icalendar_rb.html +1 -1
  110. data/docs/api/files/lib/meta_rb.html +1 -1
  111. data/docs/api/fr_class_index.html +1 -0
  112. data/docs/api/fr_method_index.html +57 -41
  113. data/docs/rfcs/itip_notes.txt +69 -0
  114. data/examples/create_cal.rb +33 -32
  115. data/examples/parse_cal.rb +2 -2
  116. data/lib/icalendar/base.rb +8 -0
  117. data/lib/icalendar/calendar.rb +32 -3
  118. data/lib/icalendar/component.rb +187 -113
  119. data/lib/icalendar/component/alarm.rb +3 -0
  120. data/lib/icalendar/component/event.rb +9 -3
  121. data/lib/icalendar/component/freebusy.rb +3 -0
  122. data/lib/icalendar/component/journal.rb +4 -0
  123. data/lib/icalendar/component/todo.rb +4 -0
  124. data/lib/icalendar/conversions.rb +7 -1
  125. data/lib/icalendar/parser.rb +68 -90
  126. data/test/calendar_test.rb +32 -8
  127. data/test/component/event_test.rb +4 -7
  128. data/test/component/todo_test.rb +13 -0
  129. data/test/component_test.rb +21 -34
  130. data/test/conversions_test.rb +66 -0
  131. data/test/coverage/-home-rosejn-projects-icalendar-lib-hash_attrs_rb.html +574 -0
  132. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-base_rb.html +575 -0
  133. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-calendar_rb.html +629 -0
  134. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-alarm_rb.html +581 -0
  135. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-event_rb.html +658 -0
  136. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-freebusy_rb.html +574 -0
  137. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-journal_rb.html +597 -0
  138. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-timezone_rb.html +625 -0
  139. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component-todo_rb.html +600 -0
  140. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-component_rb.html +924 -0
  141. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-conversions_rb.html +667 -0
  142. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar-parser_rb.html +938 -0
  143. data/test/coverage/-home-rosejn-projects-icalendar-lib-icalendar_rb.html +576 -0
  144. data/test/coverage/-home-rosejn-projects-icalendar-lib-meta_rb.html +572 -0
  145. data/test/coverage/calendar_test_rb.html +590 -0
  146. data/test/coverage/component_test_rb.html +602 -0
  147. data/test/coverage/index.html +1009 -0
  148. data/test/coverage/lib-hash_attrs_rb.html +574 -0
  149. data/test/coverage/lib-icalendar-base_rb.html +583 -0
  150. data/test/coverage/lib-icalendar-calendar_rb.html +631 -0
  151. data/test/coverage/lib-icalendar-component-alarm_rb.html +581 -0
  152. data/test/coverage/lib-icalendar-component-event_rb.html +661 -0
  153. data/test/coverage/lib-icalendar-component-freebusy_rb.html +576 -0
  154. data/test/coverage/lib-icalendar-component-journal_rb.html +599 -0
  155. data/test/coverage/lib-icalendar-component-timezone_rb.html +625 -0
  156. data/test/coverage/lib-icalendar-component-todo_rb.html +602 -0
  157. data/test/coverage/lib-icalendar-component_rb.html +941 -0
  158. data/test/coverage/lib-icalendar-conversions_rb.html +673 -0
  159. data/test/coverage/lib-icalendar-parser_rb.html +916 -0
  160. data/test/coverage/lib-icalendar_rb.html +576 -0
  161. data/test/coverage/lib-meta_rb.html +572 -0
  162. data/test/coverage/parameter_test_rb.html +569 -0
  163. data/test/coverage/parser_test_rb.html +623 -0
  164. data/test/coverage/test-calendar_test_rb.html +590 -0
  165. data/test/coverage/test-component-event_test_rb.html +584 -0
  166. data/test/coverage/test-component_test_rb.html +606 -0
  167. data/test/coverage/test-conversions_test_rb.html +603 -0
  168. data/test/coverage/test-parameter_test_rb.html +569 -0
  169. data/test/coverage/test-parser_test_rb.html +624 -0
  170. data/test/interactive.rb +3 -1
  171. data/test/life.ics +46 -1092
  172. data/test/parameter_test.rb +11 -2
  173. data/test/parser_test.rb +12 -11
  174. metadata +259 -126
  175. data/test/old_life.ics +0 -46
  176. data/test/rw.ics +0 -947
@@ -10,11 +10,11 @@ cal_file = File.open("../test/life.ics")
10
10
 
11
11
  # Parser returns an array of calendars because a single file
12
12
  # can have multiple calendar objects.
13
- cals = Icalendar::Parser.new(cal_file).parse
13
+ cals = Icalendar::parse(cal_file)
14
14
  cal = cals.first
15
15
 
16
16
  # Now you can access the cal object in just the same way I created it
17
17
  event = cal.events.first
18
18
 
19
- puts "user_id: " + event.user_id
19
+ puts "start date-time: " + event.dtstart.to_s
20
20
  puts "summary: " + event.summary
@@ -31,5 +31,13 @@ module Icalendar #:nodoc:
31
31
  class Base
32
32
  @@logger = Logger.new(STDERR)
33
33
  @@logger.level = Logger::FATAL
34
+
35
+ def self.debug
36
+ @@logger.level = Logger::DEBUG
37
+ end
38
+
39
+ def self.quiet
40
+ @@logger.level = Logger::FATAL
41
+ end
34
42
  end
35
43
  end
@@ -18,12 +18,10 @@ module Icalendar
18
18
 
19
19
  def initialize()
20
20
  super("VCALENDAR")
21
- @properties = {}
22
- @property_params = {}
23
21
 
24
22
  # Set some defaults
25
23
  self.calscale = "GREGORIAN" # Who knows, but this is the only one in the spec.
26
- self.prodid = "iCalendar-Ruby" # Current product... Should be overwritten
24
+ self.prodid = "iCalendar-Ruby" # Current product... Should be overwritten by apps that use the library
27
25
  self.version = "2.0" # Version of the specification
28
26
  end
29
27
 
@@ -35,6 +33,10 @@ module Icalendar
35
33
 
36
34
  e
37
35
  end
36
+
37
+ def find_event(uid)
38
+ self.events.find {|e| e.uid == uid}
39
+ end
38
40
 
39
41
  def todo(&block)
40
42
  e = Todo.new
@@ -45,6 +47,10 @@ module Icalendar
45
47
  e
46
48
  end
47
49
 
50
+ def find_todo(uid)
51
+ self.todos.find {|t| t.uid == uid}
52
+ end
53
+
48
54
  def journal(&block)
49
55
  e = Journal.new
50
56
  self.add_component e
@@ -54,6 +60,10 @@ module Icalendar
54
60
  e
55
61
  end
56
62
 
63
+ def find_journal(uid)
64
+ self.journals.find {|j| j.uid == uid}
65
+ end
66
+
57
67
  def freebusy(&block)
58
68
  e = Freebusy.new
59
69
  self.add_component e
@@ -63,6 +73,10 @@ module Icalendar
63
73
  e
64
74
  end
65
75
 
76
+ def find_freebusy(uid)
77
+ self.freebusys.find {|f| f.uid == uid}
78
+ end
79
+
66
80
  def timezone(&block)
67
81
  e = Timezone.new
68
82
  self.add_component e
@@ -71,6 +85,21 @@ module Icalendar
71
85
 
72
86
  e
73
87
  end
88
+
89
+ # The "PUBLISH" method in a "VEVENT" calendar component is an
90
+ # unsolicited posting of an iCalendar object. Any CU may add published
91
+ # components to their calendar. The "Organizer" MUST be present in a
92
+ # published iCalendar component. "Attendees" MUST NOT be present. Its
93
+ # expected usage is for encapsulating an arbitrary event as an
94
+ # iCalendar object. The "Organizer" may subsequently update (with
95
+ # another "PUBLISH" method), add instances to (with an "ADD" method),
96
+ # or cancel (with a "CANCEL" method) a previously published "VEVENT"
97
+ # calendar component.
98
+ def publish
99
+ ip_method = "PUBLISH"
100
+
101
+ end
102
+
74
103
  end # class Calendar
75
104
 
76
105
  end # module Icalendar
@@ -7,6 +7,23 @@
7
7
  =end
8
8
 
9
9
  module Icalendar
10
+ require 'socket'
11
+
12
+ class Geo < Icalendar::Base
13
+ attr_accessor :latitude, :longitude
14
+ alias :lat :latitude
15
+ alias :long :longitude
16
+
17
+ def initialize(lat, long)
18
+ @lat = lat
19
+ @long = long
20
+ end
21
+
22
+ def to_ical
23
+ "#{@lat.to_ical};#{@long.to_ical}"
24
+ end
25
+ end
26
+
10
27
  # The body of the iCalendar object consists of a sequence of calendar
11
28
  # properties and one or more calendar components. The calendar
12
29
  # properties are attributes that apply to the calendar as a whole. The
@@ -19,7 +36,7 @@ module Icalendar
19
36
  meta_include HashAttrs
20
37
 
21
38
  attr_reader :name
22
- attr_accessor :properties, :property_params
39
+ attr_accessor :properties
23
40
 
24
41
  @@multi_properties = {}
25
42
 
@@ -27,7 +44,6 @@ module Icalendar
27
44
  @name = name
28
45
  @components = Hash.new([])
29
46
  @properties = {}
30
- @property_params = {}
31
47
 
32
48
  @@logger.info("New #{@name[1,@name.size].capitalize}...")
33
49
  end
@@ -43,37 +59,74 @@ module Icalendar
43
59
  @components[key] << component
44
60
  end
45
61
 
62
+ # Add a component to the calendar.
46
63
  alias add add_component
47
64
 
65
+ # Add an event to the calendar.
66
+ alias add_event add_component
67
+
68
+ # Add a todo item to the calendar.
69
+ alias add_todo add_component
70
+
71
+ # Add a journal item to the calendar.
72
+ alias add_journal add_component
73
+
74
+ def remove_component(component)
75
+ key = (component.class.to_s.downcase + 's').gsub('icalendar::', '').to_sym
76
+
77
+ if @components.has_key? key
78
+ @components[key].delete(component)
79
+ end
80
+ end
81
+
82
+ # Remove a component from the calendar.
83
+ alias remove remove_component
84
+
85
+ # Remove an event from the calendar.
86
+ alias remove_event remove_component
87
+
88
+ # Remove a todo item from the calendar.
89
+ alias remove_todo remove_component
90
+
91
+ # Remove a journal item from the calendar.
92
+ alias remove_journal remove_component
93
+
94
+ # Used to generate unique component ids
95
+ def new_uid
96
+ "#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}"
97
+ end
98
+
99
+ # Output in the icalendar format
48
100
  def to_ical
49
- print_component do |s|
101
+ print_component do
102
+ s = ""
50
103
  @components.each_value do |comps|
51
104
  comps.each { |component| s << component.to_ical }
52
105
  end
106
+ s
53
107
  end
54
108
  end
55
109
 
56
110
  # Print this icalendar component
57
111
  def print_component
58
- s = ""
59
-
60
112
  # Begin a new component
61
- s << "BEGIN:#{@name.upcase}\r\n"
113
+ "BEGIN:#{@name.upcase}\r\n" +
62
114
 
63
115
  # Then the properties
64
- print_properties(s)
116
+ print_properties +
65
117
 
66
- # Any custom body of the derived component
67
- yield(s)
118
+ # sub components
119
+ yield +
68
120
 
69
121
  # End of this component
70
- s << "END:#{@name.upcase}\r\n"
122
+ "END:#{@name.upcase}\r\n"
71
123
  end
72
124
 
73
125
  # Print this components properties
74
- def print_properties(s)
75
- @properties.each do |key,value|
126
+ def print_properties
127
+ s = ""
76
128
 
129
+ @properties.each do |key,val|
77
130
  # Take out underscore for property names that conflicted
78
131
  # with built-in words.
79
132
  if key =~ /ip_.*/
@@ -81,47 +134,51 @@ module Icalendar
81
134
  end
82
135
 
83
136
  # Property name
84
- s << "#{key.upcase}"
137
+ s << "#{key.upcase}" +
85
138
 
86
139
  # Possible parameters
87
- print_parameter(s, key, value)
140
+ print_parameters(val) +
88
141
 
89
142
  # Property value
90
- s << ":#{value.to_ical}\r\n"
143
+ ":#{val.to_ical}\r\n"
91
144
  end
145
+
146
+ s
92
147
  end
93
148
 
94
149
  # Print the parameters for a specific property
95
- def print_parameter(s, key, value)
96
- if @property_params.has_key?(key)
97
- params = @property_params[key]
98
- params.each do |key,val|
99
- s << ";#{key}"
100
- val = [ val ] unless val.respond_to?(:to_ary)
101
-
102
- # Possible parameter values
103
- unless val.empty?
104
- s << "="
105
- sep = "" # First entry comes after = sign, but then we need commas
106
- val.each do |pval|
107
- if pval.respond_to? :to_ical
108
- s << sep << pval.to_ical
109
- sep = ","
110
- end
150
+ def print_parameters(val)
151
+ s = ""
152
+ return s unless val.respond_to?(:ical_params) and not val.ical_params.nil?
153
+
154
+ val.ical_params.each do |key,val|
155
+ s << ";#{key}"
156
+ val = [ val ] unless val.is_a?(Array)
157
+
158
+ # Possible parameter values
159
+ unless val.empty?
160
+ s << "="
161
+ sep = "" # First entry comes after = sign, but then we need commas
162
+ val.each do |pval|
163
+ if pval.respond_to? :to_ical
164
+ s << sep << pval.to_ical
165
+ sep = ","
111
166
  end
112
167
  end
113
-
114
168
  end
115
169
  end
170
+ s
116
171
  end
117
172
 
118
173
  # TODO: Look into the x-property, x-param stuff...
174
+ # This would really only be needed for subclassing to add additional
175
+ # properties to an application using the API.
119
176
  def custom_property(name, value)
120
177
  @properties[name] = value
121
178
  end
122
179
 
123
180
  def multi_property?(name)
124
- @@multi_properties.has_key?(name.upcase)
181
+ @@multi_properties.has_key?(name.downcase)
125
182
  end
126
183
 
127
184
  # Make it protected so we can monitor usage...
@@ -133,107 +190,114 @@ module Icalendar
133
190
 
134
191
  # Define a set of methods supporting a new property
135
192
  def Component.ical_property(property, alias_name = nil, prop_name = nil)
136
-
137
193
  property = "#{property}".strip.downcase
138
- getter = "#{property}"
139
- setter = "#{property}="
140
- query = "#{property}?"
141
194
  alias_name = "#{alias_name}".strip.downcase unless alias_name.nil?
142
195
 
143
196
  # If a prop_name was given then we use that for the actual storage
144
- property = "#{prop_name}".strip.upcase unless prop_name.nil?
197
+ property = "#{prop_name}".strip.downcase unless prop_name.nil?
145
198
 
146
- # Change underscores in property name to a dash
147
- property[/_/] = '-' if property =~ /_/
199
+ generate_getter(property, alias_name)
200
+ generate_setter(property, alias_name)
201
+ generate_query(property, alias_name)
202
+ end
203
+
204
+ # Define a set of methods defining a new property, which
205
+ # supports multiple values for the same property name.
206
+ def Component.ical_multi_property(property, singular, plural)
207
+ property = "#{property}".strip.downcase.gsub(/-/, '_')
208
+ plural = "#{plural}".strip.downcase
148
209
 
149
- # All properties names are stored in caps...
150
- property = property.upcase
210
+ # Set this key so the parser knows to use an array for
211
+ # storing this property type.
212
+ @@multi_properties["#{property}"] = true
151
213
 
152
- unless instance_methods.include? getter
153
- code = <<-code
154
- def #{getter}(val = nil, params = {})
155
- if val.nil?
156
- @properties["#{property}"]
157
- else
158
- # Set the value just like normal
159
- self.#{setter}(val)
160
- end
161
- end
162
- code
214
+ generate_multi_getter(property, plural)
215
+ generate_multi_setter(property, plural)
216
+ generate_multi_query(property, plural)
217
+ generate_multi_adder(property, singular)
218
+ generate_multi_remover(property, singular)
219
+ end
163
220
 
164
- class_eval code, "component.rb", 155
165
- alias_method("#{alias_name}", "#{getter}") unless alias_name.nil?
166
- end
221
+ private
167
222
 
168
- unless instance_methods.include? setter
223
+ def Component.generate_getter(property, alias_name)
224
+ unless instance_methods.include? property
169
225
  code = <<-code
170
- def #{setter}(val)
226
+ def #{property}(val = nil, params = nil)
227
+ return @properties["#{property}"] if val.nil?
228
+
171
229
  unless val.respond_to?(:to_ical)
172
230
  raise(NotImplementedError, "Value of type (" + val.class.to_s + ") does not support to_ical method!")
173
231
  end
174
232
 
233
+ unless params.nil?
234
+ # Extend with the parameter methods only if we have to...
235
+ unless val.respond_to?(:ical_params)
236
+ val.class.class_eval { attr_accessor :ical_params }
237
+ end
238
+ val.ical_params = params
239
+ end
240
+
175
241
  @properties["#{property}"] = val
176
242
  end
177
243
  code
178
244
 
179
- class_eval code, "component.rb", 171
245
+ class_eval code, "component.rb", 219
246
+ alias_method("#{alias_name}", "#{property}") unless alias_name.nil?
247
+ end
248
+ end
249
+
250
+ def Component.generate_setter(property, alias_name)
251
+ setter = property + '='
252
+ unless instance_methods.include? setter
253
+ code = <<-code
254
+ def #{setter}(val)
255
+ #{property}(val)
256
+ end
257
+ code
180
258
 
181
- alias_method("#{alias_name}=", "#{setter}") unless alias_name.nil?
259
+ class_eval code, "component.rb", 233
260
+ alias_method("#{alias_name}=", "#{property+'='}") unless alias_name.nil?
182
261
  end
262
+ end
183
263
 
264
+ def Component.generate_query(property, alias_name)
265
+ query = "#{property}?"
184
266
  unless instance_methods.include? query
185
267
  code = <<-code
186
268
  def #{query}
187
- @properties.has_key?("#{property}")
269
+ @properties.has_key?("#{property.downcase}")
188
270
  end
189
271
  code
190
272
 
191
- class_eval code, "component.rb", 183
273
+ class_eval code, "component.rb", 226
192
274
 
193
275
  alias_method("#{alias_name}\?", "#{query}") unless alias_name.nil?
194
276
  end
195
277
  end
196
278
 
197
- # Define a set of methods defining a new property, which
198
- # supports multiple values for the same property name.
199
- def Component.ical_multi_property(property, singular, plural)
200
- property = "#{property}".strip.upcase
201
- singular = "#{singular}".strip.downcase
202
- plural = "#{plural}".strip.downcase
203
-
204
- getter = "#{plural}"
205
- setter = "#{plural}="
206
- adder = "add_#{singular}"
207
- remover = "remove_#{singular}"
208
- query = "#{plural}?"
209
-
210
- # Set this key so the parser knows to use an array for
211
- # storing this property type.
212
- @@multi_properties["#{property}"] = true
213
-
279
+ def Component.generate_multi_getter(property, plural)
214
280
  # Getter for whole array
215
- unless instance_methods.include? getter
281
+ unless instance_methods.include? plural
216
282
  code = <<-code
217
- def #{getter}(*a)
218
- if a.empty?
219
- if @properties.has_key?("#{property}")
220
- @properties["#{property}"]
221
- else
222
- []
223
- end
283
+ def #{plural}(a = nil)
284
+ if a.nil?
285
+ @properties["#{property}"] || []
224
286
  else
225
- self.#{setter}(a.first)
287
+ self.#{plural}=(a)
226
288
  end
227
289
  end
228
290
  code
229
291
 
230
292
  class_eval code, "component.rb", 186
231
293
  end
294
+ end
232
295
 
296
+ def Component.generate_multi_setter(property, plural)
233
297
  # Setter for whole array
234
- unless instance_methods.include? setter
298
+ unless instance_methods.include? plural+'+'
235
299
  code = <<-code
236
- def #{setter}(a)
300
+ def #{plural}=(a)
237
301
  if a.respond_to?(:to_ary)
238
302
  a.to_ary.each do |val|
239
303
  unless val.respond_to?(:to_ical)
@@ -242,27 +306,31 @@ module Icalendar
242
306
  end
243
307
 
244
308
  @properties["#{property}"] = a.to_ary
245
-
246
309
  else
247
- raise ArgumentError, "#{getter} is a multi-property that must be an array! Use the #{adder} method to add single entries."
310
+ raise ArgumentError, "#{plural} is a multi-property that must be an array! Use the add_[property] method to add single entries."
248
311
  end
249
312
  end
250
313
  code
251
314
 
252
315
  class_eval code, "component.rb", 198
253
316
  end
317
+ end
254
318
 
319
+ def Component.generate_multi_query(property, plural)
255
320
  # Query for any of these properties
256
- unless instance_methods.include? query
321
+ unless instance_methods.include? plural+'?'
257
322
  code = <<-code
258
- def #{query}
323
+ def #{plural}?
259
324
  @properties.has_key?("#{property}")
260
325
  end
261
326
  code
262
327
 
263
328
  class_eval code, "component.rb", 210
264
329
  end
330
+ end
265
331
 
332
+ def Component.generate_multi_adder(property, singular)
333
+ adder = "add_"+singular.to_s
266
334
  # Add another item to this properties array
267
335
  unless instance_methods.include? adder
268
336
  code = <<-code
@@ -271,6 +339,14 @@ module Icalendar
271
339
  raise(NotImplementedError, "Property value object does not support to_ical method!")
272
340
  end
273
341
 
342
+ unless params.nil?
343
+ # Extend with the parameter methods only if we have to...
344
+ unless val.respond_to?(:ical_params)
345
+ val.class.class_eval { attr_accessor :ical_params }
346
+ end
347
+ val.ical_params = params
348
+ end
349
+
274
350
  if @properties.has_key?("#{property}")
275
351
  @properties["#{property}"] << val
276
352
  else
@@ -279,52 +355,50 @@ module Icalendar
279
355
  end
280
356
  code
281
357
 
282
- class_eval code, "component.rb", 228
358
+ class_eval code, "component.rb", 289
283
359
  alias_method("add_#{property.downcase}", "#{adder}")
284
360
  end
361
+ end
285
362
 
363
+ def Component.generate_multi_remover(property, singular)
286
364
  # Remove an item from this properties array
287
- unless instance_methods.include? remover
365
+ unless instance_methods.include? "remove_#{singular}"
288
366
  code = <<-code
289
- def #{remover}(a)
367
+ def remove_#{singular}(a)
290
368
  if @properties.has_key?("#{property}")
291
369
  @properties["#{property}"].delete(a)
292
370
  end
293
371
  end
294
372
  code
295
373
 
296
- class_eval code, "component.rb", 241
297
- alias_method("remove_#{property.downcase}", "#{remover}")
374
+ class_eval code, "component.rb", 303
375
+ alias_method("remove_#{property.downcase}", "remove_#{singular}")
298
376
  end
299
-
300
377
  end
301
378
 
302
379
  def method_missing(method_name, *args)
380
+ @@logger.debug("Inside method_missing...")
303
381
  method_name = method_name.to_s.downcase
304
382
 
305
383
  unless method_name =~ /x_.*/
306
- raise NoMethodError
307
- end
308
-
309
- val = args.first
310
-
311
- unless val.respond_to?(:to_ical)
312
- raise(NotImplementedError, "Value of type (" + val.class.to_s + ") does not support to_ical method!")
384
+ raise NoMethodError, "Method Name: #{method_name}"
313
385
  end
314
386
 
315
- if method_name =~ /(.*)(=)/ # Its a setter
316
- @properties[$1] = val
317
- @@logger.debug("Setting #{$1} => #{val}")
387
+ # x-properties are accessed with underscore but stored with a dash so
388
+ # they output correctly and we don't have to special case the
389
+ # output code, which would require checking every property.
390
+ if args.size > 0 # Its a setter
391
+ # Pull off the possible equals
392
+ @properties[method_name[/x_[^=]*/].gsub('x_', 'x-')] = args.first
318
393
  else # Or its a getter
319
- @@logger.debug("Getting #{method_name} => #{@properties[method_name]}")
320
- return @properties[method_name]
394
+ return @properties[method_name.gsub('x_', 'x-')]
321
395
  end
322
396
  end
323
397
 
324
398
  public
325
399
 
326
400
  def respond_to?(method_name)
327
- unless method_name.to_s.downcase =~ /x-.*/
401
+ unless method_name.to_s.downcase =~ /x_.*/
328
402
  super
329
403
  end
330
404