icalendar 1.5.4 → 2.0.0.beta.1
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.
- 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
|