kali 0.0.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.
@@ -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