icalendar2 0.1.0

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.
Files changed (68) hide show
  1. data/CHANGELOG.md +2 -0
  2. data/README.md +89 -0
  3. data/lib/icalendar2/calendar.rb +85 -0
  4. data/lib/icalendar2/calendar_property/calscale.rb +9 -0
  5. data/lib/icalendar2/calendar_property/method.rb +9 -0
  6. data/lib/icalendar2/calendar_property/prodid.rb +9 -0
  7. data/lib/icalendar2/calendar_property/version.rb +9 -0
  8. data/lib/icalendar2/calendar_property.rb +12 -0
  9. data/lib/icalendar2/component/base.rb +90 -0
  10. data/lib/icalendar2/component/event.rb +32 -0
  11. data/lib/icalendar2/component.rb +8 -0
  12. data/lib/icalendar2/parameter/altrep.rb +8 -0
  13. data/lib/icalendar2/parameter/base.rb +34 -0
  14. data/lib/icalendar2/parameter/cn.rb +8 -0
  15. data/lib/icalendar2/parameter/cutype.rb +8 -0
  16. data/lib/icalendar2/parameter/delegated_from.rb +8 -0
  17. data/lib/icalendar2/parameter/delegated_to.rb +8 -0
  18. data/lib/icalendar2/parameter/dir.rb +8 -0
  19. data/lib/icalendar2/parameter/encoding.rb +8 -0
  20. data/lib/icalendar2/parameter/fmttype.rb +10 -0
  21. data/lib/icalendar2/parameter.rb +4 -0
  22. data/lib/icalendar2/parser.rb +136 -0
  23. data/lib/icalendar2/property/attach.rb +10 -0
  24. data/lib/icalendar2/property/attendee.rb +8 -0
  25. data/lib/icalendar2/property/base.rb +80 -0
  26. data/lib/icalendar2/property/categories.rb +10 -0
  27. data/lib/icalendar2/property/class.rb +9 -0
  28. data/lib/icalendar2/property/comment.rb +8 -0
  29. data/lib/icalendar2/property/contact.rb +8 -0
  30. data/lib/icalendar2/property/description.rb +9 -0
  31. data/lib/icalendar2/property/dtend.rb +9 -0
  32. data/lib/icalendar2/property/dtstamp.rb +9 -0
  33. data/lib/icalendar2/property/dtstart.rb +9 -0
  34. data/lib/icalendar2/property/exdate.rb +10 -0
  35. data/lib/icalendar2/property/geo.rb +9 -0
  36. data/lib/icalendar2/property/last_mod.rb +9 -0
  37. data/lib/icalendar2/property/location.rb +9 -0
  38. data/lib/icalendar2/property/nil.rb +13 -0
  39. data/lib/icalendar2/property/organizer.rb +9 -0
  40. data/lib/icalendar2/property/priority.rb +9 -0
  41. data/lib/icalendar2/property/rdate.rb +8 -0
  42. data/lib/icalendar2/property/related_to.rb +8 -0
  43. data/lib/icalendar2/property/resources.rb +8 -0
  44. data/lib/icalendar2/property/rrule.rb +10 -0
  45. data/lib/icalendar2/property/rstatus.rb +8 -0
  46. data/lib/icalendar2/property/sequence.rb +9 -0
  47. data/lib/icalendar2/property/summary.rb +9 -0
  48. data/lib/icalendar2/property/uid.rb +9 -0
  49. data/lib/icalendar2/property.rb +9 -0
  50. data/lib/icalendar2/tokens.rb +32 -0
  51. data/lib/icalendar2/value/binary_value.rb +8 -0
  52. data/lib/icalendar2/value/boolean_value.rb +6 -0
  53. data/lib/icalendar2/value/cal_address_value.rb +6 -0
  54. data/lib/icalendar2/value/date_time_value.rb +14 -0
  55. data/lib/icalendar2/value/date_value.rb +14 -0
  56. data/lib/icalendar2/value/duration_value.rb +14 -0
  57. data/lib/icalendar2/value/float_value.rb +7 -0
  58. data/lib/icalendar2/value/integer_value.rb +6 -0
  59. data/lib/icalendar2/value/lat_lon_value.rb +6 -0
  60. data/lib/icalendar2/value/period_value.rb +8 -0
  61. data/lib/icalendar2/value/recur_value.rb +39 -0
  62. data/lib/icalendar2/value/text_value.rb +17 -0
  63. data/lib/icalendar2/value/time_value.rb +6 -0
  64. data/lib/icalendar2/value/uri_value.rb +6 -0
  65. data/lib/icalendar2/value.rb +45 -0
  66. data/lib/icalendar2/version.rb +3 -0
  67. data/lib/icalendar2.rb +74 -0
  68. metadata +131 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ 0.1.0
2
+ * Initial commit
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # icalendar2 - generate, consume and validate iCalendar feeds
2
+
3
+ ## Warning
4
+
5
+ This is NOT production ready yet. It has not been extensively tested, and you
6
+ use it at your own risk.
7
+
8
+ ## Installation
9
+
10
+ If you want to jump right in and try it out, here's how you install it:
11
+
12
+ ``` bash
13
+ gem install icalendar2
14
+ ```
15
+
16
+ Website: https://github.com/ericcf/icalendar2
17
+
18
+ ## Introduction
19
+
20
+ icalendar2 is a Ruby library for parsing, manipulating and generating iCalendar
21
+ objects as described in RFC 5545. Its API is intended to be mostly compatible
22
+ with that of the icalendar gem (https://github.com/sdague/icalendar), at least
23
+ initially.
24
+
25
+ For example:
26
+
27
+ ``` ruby
28
+ require 'rubygems'
29
+ require 'icalendar2'
30
+ include Icalendar2
31
+
32
+ calendar = Calendar.new
33
+ calendar.event do
34
+ dtstart Date.new(2012, 12, 25)
35
+ dtend Date.new(2012, 1, 5)
36
+ summary "12 days of Christmas."
37
+ description "Eat lots of cookies."
38
+ end
39
+ calendar.valid? # true
40
+ puts calendar.to_ical
41
+ # BEGIN:VCALENDAR
42
+ # BEGIN:VEVENT
43
+ # UID:2012-12-12T10:12:45-06:00_253060006@example.com
44
+ # DTSTAMP:20121212T101245
45
+ # DTSTART:20121225
46
+ # DTEND:20120105
47
+ # SUMMARY:12 days of Christmas.
48
+ # DESCRIPTION:Eat lots of cookies.
49
+ # END:VEVENT
50
+ # END:VCALENDAR
51
+
52
+ calendars = Parser.new(calendar.to_ical).parse
53
+ calendars.size # 1
54
+ calendars.first.valid? # true
55
+ ```
56
+
57
+ ## Improvements over/differences with icalendar gem
58
+
59
+ While this gem is based on (and borrows some code from) the icalendar gem, it
60
+ also improves on it in several key areas:
61
+ * No monkey patching of core Ruby classes such as String for conversions
62
+ * Better separation of concerns makes it easier to test and extend
63
+ * Validates calendars and pinpoints errors
64
+ * Uses RSpec for tests
65
+
66
+ ## Testing
67
+
68
+ To run the icalendar2 tests ensure that the rspec gem is installed, and run:
69
+
70
+ ``` bash
71
+ rake test
72
+ ```
73
+
74
+ ## Building the gem
75
+
76
+ ``` bash
77
+ gem build icalendar2.gemspec
78
+ ```
79
+
80
+ ## Contributions
81
+
82
+ I need your help to make this library better. Please use the GitHub issue
83
+ tracker for feature requests and bug reports. The more detail you can provide
84
+ the better.
85
+
86
+ ## License
87
+
88
+ As this is based on the icalendar gem, which employs the Ruby licence, this too
89
+ falls under that license: http://www.ruby-lang.org/en/about/license.txt
@@ -0,0 +1,85 @@
1
+ module Icalendar2
2
+ # See http://tools.ietf.org/html/rfc5545#section-3.4
3
+ class Calendar
4
+ VALUE = "VCALENDAR"
5
+
6
+ def initialize
7
+ @components = {
8
+ Event::VALUE => []
9
+ }
10
+ @properties = {
11
+ :calscale => Property::Nil.new,
12
+ :method => Property::Nil.new,
13
+ :prodid => Property::Nil.new,
14
+ :version => Property::Nil.new
15
+ }
16
+ end
17
+
18
+ def calscale(value = nil)
19
+ set_property(:calscale, value)
20
+ end
21
+
22
+ def method_property(value = nil)
23
+ set_property(:method, value)
24
+ end
25
+
26
+ def prodid(value = nil)
27
+ set_property(:prodid, value)
28
+ end
29
+
30
+ def version(value = nil)
31
+ set_property(:version, value)
32
+ end
33
+
34
+ def set_property(property_name, value, parameters = {})
35
+ property = property_name.downcase.to_sym
36
+ if value.nil?
37
+ @properties[property].value.to_s
38
+ elsif (factory = CalendarProperty.get_factory(property_name))
39
+ if value.is_a? factory
40
+ @properties[property] = value
41
+ else
42
+ @properties[property] = factory.new(value)
43
+ end
44
+ end
45
+ end
46
+
47
+ def events
48
+ @components[Event::VALUE]
49
+ end
50
+
51
+ def event(&block)
52
+ e = Event.new
53
+ e.instance_eval(&block)
54
+ events << e
55
+
56
+ e
57
+ end
58
+
59
+ def add_event(event)
60
+ events << event
61
+ end
62
+
63
+ def to_ical
64
+ str = "#{Tokens::COMPONENT_BEGIN}:#{VALUE}#{Tokens::CRLF}"
65
+ str << body_to_ical
66
+ str << "#{Tokens::COMPONENT_END}:#{VALUE}#{Tokens::CRLF}"
67
+ str.encode("UTF-8")
68
+ end
69
+
70
+ def add_component(component)
71
+ @components[component.class::VALUE] << component
72
+ end
73
+
74
+ def valid?
75
+ events.all?(&:valid?)
76
+ end
77
+
78
+ private
79
+
80
+ def body_to_ical
81
+ str = @properties.values.map(&:to_ical).join
82
+ str << events.map(&:to_ical).join
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ module Icalendar2
2
+ module CalendarProperty
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.7.1
4
+ class Calscale < Property::Base
5
+ name "CALSCALE"
6
+ value :types => [:text]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Icalendar2
2
+ module CalendarProperty
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.7.2
4
+ class Method < Property::Base
5
+ name "METHOD"
6
+ value :types => [:text]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Icalendar2
2
+ module CalendarProperty
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.7.3
4
+ class Prodid < Property::Base
5
+ name "PRODID"
6
+ value :types => [:text]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Icalendar2
2
+ module CalendarProperty
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.7.4
4
+ class Version < Property::Base
5
+ name "VERSION"
6
+ value :types => [:text]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Icalendar2
2
+ module CalendarProperty
3
+ def self.get_factory(calendar_property_name)
4
+ # "VERSION" => CalendarProperty::Version, etc.
5
+ begin
6
+ const_get(calendar_property_name.capitalize, false)
7
+ rescue NameError
8
+ nil
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,90 @@
1
+ module Icalendar2
2
+ module Component
3
+ require 'socket'
4
+
5
+ class Base
6
+
7
+ class << self; attr_reader :property_names end
8
+ class << self; attr_reader :required_property_names end
9
+
10
+ def self.requires(properties = {})
11
+ add_properties(properties)
12
+ @required_property_names = properties.values.flatten
13
+ end
14
+
15
+ def self.accepts(properties = {})
16
+ add_properties(properties)
17
+ end
18
+
19
+ def initialize
20
+ @properties = {}
21
+ end
22
+
23
+ def new_uid
24
+ "#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}"
25
+ end
26
+
27
+ def new_timestamp
28
+ DateTime.now
29
+ end
30
+
31
+ def valid?
32
+ present_property_names = @properties.keys
33
+ @properties.values.flatten.all?(&:valid?) &&
34
+ self.class.required_property_names.all? { |p| present_property_names.include?(p) }
35
+ end
36
+
37
+ def set_property(property_name, value, parameters = {})
38
+ name = (property_name == "class" ? "klass" : property_name)
39
+ unless self.class.property_names.include? name.to_sym
40
+ raise "#{self.class} component property #{name} not defined"
41
+ end
42
+ self.send(name, value, parameters)
43
+ end
44
+
45
+ private
46
+
47
+ def self.add_properties(properties)
48
+ single_properties = properties[:exactly_one] || []
49
+ single_properties.each do |property_sym|
50
+ define_accessor(property_sym)
51
+ end
52
+ multiple_properties = properties[:one_or_more] || []
53
+ multiple_properties.each do |property_sym|
54
+ define_multiple_accessor(property_sym)
55
+ end
56
+ @property_names ||= []
57
+ @property_names += single_properties + multiple_properties
58
+ end
59
+
60
+ def self.define_accessor(property_sym)
61
+ define_method(property_sym) do |*args|
62
+ value, parameters = *args
63
+ if value.nil?
64
+ @properties[property_sym]
65
+ else
66
+ @properties[property_sym] = Property.get_factory(property_sym).new(value, parameters)
67
+ end
68
+ end
69
+ alias_method "#{property_sym}=", property_sym
70
+ end
71
+
72
+ def self.define_multiple_accessor(property_sym)
73
+ property_factory = Property.get_factory(property_sym)
74
+ define_method(property_factory::PLURAL) do
75
+ @properties[property_sym]
76
+ end
77
+ define_method(property_sym) do |*args|
78
+ value, parameters = *args
79
+ @properties[property_sym] ||= []
80
+ @properties[property_sym] << property_factory.new(value, parameters)
81
+ end
82
+ alias_method "#{property_sym}=", property_sym
83
+ end
84
+
85
+ def properties_to_ical
86
+ @properties.values.flatten.map(&:to_ical).join
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,32 @@
1
+ module Icalendar2
2
+ # See http://tools.ietf.org/html/rfc5545#section-3.6.1
3
+ class Event < Component::Base
4
+ VALUE = "VEVENT"
5
+
6
+ requires :exactly_one => [:dtstamp, :uid]
7
+ accepts :exactly_one => [:dtstart, :klass, :created, :description, :geo,
8
+ :last_mod, :location, :organizer, :priority, :sequence, :status, :summary,
9
+ :transp, :url, :recurid, :dtend, :duration],
10
+ :one_or_more => [:rrule, :attach, :attendee, :categories, :comment,
11
+ :contact, :exdate, :rstatus, :related_to, :resources, :rdate]
12
+
13
+ def initialize
14
+ super
15
+ self.uid = new_uid
16
+ self.dtstamp = new_timestamp
17
+ end
18
+
19
+ def to_ical
20
+ str = "#{Tokens::COMPONENT_BEGIN}:#{VALUE}#{Tokens::CRLF}"
21
+ str << properties_to_ical
22
+ #str << alarm_to_ical
23
+ str << "#{Tokens::COMPONENT_END}:#{VALUE}#{Tokens::CRLF}"
24
+ end
25
+
26
+ private
27
+
28
+ def alarm_to_ical
29
+ #alarm.print if alarm
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Component
3
+ def self.get_factory(component_name)
4
+ # "VFOO" => "Foo", etc.
5
+ Icalendar2.const_get(component_name.gsub(/^V/, "").capitalize, false)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.1
4
+ class Altrep < Base
5
+ format /^ALTREP="#{Tokens::URI}"$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ class Base
4
+
5
+ class << self; attr_reader :format_matcher end
6
+
7
+ def self.format(matcher)
8
+ @format_matcher = matcher
9
+ end
10
+
11
+ def initialize(value)
12
+ @valid = true
13
+ @value = value
14
+ validate
15
+ end
16
+
17
+ def valid?
18
+ @valid
19
+ end
20
+
21
+ private
22
+
23
+ def validate
24
+ unless self.class.format_matcher.nil?
25
+ if @value.respond_to?(:to_s) && (val = @value.to_s).respond_to?(:match)
26
+ @valid = !!val.match(self.class.format_matcher)
27
+ else
28
+ @valid = false
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.2
4
+ class Cn < Base
5
+ format /^CN=#{Tokens::PARAM_VALUE}$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.3
4
+ class Cutype < Base
5
+ format /^CUTYPE=(?:INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN|(?:#{Tokens::X_NAME})|(?:#{Tokens::IANA_TOKEN}))$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.4
4
+ class DelegatedFrom < Base
5
+ format /^DELEGATED-FROM="#{Tokens::URI}"(?:,"#{Tokens::URI}")*$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.5
4
+ class DelegatedTo < Base
5
+ format /^DELEGATED-TO="#{Tokens::URI}"(?:,"#{Tokens::URI}")*$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.6
4
+ class Dir < Base
5
+ format /^DIR="#{Tokens::URI}"$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.7
4
+ class Encoding < Base
5
+ format /^ENCODING=(?:8BIT|BASE64)$/
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.2.8
4
+ class Fmttype < Base
5
+ TYPE_NAME = "(?:#{Tokens::ALPHA}|#{Tokens::DIGIT}|[-!#$&.+^_])+"
6
+ SUBTYPE_NAME = TYPE_NAME
7
+ format /^FMTTYPE=#{TYPE_NAME}\/#{SUBTYPE_NAME}$/
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Icalendar2
2
+ module Parameter
3
+ end
4
+ end
@@ -0,0 +1,136 @@
1
+ module Icalendar2
2
+ require 'stringio'
3
+
4
+ class Parser
5
+
6
+ def initialize(source)
7
+ if source.respond_to?(:gets)
8
+ @file = source
9
+ elsif !source.nil? && source.respond_to?(:to_s)
10
+ @file = StringIO.new(source.to_s, 'r')
11
+ else
12
+ raise ArgumentError, "#{self.class}.new must be called with an IO-like object or a path"
13
+ end
14
+
15
+ @previous_line = next_line_of_file
16
+ end
17
+
18
+ # Returns an iCalendar stream (collection of iCalendar objects).
19
+ def parse
20
+ calendars = []
21
+
22
+ while (line = next_multiline)
23
+ fields = parse_line(line)
24
+ if fields[:name] == Tokens::COMPONENT_BEGIN && fields[:value] == Calendar::VALUE
25
+ calendar = parse_calendar
26
+ calendars << calendar
27
+ end
28
+ end
29
+
30
+ calendars
31
+ end
32
+
33
+ private
34
+
35
+ def parse_calendar
36
+ calendar = Calendar.new
37
+
38
+ while (line = next_multiline)
39
+ fields = parse_line(line)
40
+
41
+ name, value = fields[:name].upcase, fields[:value]
42
+ if name == Tokens::COMPONENT_BEGIN
43
+ component_factory = Component.get_factory(value)
44
+ if component_factory.nil?
45
+ raise "Unable to find component: #{value}"
46
+ end
47
+ calendar.add_component parse_component(component_factory.new)
48
+ else
49
+ calendar.set_property(name, value, fields[:params])
50
+ end
51
+ end
52
+
53
+ calendar
54
+ end
55
+
56
+ def parse_component(component)
57
+ while (line = next_multiline)
58
+ fields = parse_line(line)
59
+ name = fields[:name].downcase
60
+ break if name.upcase == Tokens::COMPONENT_END
61
+ params, value = fields[:params], fields[:value]
62
+ component.set_property(name, value, params)
63
+ end
64
+
65
+ component
66
+ end
67
+
68
+ CONTINUED_LINE = /^[ \t]/
69
+ NAME = "(?:#{Tokens::IANA_TOKEN}|#{Tokens::X_NAME})"
70
+ PARAM_NAME = "(?:#{Tokens::IANA_TOKEN}|#{Tokens::X_NAME})"
71
+ PARAM = "(?:#{PARAM_NAME}=#{Tokens::PARAM_VALUE}(?:,#{Tokens::PARAM_VALUE})*)"
72
+ PARAM_CAPTURE = "(#{PARAM_NAME})=(#{Tokens::PARAM_VALUE}(?:,#{Tokens::PARAM_VALUE})*)"
73
+ CONTENT_LINE = /^(#{NAME})(;#{PARAM})*:(#{Tokens::VALUE_CHAR}*)/
74
+
75
+ def next_line_of_file
76
+ line = @file.gets
77
+ line.chomp! unless line.nil?
78
+ end
79
+
80
+ def next_multiline
81
+ line = @previous_line
82
+
83
+ if line.nil?
84
+ return nil
85
+ end
86
+
87
+ loop do
88
+ next_line = next_line_of_file
89
+
90
+ if next_line =~ CONTINUED_LINE
91
+ line << next_line[1, next_line.size]
92
+ elsif next_line.nil? || next_line.size > 0
93
+ @previous_line = next_line
94
+ break
95
+ end
96
+ end
97
+
98
+ line
99
+ end
100
+
101
+ def parse_line(line)
102
+ unless line =~ %r{#{CONTENT_LINE}}i
103
+ raise "Invalid line in calendar string: #{line}"
104
+ end
105
+
106
+ name = $1.upcase
107
+ value = unescape($3)
108
+ params = parse_params_str($2)
109
+
110
+ { :name => name, :value => value, :params => params }
111
+ end
112
+
113
+ def unescape(str)
114
+ str.gsub("\\;", ";").gsub("\\,", ",").gsub("\\n", "\n").gsub("\\\\", "\\")
115
+ end
116
+
117
+ def parse_params_str(params_str)
118
+ params = {}
119
+ if !params_str.nil? && params_str.size > 1
120
+ params_str.scan(/#{PARAM_CAPTURE}/) do
121
+ name = $1
122
+ values = $2
123
+
124
+ params[name] ||= []
125
+ values.scan(/(#{Tokens::PARAM_VALUE})/) do
126
+ if $1.size > 0
127
+ params[name] << $1
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ params
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,10 @@
1
+ module Icalendar2
2
+ module Property
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.8.1.1
4
+ class Attach < Base
5
+ PLURAL = "attachments"
6
+ name "ATTACH"
7
+ value :types => [:uri, :binary]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module Icalendar2
2
+ module Property
3
+ # See http://tools.ietf.org/html/rfc5545#section-3.8.4.1
4
+ class Attendee < Base
5
+ PLURAL = "attendees"
6
+ end
7
+ end
8
+ end