icalendar 1.5.4 → 2.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/.rspec +2 -0
- data/.travis.yml +1 -2
- data/History.txt +2 -7
- data/README.md +82 -107
- data/Rakefile +6 -7
- data/icalendar.gemspec +10 -9
- data/lib/icalendar.rb +17 -33
- data/lib/icalendar/alarm.rb +35 -0
- data/lib/icalendar/calendar.rb +17 -100
- data/lib/icalendar/component.rb +41 -403
- data/lib/icalendar/event.rb +51 -0
- data/lib/icalendar/freebusy.rb +27 -0
- data/lib/icalendar/has_components.rb +83 -0
- data/lib/icalendar/has_properties.rb +156 -0
- data/lib/icalendar/journal.rb +39 -0
- data/lib/icalendar/parser.rb +75 -403
- data/lib/icalendar/timezone.rb +53 -0
- data/lib/icalendar/todo.rb +52 -0
- data/lib/icalendar/tzinfo.rb +30 -30
- data/lib/icalendar/value.rb +80 -0
- data/lib/icalendar/values/array.rb +43 -0
- data/lib/icalendar/values/binary.rb +31 -0
- data/lib/icalendar/values/boolean.rb +17 -0
- data/lib/icalendar/values/cal_address.rb +8 -0
- data/lib/icalendar/values/date.rb +26 -0
- data/lib/icalendar/values/date_time.rb +34 -0
- data/lib/icalendar/values/duration.rb +48 -0
- data/lib/icalendar/values/float.rb +17 -0
- data/lib/icalendar/values/integer.rb +17 -0
- data/lib/icalendar/values/period.rb +46 -0
- data/lib/icalendar/values/recur.rb +63 -0
- data/lib/icalendar/values/text.rb +26 -0
- data/lib/icalendar/values/time.rb +34 -0
- data/lib/icalendar/values/time_with_zone.rb +31 -0
- data/lib/icalendar/values/uri.rb +19 -0
- data/lib/icalendar/values/utc_offset.rb +39 -0
- data/lib/icalendar/version.rb +5 -0
- data/spec/alarm_spec.rb +108 -0
- data/spec/calendar_spec.rb +167 -0
- data/spec/event_spec.rb +108 -0
- data/{test/fixtures/folding.ics → spec/fixtures/nondefault_values.ics} +2 -2
- data/{test → spec}/fixtures/single_event.ics +11 -14
- data/spec/fixtures/timezone.ics +35 -0
- data/spec/freebusy_spec.rb +7 -0
- data/spec/journal_spec.rb +7 -0
- data/spec/parser_spec.rb +26 -0
- data/spec/roundtrip_spec.rb +40 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/timezone_spec.rb +31 -0
- data/spec/todo_spec.rb +24 -0
- data/spec/tzinfo_spec.rb +85 -0
- data/spec/values/date_time_spec.rb +80 -0
- data/spec/values/duration_spec.rb +67 -0
- data/spec/values/period_spec.rb +47 -0
- data/spec/values/recur_spec.rb +47 -0
- data/spec/values/text_spec.rb +72 -0
- data/spec/values/utc_offset_spec.rb +41 -0
- metadata +129 -88
- data/GPL +0 -340
- data/examples/create_cal.rb +0 -45
- data/examples/parse_cal.rb +0 -20
- data/examples/single_event.ics +0 -18
- data/lib/hash_attrs.rb +0 -34
- data/lib/icalendar/base.rb +0 -47
- data/lib/icalendar/component/alarm.rb +0 -47
- data/lib/icalendar/component/event.rb +0 -131
- data/lib/icalendar/component/freebusy.rb +0 -38
- data/lib/icalendar/component/journal.rb +0 -60
- data/lib/icalendar/component/timezone.rb +0 -91
- data/lib/icalendar/component/todo.rb +0 -64
- data/lib/icalendar/conversions.rb +0 -107
- data/lib/icalendar/helpers.rb +0 -109
- data/lib/icalendar/parameter.rb +0 -33
- data/lib/icalendar/rrule.rb +0 -133
- data/lib/meta.rb +0 -32
- data/script/console +0 -10
- data/script/recur1.ics +0 -38
- data/script/tryit.rb +0 -13
- data/test/component/test_event.rb +0 -253
- data/test/component/test_timezone.rb +0 -74
- data/test/component/test_todo.rb +0 -31
- data/test/fixtures/life.ics +0 -46
- data/test/fixtures/nonstandard.ics +0 -25
- data/test/fixtures/simplecal.ics +0 -119
- data/test/interactive.rb +0 -17
- data/test/read_write.rb +0 -23
- data/test/test_calendar.rb +0 -167
- data/test/test_component.rb +0 -102
- data/test/test_conversions.rb +0 -104
- data/test/test_helper.rb +0 -7
- data/test/test_parameter.rb +0 -91
- data/test/test_parser.rb +0 -100
- data/test/test_tzinfo.rb +0 -83
- data/website/index.html +0 -70
- data/website/index.txt +0 -38
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -159
- data/website/template.html.erb +0 -50
@@ -0,0 +1,35 @@
|
|
1
|
+
module Icalendar
|
2
|
+
|
3
|
+
class Alarm < Component
|
4
|
+
|
5
|
+
required_property :action
|
6
|
+
required_property :trigger, Icalendar::Values::Duration
|
7
|
+
required_property :description, Icalendar::Values::Text,
|
8
|
+
->(alarm, description) { alarm.action.downcase == 'audio' || !description.nil? }
|
9
|
+
required_property :summary, Icalendar::Values::Text,
|
10
|
+
->(alarm, summary) { alarm.action.downcase != 'email' || !summary.nil? }
|
11
|
+
required_multi_property :attendee, Icalendar::Values::CalAddress,
|
12
|
+
->(alarm, attendees) { alarm.action.downcase != 'email' || !attendees.compact.empty? }
|
13
|
+
|
14
|
+
optional_single_property :duration, Icalendar::Values::Duration
|
15
|
+
optional_single_property :repeat, Icalendar::Values::Integer
|
16
|
+
|
17
|
+
optional_property :attach, Icalendar::Values::Uri
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super 'alarm'
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?(strict = false)
|
24
|
+
if strict
|
25
|
+
# must be part of event or todo
|
26
|
+
!(parent.nil? || parent.name == 'event' || parent.name == 'todo') and return false
|
27
|
+
end
|
28
|
+
# either both duration and repeat or neither should be set
|
29
|
+
[duration, repeat].compact.size == 1 and return false
|
30
|
+
# attach must be single for audio actions
|
31
|
+
action.downcase == 'audio' && attach.compact.size > 1 and return false
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/icalendar/calendar.rb
CHANGED
@@ -1,110 +1,27 @@
|
|
1
|
-
=begin
|
2
|
-
Copyright (C) 2005 Jeff Rose
|
3
|
-
|
4
|
-
This library is free software; you can redistribute it and/or modify it
|
5
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
-
details.
|
7
|
-
=end
|
8
|
-
|
9
1
|
module Icalendar
|
10
2
|
|
11
3
|
class Calendar < Component
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
ical_property :version
|
17
|
-
ical_property :ip_method
|
18
|
-
|
19
|
-
def initialize()
|
20
|
-
super("VCALENDAR")
|
4
|
+
required_property :version
|
5
|
+
required_property :prodid
|
6
|
+
optional_single_property :calscale
|
7
|
+
optional_single_property :ip_method
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def print_headers
|
29
|
-
"VERSION:#{version}\r\n"
|
30
|
-
end
|
9
|
+
component :timezone, :tzid
|
10
|
+
component :event
|
11
|
+
component :todo
|
12
|
+
component :journal
|
13
|
+
component :freebusy
|
31
14
|
|
32
|
-
def
|
33
|
-
|
15
|
+
def initialize
|
16
|
+
super 'calendar'
|
17
|
+
self.prodid = 'icalendar-ruby'
|
18
|
+
self.version = '2.0'
|
19
|
+
self.calscale = 'GREGORIAN'
|
34
20
|
end
|
35
21
|
|
36
|
-
def event(&block)
|
37
|
-
calendar_tzid = timezone_id
|
38
|
-
build_component Event.new do
|
39
|
-
# Note: I'm not sure this is the best way to pass this down, but it works
|
40
|
-
self.tzid = calendar_tzid
|
41
|
-
|
42
|
-
if block
|
43
|
-
instance_eval(&block)
|
44
|
-
if tzid
|
45
|
-
dtstart.ical_params = { "TZID" => tzid }
|
46
|
-
dtend.ical_params = { "TZID" => tzid } unless dtend.nil?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def find_event(uid)
|
53
|
-
events.find {|e| e.uid == uid}
|
54
|
-
end
|
55
|
-
|
56
|
-
def todo(&block)
|
57
|
-
build_component Todo.new, &block
|
58
|
-
end
|
59
|
-
|
60
|
-
def find_todo(uid)
|
61
|
-
todos.find {|t| t.uid == uid}
|
62
|
-
end
|
63
|
-
|
64
|
-
def journal(&block)
|
65
|
-
build_component Journal.new, &block
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_journal(uid)
|
69
|
-
journals.find {|j| j.uid == uid}
|
70
|
-
end
|
71
|
-
|
72
|
-
def freebusy(&block)
|
73
|
-
build_component Freebusy.new, &block
|
74
|
-
end
|
75
|
-
|
76
|
-
def find_freebusy(uid)
|
77
|
-
freebusys.find {|f| f.uid == uid}
|
78
|
-
end
|
79
|
-
|
80
|
-
def timezone(&block)
|
81
|
-
build_component Timezone.new, &block
|
82
|
-
end
|
83
|
-
|
84
|
-
# The "PUBLISH" method in a "VEVENT" calendar component is an
|
85
|
-
# unsolicited posting of an iCalendar object. Any CU may add published
|
86
|
-
# components to their calendar. The "Organizer" MUST be present in a
|
87
|
-
# published iCalendar component. "Attendees" MUST NOT be present. Its
|
88
|
-
# expected usage is for encapsulating an arbitrary event as an
|
89
|
-
# iCalendar object. The "Organizer" may subsequently update (with
|
90
|
-
# another "PUBLISH" method), add instances to (with an "ADD" method),
|
91
|
-
# or cancel (with a "CANCEL" method) a previously published "VEVENT"
|
92
|
-
# calendar component.
|
93
22
|
def publish
|
94
|
-
self.ip_method =
|
23
|
+
self.ip_method = 'PUBLISH'
|
95
24
|
end
|
25
|
+
end
|
96
26
|
|
97
|
-
|
98
|
-
|
99
|
-
def build_component(component, &block)
|
100
|
-
add_component component
|
101
|
-
component.instance_eval(&block) if block
|
102
|
-
component
|
103
|
-
end
|
104
|
-
|
105
|
-
def timezone_id
|
106
|
-
timezones[0].tzid if timezones.length > 0
|
107
|
-
end
|
108
|
-
end # class Calendar
|
109
|
-
|
110
|
-
end # module Icalendar
|
27
|
+
end
|
data/lib/icalendar/component.rb
CHANGED
@@ -1,432 +1,70 @@
|
|
1
|
-
|
2
|
-
Copyright (C) 2005 Jeff Rose
|
3
|
-
|
4
|
-
This library is free software; you can redistribute it and/or modify it
|
5
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
-
details.
|
7
|
-
=end
|
1
|
+
require 'socket'
|
8
2
|
|
9
3
|
module Icalendar
|
10
|
-
require 'socket'
|
11
|
-
|
12
|
-
MAX_LINE_LENGTH = 75
|
13
|
-
|
14
|
-
class Geo < Icalendar::Base
|
15
|
-
attr_accessor :latitude, :longitude
|
16
|
-
alias :lat :latitude
|
17
|
-
alias :lat= :latitude=
|
18
|
-
alias :long :longitude
|
19
|
-
alias :long= :longitude=
|
20
|
-
|
21
|
-
def initialize(lat, long)
|
22
|
-
@latitude = lat
|
23
|
-
@longitude = long
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_ical
|
27
|
-
"#{@latitude.to_ical};#{@longitude.to_ical}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# The body of the iCalendar object consists of a sequence of calendar
|
32
|
-
# properties and one or more calendar components. The calendar
|
33
|
-
# properties are attributes that apply to the calendar as a whole. The
|
34
|
-
# calendar components are collections of properties that express a
|
35
|
-
# particular calendar semantic. For example, the calendar component can
|
36
|
-
# specify an Event, a Todo, a Journal entry, Timezone information, or
|
37
|
-
# Freebusy time information, or an Alarm.
|
38
|
-
class Component < Icalendar::Base
|
39
|
-
|
40
|
-
CAL_EXTENSION_REGEX = /\Ax_[a-z_]+=?\Z/
|
41
4
|
|
42
|
-
|
5
|
+
class Component
|
6
|
+
include HasProperties
|
7
|
+
include HasComponents
|
43
8
|
|
44
9
|
attr_reader :name
|
45
|
-
|
10
|
+
attr_reader :ical_name
|
11
|
+
attr_accessor :parent
|
46
12
|
|
47
|
-
|
48
|
-
@@multiline_properties = {}
|
49
|
-
|
50
|
-
def initialize(name)
|
13
|
+
def initialize(name, ical_name = nil)
|
51
14
|
@name = name
|
52
|
-
@
|
53
|
-
|
54
|
-
|
55
|
-
@@logger.info("New #{@name[1,@name.size].capitalize}...")
|
56
|
-
end
|
57
|
-
|
58
|
-
# Add a sub-component to the current component object.
|
59
|
-
def add_component(component)
|
60
|
-
@components[component.key_name] << component
|
61
|
-
end
|
62
|
-
|
63
|
-
# Add a component to the calendar.
|
64
|
-
alias add add_component
|
65
|
-
|
66
|
-
# Add an event to the calendar.
|
67
|
-
alias add_event add_component
|
68
|
-
|
69
|
-
# Add a todo item to the calendar.
|
70
|
-
alias add_todo add_component
|
71
|
-
|
72
|
-
# Add a journal item to the calendar.
|
73
|
-
alias add_journal add_component
|
74
|
-
|
75
|
-
def remove_component(component)
|
76
|
-
@components[component.key_name].delete(component)
|
15
|
+
@ical_name = ical_name || "V#{name.upcase}"
|
16
|
+
super()
|
77
17
|
end
|
78
18
|
|
79
|
-
# Remove a component from the calendar.
|
80
|
-
alias remove remove_component
|
81
|
-
|
82
|
-
# Remove an event from the calendar.
|
83
|
-
alias remove_event remove_component
|
84
|
-
|
85
|
-
# Remove a todo item from the calendar.
|
86
|
-
alias remove_todo remove_component
|
87
|
-
|
88
|
-
# Remove a journal item from the calendar.
|
89
|
-
alias remove_journal remove_component
|
90
|
-
|
91
|
-
# Used to generate unique component ids
|
92
19
|
def new_uid
|
93
20
|
"#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}"
|
94
21
|
end
|
95
22
|
|
96
|
-
# Output in the icalendar format
|
97
23
|
def to_ical
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
# Print this icalendar component
|
106
|
-
def print_component
|
107
|
-
to_ical
|
24
|
+
[
|
25
|
+
"BEGIN:#{ical_name}",
|
26
|
+
ical_properties,
|
27
|
+
ical_components,
|
28
|
+
"END:#{ical_name}\r\n"
|
29
|
+
].compact.join "\r\n"
|
108
30
|
end
|
109
31
|
|
110
|
-
def print_subcomponents
|
111
|
-
@components.values.map do |component_parts|
|
112
|
-
Array(component_parts).map &:to_ical
|
113
|
-
end.join
|
114
|
-
end
|
115
|
-
|
116
|
-
def printer
|
117
|
-
["BEGIN:#{@name.upcase}\r\n",
|
118
|
-
yield,
|
119
|
-
"END:#{@name.upcase}\r\n"].join
|
120
|
-
end
|
121
|
-
|
122
|
-
def print_properties(properties = properties_to_print)
|
123
|
-
excludes = %w(geo rrule categories exdate)
|
124
|
-
properties.sort.map do |key, val|
|
125
|
-
property = fix_conflict_with_built_in(key)
|
126
|
-
prelude = property.gsub(/_/, '-').upcase
|
127
|
-
|
128
|
-
if multiline_property? property
|
129
|
-
val.map do |part|
|
130
|
-
params = print_parameters part
|
131
|
-
value = escape_chars ":#{part.to_ical}"
|
132
|
-
chunk_lines "#{prelude}#{params}#{value}"
|
133
|
-
end.join
|
134
|
-
else
|
135
|
-
params = print_parameters val
|
136
|
-
value = ":#{val.to_ical}"
|
137
|
-
value = escape_chars(value) unless excludes.include? property
|
138
|
-
chunk_lines "#{prelude}#{params}#{value}"
|
139
|
-
end
|
140
|
-
end.join
|
141
|
-
end
|
142
|
-
|
143
|
-
# Take out underscore for property names that conflicted
|
144
|
-
# with built-in words.
|
145
|
-
def fix_conflict_with_built_in(key)
|
146
|
-
key.sub(/\Aip_/, '')
|
147
|
-
end
|
148
|
-
|
149
|
-
def escape_chars(value)
|
150
|
-
value.gsub("\\", "\\\\").gsub("\r\n", "\n").gsub("\r", "\n").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
|
151
|
-
end
|
152
|
-
|
153
|
-
def chunk_lines(str, length = MAX_LINE_LENGTH, separator = "\r\n ")
|
154
|
-
chunks = str.scan(/.{1,#{length}}/)
|
155
|
-
lines = chunks.join(separator) << separator
|
156
|
-
lines.gsub(/ *$/, '')
|
157
|
-
end
|
158
|
-
|
159
|
-
# Print the parameters for a specific property.
|
160
|
-
def print_parameters(value)
|
161
|
-
return "" unless value.respond_to?(:ical_params)
|
162
|
-
|
163
|
-
Array(value.ical_params).map do |key, val|
|
164
|
-
val = Array(val)
|
165
|
-
next if val.empty?
|
166
|
-
|
167
|
-
escaped = val.map { |v| Parser.escape(v.to_ical) }.join(',')
|
168
|
-
";#{key}=" << escaped
|
169
|
-
end.join
|
170
|
-
end
|
171
|
-
|
172
|
-
def properties_to_print
|
173
|
-
@properties # subclasses can exclude properties
|
174
|
-
end
|
175
|
-
|
176
|
-
def print_headers
|
177
|
-
"" # subclasses can specify headers
|
178
|
-
end
|
179
|
-
|
180
|
-
# TODO: Look into the x-property, x-param stuff...
|
181
|
-
# This would really only be needed for subclassing to add additional
|
182
|
-
# properties to an application using the API.
|
183
|
-
def custom_property(name, value)
|
184
|
-
@properties[name] = value
|
185
|
-
end
|
186
|
-
|
187
|
-
def multi_property?(name)
|
188
|
-
@@multi_properties.has_key?(name.downcase)
|
189
|
-
end
|
190
|
-
|
191
|
-
def multiline_property?(name)
|
192
|
-
@@multiline_properties.has_key?(name.downcase)
|
193
|
-
end
|
194
|
-
|
195
|
-
# Make it protected so we can monitor usage...
|
196
|
-
protected
|
197
|
-
|
198
|
-
def key_name
|
199
|
-
(self.class.to_s.downcase + 's').gsub('icalendar::', '').to_sym
|
200
|
-
end
|
201
|
-
|
202
|
-
def self.ical_component(*syms)
|
203
|
-
hash_accessor :@components, *syms
|
204
|
-
end
|
205
|
-
|
206
|
-
# Define a set of methods supporting a new property
|
207
|
-
def self.ical_property(property, alias_name = nil, prop_name = nil)
|
208
|
-
property = "#{property}".strip.downcase
|
209
|
-
alias_name = "#{alias_name}".strip.downcase unless alias_name.nil?
|
210
|
-
# If a prop_name was given then we use that for the actual storage
|
211
|
-
property = "#{prop_name}".strip.downcase unless prop_name.nil?
|
212
|
-
|
213
|
-
generate_getter(property, alias_name)
|
214
|
-
generate_setter(property, alias_name)
|
215
|
-
generate_query(property, alias_name)
|
216
|
-
end
|
217
|
-
|
218
|
-
# Define a set of methods defining a new property, which
|
219
|
-
# supports multiple values for the same property name.
|
220
|
-
def self.ical_multi_property(property, singular, plural)
|
221
|
-
property = "#{property}".strip.downcase.gsub(/-/, '_')
|
222
|
-
plural = "#{plural}".strip.downcase
|
223
|
-
|
224
|
-
# Set this key so the parser knows to use an array for
|
225
|
-
# storing this property type.
|
226
|
-
@@multi_properties["#{property}"] = true
|
227
|
-
|
228
|
-
generate_multi_getter(property, plural)
|
229
|
-
generate_multi_setter(property, plural)
|
230
|
-
generate_multi_query(property, plural)
|
231
|
-
generate_multi_adder(property, singular)
|
232
|
-
generate_multi_remover(property, singular)
|
233
|
-
end
|
234
|
-
|
235
|
-
# Define a set of methods defining a new property, which
|
236
|
-
# supports multiple values in multiple lines with same property name
|
237
|
-
def self.ical_multiline_property(property, singular, plural)
|
238
|
-
@@multiline_properties["#{property}"] = true
|
239
|
-
ical_multi_property(property, singular, plural)
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
32
|
private
|
244
33
|
|
245
|
-
def
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
unless val.respond_to?(:ical_params)
|
259
|
-
val.class.class_eval { attr_accessor :ical_params }
|
260
|
-
end
|
261
|
-
val.ical_params = params
|
262
|
-
end
|
263
|
-
|
264
|
-
@properties["#{property}"] = val
|
265
|
-
end
|
266
|
-
code
|
267
|
-
|
268
|
-
class_eval code, "component.rb", 219
|
269
|
-
alias_method("#{alias_name}", "#{property}") unless alias_name.nil?
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
def self.generate_setter(property, alias_name)
|
274
|
-
setter = property + '='
|
275
|
-
unless instance_methods.include? setter
|
276
|
-
code = <<-code
|
277
|
-
def #{setter}(val)
|
278
|
-
#{property}(val)
|
279
|
-
end
|
280
|
-
code
|
281
|
-
|
282
|
-
class_eval code, "component.rb", 233
|
283
|
-
alias_method("#{alias_name}=", "#{property+'='}") unless alias_name.nil?
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
def self.generate_query(property, alias_name)
|
288
|
-
query = "#{property}?"
|
289
|
-
unless instance_methods.include? query
|
290
|
-
code = <<-code
|
291
|
-
def #{query}
|
292
|
-
@properties.has_key?("#{property.downcase}")
|
293
|
-
end
|
294
|
-
code
|
295
|
-
|
296
|
-
class_eval code, "component.rb", 226
|
297
|
-
|
298
|
-
alias_method("#{alias_name}\?", "#{query}") unless alias_name.nil?
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def self.generate_multi_getter(property, plural)
|
303
|
-
# Getter for whole array
|
304
|
-
unless instance_methods.include? plural
|
305
|
-
code = <<-code
|
306
|
-
def #{plural}(a = nil, params = nil)
|
307
|
-
if a.nil?
|
308
|
-
@properties["#{property}"] || []
|
309
|
-
else
|
310
|
-
self.#{plural}=(a).tap do |val|
|
311
|
-
unless params.nil?
|
312
|
-
unless val.respond_to?(:ical_params)
|
313
|
-
val.class.class_eval { attr_accessor :ical_params }
|
314
|
-
end
|
315
|
-
val.ical_params = params
|
316
|
-
end
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
code
|
321
|
-
|
322
|
-
class_eval code, "component.rb", 186
|
323
|
-
alias_method property, plural
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def self.generate_multi_setter(property, plural)
|
328
|
-
# Setter for whole array
|
329
|
-
unless instance_methods.include? plural+'+'
|
330
|
-
code = <<-code
|
331
|
-
def #{plural}=(a)
|
332
|
-
if a.respond_to?(:to_ary)
|
333
|
-
@properties["#{property}"] = a.to_ary
|
334
|
-
elsif a =~ /^[^"].*(?<!\\\\),.*[^"]$/
|
335
|
-
@properties["#{property}"] = a.split(/(?<!\\\\),/).to_ary
|
336
|
-
else
|
337
|
-
(@properties["#{property}"] ||= []) << a
|
338
|
-
end
|
339
|
-
end
|
340
|
-
code
|
341
|
-
|
342
|
-
class_eval code, "component.rb", 198
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
def self.generate_multi_query(property, plural)
|
347
|
-
# Query for any of these properties
|
348
|
-
unless instance_methods.include? plural+'?'
|
349
|
-
code = <<-code
|
350
|
-
def #{plural}?
|
351
|
-
@properties.has_key?("#{property}")
|
352
|
-
end
|
353
|
-
code
|
354
|
-
|
355
|
-
class_eval code, "component.rb", 210
|
356
|
-
end
|
34
|
+
def ical_properties
|
35
|
+
(self.class.properties + custom_properties.keys).map do |prop|
|
36
|
+
value = send prop
|
37
|
+
unless value.nil?
|
38
|
+
if value.is_a? ::Array
|
39
|
+
value.map do |part|
|
40
|
+
ical_fold "#{ical_prop_name prop}#{part.to_ical self.class.default_property_types[prop]}"
|
41
|
+
end.join "\r\n" unless value.empty?
|
42
|
+
else
|
43
|
+
ical_fold "#{ical_prop_name prop}#{value.to_ical self.class.default_property_types[prop]}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end.compact.join "\r\n"
|
357
47
|
end
|
358
48
|
|
359
|
-
def
|
360
|
-
|
361
|
-
# Add another item to this properties array
|
362
|
-
unless instance_methods.include? adder
|
363
|
-
code = <<-code
|
364
|
-
def #{adder}(val, params = {})
|
365
|
-
unless val.respond_to?(:to_ical)
|
366
|
-
raise(NotImplementedError, "Property value object does not support to_ical method!")
|
367
|
-
end
|
368
|
-
|
369
|
-
unless params.nil?
|
370
|
-
# Extend with the parameter methods only if we have to...
|
371
|
-
unless val.respond_to?(:ical_params)
|
372
|
-
val.class.class_eval { attr_accessor :ical_params }
|
373
|
-
end
|
374
|
-
val.ical_params = params
|
375
|
-
end
|
376
|
-
|
377
|
-
if @properties.has_key?("#{property}")
|
378
|
-
@properties["#{property}"] << val
|
379
|
-
else
|
380
|
-
@properties["#{property}"] = [val]
|
381
|
-
end
|
382
|
-
end
|
383
|
-
code
|
384
|
-
|
385
|
-
class_eval code, "component.rb", 289
|
386
|
-
alias_method("add_#{property.downcase}", "#{adder}")
|
387
|
-
end
|
49
|
+
def ical_prop_name(prop_name)
|
50
|
+
prop_name.gsub(/\Aip_/, '').gsub('_', '-').upcase
|
388
51
|
end
|
389
52
|
|
390
|
-
def
|
391
|
-
|
392
|
-
|
393
|
-
code = <<-code
|
394
|
-
def remove_#{singular}(a)
|
395
|
-
if @properties.has_key?("#{property}")
|
396
|
-
@properties["#{property}"].delete(a)
|
397
|
-
end
|
398
|
-
end
|
399
|
-
code
|
400
|
-
|
401
|
-
class_eval code, "component.rb", 303
|
402
|
-
alias_method("remove_#{property.downcase}", "remove_#{singular}")
|
403
|
-
end
|
53
|
+
def ical_fold(content_line)
|
54
|
+
split = content_line.split ''
|
55
|
+
[].tap { |a| a << split.shift(Icalendar::MAX_LINE_LENGTH).join until split.empty? }.join "\r\n "
|
404
56
|
end
|
405
57
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
# cal.x_wr_calname = "iCalendar Calendar"
|
413
|
-
if method_name =~ CAL_EXTENSION_REGEX
|
414
|
-
|
415
|
-
# Make sure to remove '=' from the end of the method_name so we can
|
416
|
-
# define it
|
417
|
-
name = method_name.to_s.chomp '='
|
418
|
-
|
419
|
-
self.class.class_eval do
|
420
|
-
ical_multiline_property name, name, name
|
58
|
+
def ical_components
|
59
|
+
collection = []
|
60
|
+
(self.class.components + custom_components.keys).each do |component_name|
|
61
|
+
components = send component_name
|
62
|
+
components.each do |component|
|
63
|
+
collection << component.to_ical
|
421
64
|
end
|
422
|
-
send method_name, *args
|
423
|
-
else
|
424
|
-
super
|
425
65
|
end
|
66
|
+
collection.empty? ? nil : collection.join.chomp("\r\n")
|
426
67
|
end
|
68
|
+
end
|
427
69
|
|
428
|
-
def respond_to_missing?(method_name, include_private = false)
|
429
|
-
method_name.to_s =~ CAL_EXTENSION_REGEX || super
|
430
|
-
end
|
431
|
-
end # class Component
|
432
70
|
end
|