kali 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,207 @@
1
+ module Kali
2
+ # Identifier for the product that created this iCalendar object.
3
+ #
4
+ # As per http://tools.ietf.org/html/rfc5545#section-3.7.3
5
+ class Property::ProductIdentifier < Property
6
+ name "PRODID"
7
+ type Type::Text.new
8
+ default "Kali/#{Kali::VERSION}"
9
+ end
10
+
11
+ # Version of the iCalendar protocol implemented in this iCalendar object.
12
+ #
13
+ # As per http://tools.ietf.org/html/rfc5545#section-3.7.4
14
+ class Property::Version < Property
15
+ name "VERSION"
16
+ type Type::Text.new("2.0")
17
+ default "2.0"
18
+ end
19
+
20
+ # Calendar scale used in this iCalendar object.
21
+ #
22
+ # As per http://tools.ietf.org/html/rfc5545#section-3.7.1
23
+ class Property::CalendarScale < Property
24
+ name "CALSCALE"
25
+ type Type::Text.new("GREGORIAN")
26
+ default "GREGORIAN"
27
+ end
28
+
29
+ # The iCalendar object method associated with this iCalendar object.
30
+ #
31
+ # As per http://tools.ietf.org/html/rfc5545#section-3.7.2
32
+ class Property::Method < Property
33
+ name "METHOD"
34
+ type Type::Text.new(
35
+ Enum["PUBLISH", "REQUEST", "REPLY", "ADD", "CANCEL", "REFRESH", "COUNTER",
36
+ "DECLINECOUNTER"]
37
+ )
38
+ end
39
+
40
+ # Globally unique identifier for this component.
41
+ #
42
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.4.7
43
+ class Property::UniqueIdentifier < Property
44
+ name "UID"
45
+ type Type::Text.new
46
+ end
47
+
48
+ # The "current version" of the component, according to the organizer's
49
+ # judgement.
50
+ #
51
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.7.4
52
+ class Property::SequenceNumber < Property
53
+ name "SEQUENCE"
54
+ type Type::Integer.new(->(i) { i >= 0 })
55
+ default 0
56
+ end
57
+
58
+ # Short summary of the current component.
59
+ #
60
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.12
61
+ class Property::Summary < Property
62
+ name "SUMMARY"
63
+ type Type::Text.new
64
+ end
65
+
66
+ # Longer description of the current component.
67
+ #
68
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.5
69
+ class Property::Description < Property
70
+ name "DESCRIPTION"
71
+ type Type::Text.new
72
+ end
73
+
74
+ # Lat/Lng pair indicating where this component takes place.
75
+ #
76
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.6
77
+ class Property::GeographicPosition < Property
78
+ name "GEO"
79
+ type Type::Geo.new
80
+ end
81
+
82
+ # Name/description of the venue where this component takes place.
83
+ #
84
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.7
85
+ class Property::Location < Property
86
+ name "LOCATION"
87
+ type Type::Text.new
88
+ end
89
+
90
+ # How important is this component compared to others. Can be 0 (no special
91
+ # priority), or range from 1 (high) to 9 (low).
92
+ #
93
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.9
94
+ class Property::Priority < Property
95
+ name "PRIORITY"
96
+ type Type::Integer.new(0..9)
97
+ default 0
98
+ end
99
+
100
+ # List of categories describing this component.
101
+ #
102
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.2
103
+ class Property::Categories < Property
104
+ name "CATEGORIES"
105
+ type Type::List.new(Type::Text.new)
106
+ end
107
+
108
+ # List of resources needed to be able to fulfill this component. For example
109
+ # ["PROJECTOR", "EASEL", "WHITEBOARD"].
110
+ #
111
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.10
112
+ class Property::Resources < Property
113
+ name "RESOURCES"
114
+ type Type::List.new(Type::Text.new)
115
+ end
116
+
117
+ # User provided comments about the current component. Can be specified
118
+ # multiple times for a single component.
119
+ #
120
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.4
121
+ class Property::Comment < Property
122
+ name "COMMENT"
123
+ type Type::Text.new
124
+ end
125
+
126
+ # Status representing whether the event is taking place or not.
127
+ #
128
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.1.11
129
+ class Property::EventStatus < Property
130
+ name "STATUS"
131
+ type Type::Text.new(Enum["TENTATIVE", "CONFIRMED", "CANCELLED"])
132
+ end
133
+
134
+ # End date of a component. This is required, unless there's a Duration, in
135
+ # which case this can't appear.
136
+ #
137
+ # TODO: This can be a DateTime or a Date. We should take that into
138
+ # consideration.
139
+ #
140
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.2.2
141
+ class Property::EndDateTime < Property
142
+ name "DTEND"
143
+ type Type::DateTime.new
144
+ end
145
+
146
+ # Start date of a component.
147
+ #
148
+ # TODO: This can be a DateTime or a Date. We should take that into
149
+ # consideration.
150
+ #
151
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.2.4
152
+ class Property::StartDateTime < Property
153
+ name "DTSTART"
154
+ type Type::DateTime.new
155
+ end
156
+
157
+ # Duration of the component.
158
+ #
159
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.2.5
160
+ class Property::Duration < Property
161
+ name "DURATION"
162
+ type Type::Duration.new
163
+ end
164
+
165
+ # Does this event affect your free/busy schedule? Transparent events are the
166
+ # ones that don't, while opaque events do.
167
+ #
168
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.2.7
169
+ class Property::TimeTransparency < Property
170
+ name "TRANSP"
171
+ type Type::Text.new(Enum["OPAQUE", "TRANSPARENT"])
172
+ default "OPAQUE"
173
+ end
174
+
175
+ # Represent a person involved with this event.
176
+ #
177
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.4.1
178
+ class Property::Attendee < Property
179
+ name "ATTENDEE"
180
+ type Type::CalAddress.new
181
+ end
182
+
183
+ # Represent the party organizing the event.
184
+ #
185
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.4.3
186
+ class Property::Organizer < Property
187
+ name "ORGANIZER"
188
+ type Type::CalAddress.new
189
+ end
190
+
191
+ # This component is used to represent contact information for someone
192
+ # associated with the Event's venue.
193
+ #
194
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.4.3
195
+ class Property::Contact < Property
196
+ name "CONTACT"
197
+ type Type::Text.new
198
+ end
199
+
200
+ # External URL associated with this calendar component.
201
+ #
202
+ # As per http://tools.ietf.org/html/rfc5545#section-3.8.4.6
203
+ class Property::URL < Property
204
+ name "URL"
205
+ type Type::URI.new
206
+ end
207
+ end
@@ -0,0 +1,93 @@
1
+ module Kali
2
+ # Public: Properties are the definition of an attribute describing a
3
+ # component.
4
+ class Property < KeyValuePair
5
+ # Public: Add a new parameter to this Property.
6
+ #
7
+ # type - A subclass of Kali::Parameter.
8
+ #
9
+ # Returns nothing.
10
+ def self.parameter(type, method = type.method_name)
11
+ default_parameter_types[type.name] = type
12
+
13
+ define_method method do
14
+ parameters[type.name] ||= type.new
15
+ end
16
+
17
+ define_method "#{method}=" do |value|
18
+ parameters[type.name] = type.wrap(value)
19
+ end
20
+ end
21
+
22
+ # Public: Register the type of this property, and all the parameters
23
+ # associated with that type, or get the current type of the object.
24
+ #
25
+ # See `KeyValuePair.type`.
26
+ def self.type(type = nil)
27
+ if type
28
+ type.parameters.each do |param, method|
29
+ parameter(param, method)
30
+ end
31
+ end
32
+ super
33
+ end
34
+
35
+ # Internal: List of parameter types added to the class explicitly. Any
36
+ # parameter added that is not in this store will be assumed to be a simple
37
+ # Type::Text parameter. See `Property#[]=`.
38
+ def self.default_parameter_types
39
+ @parameter_types ||= {}
40
+ end
41
+
42
+ def initialize(*) # :nodoc:
43
+ super
44
+ end
45
+
46
+ # Internal: Storage of parameters associated with this instance of the
47
+ # property.
48
+ #
49
+ # Returns a Hash.
50
+ def parameters
51
+ @parameters ||= {}
52
+ end
53
+
54
+ # Public: Get a parameter by iCalendar name.
55
+ #
56
+ # name - The name of the parameter.
57
+ #
58
+ # Returns the passed in value.
59
+ def [](name)
60
+ parameters[name]
61
+ end
62
+
63
+ # Public: Set a parameter manually by explicitly passing the iCalendar name
64
+ # (so, for example, "DELEGATED-TO" instead of calling `#delegated_to=`).
65
+ #
66
+ # name - A String with the name of the parameter. If there's a typed
67
+ # parameter for this name already, this will be equivalent to
68
+ # setting that parameter via the setter. Otherwise it will assume
69
+ # this is a Text parameter and set it. Useful for setting
70
+ # experimental ("X-*") params, such as "X-GUEST-COUNT".
71
+ # value - The value of the param.
72
+ #
73
+ # Returns the passed in value.
74
+ def []=(name, value)
75
+ param = if type = self.class.default_parameter_types.fetch(name, false)
76
+ type.wrap(value)
77
+ else
78
+ Parameter::Default.new(name, value)
79
+ end
80
+
81
+ parameters[name] = param
82
+ end
83
+
84
+ # Public: Generate an iCalendar representation of this property.
85
+ #
86
+ # Returns a String.
87
+ def to_ics
88
+ params = parameters.map { |_, value| value.to_ics }.join("")
89
+ encoded_value = self.class.type.encode(value)
90
+ Utils::Text.fold_line "#{self.class.name}#{params}:#{encoded_value}"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,118 @@
1
+ require "set"
2
+
3
+ module Kali
4
+ # Public: Type classes are used to convert ruby objects from and to their
5
+ # iCalendar representations.
6
+ #
7
+ # Examples:
8
+ # type = Kali::Type::Date.new
9
+ # type.encode(Date.today) #=> "20130712"
10
+ # type.decode("20130712") #=> Date.new(2013, 7, 12)
11
+ #
12
+ # type = Kali::Type::Integer.new(0..9)
13
+ # type.encode(3) #=> "3"
14
+ # type.encode(15) #=> ArgumentError
15
+ # type.decode("7") #=> 7
16
+ #
17
+ # type = Kali::Type::Text.new(->(s) { s.size > 3 })
18
+ # type.encode("x") #=> ArgumentError
19
+ # type.encode("long") #=> "long"
20
+ #
21
+ # type = Kali::Type::Text.new(Enum["ACTIVE", "INACTIVE"])
22
+ # type.encode("ACTIVE") #=> "ACTIVE"
23
+ # type.encode("nope") #=> ArgumentError
24
+ class Type
25
+ # Public: Initialize the Type.
26
+ #
27
+ # restriction - An object that implenents the #=== operator. When we
28
+ # validate that a certain value is valid for this type, we
29
+ # will check the value against `restriction.===`. If it's
30
+ # `nil` then we don't restrict the value.
31
+ def initialize(restriction = nil)
32
+ @restriction = restriction
33
+ end
34
+
35
+ # Public: Parameters added by deafult to properties that are of this type.
36
+ #
37
+ # Returns an Array where each entry is a Hash keyed by a subclass of
38
+ # Kali::Parameter, and the value is a Symbol with the name of the method for
39
+ # that property.
40
+ def parameters
41
+ []
42
+ end
43
+
44
+ # Public: Encode an object into an iCalendar-compatible String using the
45
+ # format specific to this Type.
46
+ #
47
+ # Requires a subclass to implement #encode!
48
+ #
49
+ # obj - An Object that matches this Type.
50
+ #
51
+ # Returns a String that matches the iCalendar representation of this Type.
52
+ def encode(obj)
53
+ encode!(validate(obj))
54
+ end
55
+
56
+ # Public: Decode an iCalendar-compatible String into an Object that matches
57
+ # this Type.
58
+ #
59
+ # Requires a subclass to implement #decode!
60
+ #
61
+ # obj - A String that matches the iCalendar representation for this Type..
62
+ #
63
+ # Returns an Object that matches this Type.
64
+ def decode(obj)
65
+ validate(decode!(obj))
66
+ end
67
+
68
+ private
69
+
70
+ # Internal: Check that an Object meets the restrictions of this particular
71
+ # Type.
72
+ #
73
+ # value - An Object.
74
+ #
75
+ # Returns the value if it satisfies the restriction.
76
+ # Raises ArgumentError if the restriction is not satisfied.
77
+ def validate(value)
78
+ if @restriction.nil? || @restriction === value
79
+ value
80
+ else
81
+ raise ArgumentError, "#{value.inspect} does not satisfy the type restriction for #{self.class} (#{@restriction.inspect})"
82
+ end
83
+ end
84
+
85
+ autoload :Text, "kali/type/text"
86
+ autoload :Integer, "kali/type/integer"
87
+ autoload :Float, "kali/type/float"
88
+ autoload :Geo, "kali/type/geo"
89
+ autoload :List, "kali/type/list"
90
+ autoload :DateTime, "kali/type/date_time"
91
+ autoload :Date, "kali/type/date"
92
+ autoload :Time, "kali/type/time"
93
+ autoload :Duration, "kali/type/duration"
94
+ autoload :URI, "kali/type/uri"
95
+ autoload :CalAddress, "kali/type/cal_address"
96
+ autoload :Quoted, "kali/type/quoted"
97
+ autoload :Boolean, "kali/type/boolean"
98
+ end
99
+
100
+ # Public: Helper class that allows us to easily restrict a value from a set of
101
+ # predetermined values. It's just a Set that implements the case equality
102
+ # operator to check for Set inclusion.
103
+ #
104
+ # This is meant to be used as a Type restriction. See Type for examples.
105
+ #
106
+ # Examples:
107
+ # Enum[1, 2, 3] === 1 #=> true
108
+ # Enum[1, 2, 3] === 5 #=> false
109
+ class Enum < Set
110
+ def self.[](*values)
111
+ new(values).freeze
112
+ end
113
+
114
+ def ===(element)
115
+ include?(element)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,18 @@
1
+ module Kali
2
+ class Type::Boolean < Type
3
+ def initialize(requirement = nil)
4
+ super(->(o) { (o == true || o == false) && (requirement.nil? || requirement === o) })
5
+ end
6
+
7
+ def encode!(flag)
8
+ flag ? "TRUE" : "FALSE"
9
+ end
10
+
11
+ def decode!(string)
12
+ case string
13
+ when "TRUE"; true
14
+ when "FALSE"; false
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ require "uri"
2
+
3
+ module Kali
4
+ # The CAL-ADDRESS type represents a person involved with an event. It must be
5
+ # an email URI.
6
+ #
7
+ # See http://tools.ietf.org/html/rfc5545#section-3.3.3
8
+ class Type::CalAddress < Type::URI
9
+ def initialize
10
+ super(::URI::MailTo)
11
+ end
12
+
13
+ def parameters
14
+ {
15
+ Parameter::CommonName => :cn,
16
+ Parameter::CalendarUserType => :cutype,
17
+ Parameter::Delegators => :delegators,
18
+ Parameter::Delegatees => :delegatees,
19
+ Parameter::DirectoryEntry => :dir,
20
+ Parameter::GroupMemberships => :members,
21
+ Parameter::EventParticipationStatus => :partstat,
22
+ Parameter::ParticipationRole => :role,
23
+ Parameter::RSVPExpectation => :rsvp,
24
+ Parameter::SentBy => :sent_by
25
+ }
26
+ end
27
+ end
28
+ end