caldav 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,3 @@
1
+ CalDAV library
2
+
3
+ based on 4fthawaiian/ruby-caldav and collectiveidea/caldav
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'caldav'
4
+
5
+ prg = CalDAV::CalDAVer.new
6
+ prg.run_args( ARGV )
7
+
@@ -0,0 +1,99 @@
1
+ require './lib/caldav.rb'
2
+
3
+ class CalDAVTester
4
+
5
+ def initialize
6
+ $caldav = {
7
+ :host => 'mail.server.com',
8
+ :port => 443,
9
+ :url => '/caldav.php/martin.povolny@solnet.cz/test',
10
+ :user => 'user@test.com',
11
+ :password => 'yourpassword'
12
+ }
13
+ begin
14
+ require '~/.caldav_cred.rb'
15
+ rescue LoadError
16
+ end
17
+
18
+ @cal = CalDAV::Client.new $caldav[:host], $caldav[:port], $caldav[:url], $caldav[:user], $caldav[:password]
19
+ end
20
+
21
+ attr :cal
22
+
23
+ def test_create_event
24
+ myevent = Event.new
25
+ myevent.dtstart = DateTime.parse( '2012/08/11 09:45')
26
+ myevent.dtend = DateTime.parse( '2012/08/11 10:45')
27
+ myevent.summary = "Jo?"
28
+
29
+ uuid, response = cal.create myevent
30
+
31
+ puts "CREATE Response #{response.code} #{response.message}: #{response.body}"
32
+ puts "UUID: #{uuid}"
33
+
34
+ return uuid
35
+ end
36
+
37
+ def test_delete_event( uuid )
38
+ response = cal.delete uuid
39
+ puts "DETELE Response #{response.code} #{response.message}: #{response.body}"
40
+ end
41
+
42
+ def test_get_event( uuid )
43
+ ev, response = cal.get uuid
44
+ puts "GET Response #{response.code} #{response.message}: #{response.body}"
45
+ puts ev
46
+ end
47
+
48
+ def test_read_todo
49
+ puts '*' * 20 + ' TODO ' + '*' * 20
50
+ res = cal.todo
51
+
52
+ res.each{ |todo|
53
+ p todo
54
+ }
55
+ end
56
+
57
+ def test_report
58
+ puts '*' * 20 + ' EVENTS ' + '*' * 20
59
+ p cal.report "20111201T000000", "20131231T000000"
60
+ end
61
+
62
+ def test_query0
63
+ puts CalDAV::Query.event.to_xml
64
+
65
+ time1 = DateTime.parse('2011/08/08 09:45')
66
+ time2 = DateTime.parse('2012/08/08 10:45')
67
+ puts CalDAV::Query.event(time1..time2).to_xml
68
+ puts CalDAV::Query.event.uid( UUID.generate ).to_xml
69
+ #puts CalDAV::Query.todo.alarm(time1..time2).to_xml
70
+ #puts CalDAV::Query.event.attendee(email).partstat('NEEDS-ACTION').to_xml
71
+ #puts CalDAV::Query.todo.completed(false).status(:cancelled => false).to_xml
72
+ end
73
+
74
+ def test_query
75
+ p cal.query( CalDAV::Query.event ) #=> All events
76
+ p cal.query( CalDAV::Query.event(time1..time2) )
77
+ p cal.query( CalDAV::Query.event.uid("UID") )
78
+ p cal.query( CalDAV::Query.todo.alarm(time1..time2) )
79
+ p cal.query( CalDAV::Query.event.attendee(email).partstat('NEEDS-ACTION') )
80
+ p cal.query( CalDAV::Query.todo.completed(false).status(:cancelled => false) )
81
+ end
82
+ end
83
+
84
+ #r = CalDAV::Request::Report.new( "20111201T000000", "20131231T000000" )
85
+ #puts r.to_xml
86
+ #exit
87
+
88
+ t = CalDAVTester.new
89
+
90
+ t.test_query0
91
+
92
+ uuid = t.test_create_event
93
+ t.test_get_event( uuid )
94
+ t.test_delete_event( uuid )
95
+
96
+ t.test_report
97
+ t.test_read_todo
98
+ #t.test_query
99
+
@@ -0,0 +1,19 @@
1
+ require 'net/https'
2
+ require 'uuid'
3
+ require 'rexml/document'
4
+ require 'rexml/xpath'
5
+ require 'date'
6
+ require 'icalendar'
7
+ require 'time'
8
+
9
+ ['client.rb', 'request.rb', 'net.rb', 'query.rb', 'filter.rb', 'caldaver.rb'].each do |f|
10
+ require File.join( File.dirname(__FILE__), 'caldav', f )
11
+ end
12
+
13
+ class Event
14
+ attr_accessor :uid, :created, :dtstart, :dtend, :lastmodified, :summary, :description, :name, :action
15
+ end
16
+
17
+ class Todo
18
+ attr_accessor :uid, :created, :summary, :dtstart, :status, :completed
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'optparse'
2
+
3
+ # caldav --user USER --password PASSWORD --uri URI --command COMMAND
4
+ # caldav --user martin@solnet.cz --password test --uri https://mail.solnet.cz/caldav.php/martin.povolny@solnet.cz/test --command create_event
5
+
6
+ module CalDAV
7
+
8
+ class CalDAVer
9
+ def run_args( args )
10
+ o = nil
11
+
12
+ help = Proc.new do
13
+ puts o.help
14
+ exit 1
15
+ end
16
+
17
+ options = {}
18
+ o = OptionParser.new do |o|
19
+ o.on('-p', '--password', String, 'Password') { |p| options[:password] = p }
20
+ o.on('-u', '--user', String, 'User (login)') { |l| options[:login] = l }
21
+ o.on('--uri [STRING]', String, 'Calendar URI') { |uri| options[:uri] = uri }
22
+ o.on('--command [STRING]', String, 'Command') { |c| options[:command] = command }
23
+ o.on('--begin [DATETIME]', DateTime, 'Start time') { |dt| options[:begin] = dt }
24
+ o.on('--end [DATETIME]', DateTime, 'End time') { |dt| options[:end] = dt }
25
+ o.on('-h') { help.call }
26
+ end
27
+
28
+ o.parse( args )
29
+
30
+ help.call if options[:command].to_s.empty? or options[:uri].to_s.empty?
31
+
32
+ cal = cal.new( options[:uri], options[:login], options[:password] )
33
+
34
+ case options[:command].to_s
35
+ when 'create'
36
+ when 'delete'
37
+ #when 'modify'
38
+ when 'get'
39
+ when 'report'
40
+ cal.report( options[:begin], options[:end] )
41
+ else
42
+ help.call
43
+ end
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,243 @@
1
+ module CalDAV
2
+ class Client
3
+ include Icalendar
4
+ attr_accessor :host, :port, :url, :user, :password, :ssl
5
+
6
+ def initialize( *args )
7
+ case args.length
8
+ when 3
9
+ __init_from_uri( *args )
10
+ when 5
11
+ __init_from_host_port( *args )
12
+ else
13
+ raise "#{self.class.to_s}: invalid number of arguments: #{args.length}"
14
+ end
15
+ end
16
+
17
+ def __init_from_uri( suri, user, password )
18
+ uri = URI.new( suri )
19
+ puts "FIXME"
20
+ end
21
+
22
+ def __init_from_host_port( host, port, url, user, password )
23
+ @host = host
24
+ @port = port
25
+ @url = url
26
+ @user = user
27
+ @password = password
28
+ @ssl = port == 443
29
+ end
30
+
31
+ def __create_http
32
+ http = Net::HTTP.new(@host, @port)
33
+ http.use_ssl = @ssl
34
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
+ #http.set_debug_output $stderr
36
+ http
37
+ end
38
+
39
+ def report start, stop
40
+ res = nil
41
+ __create_http.start {|http|
42
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
43
+ req.basic_auth @user, @password
44
+ req.body = CalDAV::Request::ReportVEVENT.new( start, stop ).to_xml
45
+ res = http.request( req )
46
+ }
47
+ result = []
48
+ xml = REXML::Document.new( res.body )
49
+ REXML::XPath.each( xml, '//c:calendar-data/', { "c"=>"urn:ietf:params:xml:ns:caldav"} ){ |c|
50
+ result += parse_events( c.text )
51
+ }
52
+ return result
53
+ end
54
+
55
+ def get uuid
56
+ res = nil
57
+ __create_http.start {|http|
58
+ req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
59
+ req.basic_auth @user, @password
60
+ res = http.request( req )
61
+ }
62
+
63
+ # FIXME: parse event/todo/vcard
64
+ return parse_events( res.body ), res
65
+ end
66
+
67
+ def delete uuid
68
+ __create_http.start {|http|
69
+ req = Net::HTTP::Delete.new("#{@url}/#{uuid}.ics")
70
+ req.basic_auth @user, @password
71
+ res = http.request( req )
72
+ }
73
+ end
74
+
75
+ def create event
76
+ nowstr = DateTime.now.strftime "%Y%m%dT%H%M%SZ"
77
+ uuid = UUID.generate
78
+ dings = """BEGIN:VCALENDAR
79
+ PRODID:Caldav.rb
80
+ VERSION:2.0
81
+ BEGIN:VEVENT
82
+ CREATED:#{nowstr}
83
+ UID:#{uuid}
84
+ SUMMARY:#{event.summary}
85
+ DTSTART:#{event.dtstart.strftime("%Y%m%dT%H%M%S")}
86
+ DTEND:#{event.dtend.strftime("%Y%m%dT%H%M%S")}
87
+ END:VEVENT
88
+ END:VCALENDAR"""
89
+
90
+ res = nil
91
+ http = Net::HTTP.new(@host, @port)
92
+ __create_http.start { |http|
93
+ req = Net::HTTP::Put.new("#{@url}/#{uuid}.ics")
94
+ req['Content-Type'] = 'text/calendar'
95
+ req.basic_auth @user, @password
96
+ req.body = dings
97
+ res = http.request( req )
98
+ }
99
+ return uuid, res
100
+ end
101
+
102
+ def add_alarm tevent, altCal="Calendar"
103
+ #[#<Icalendar::Alarm:0x10b9d1b90 @name=\"VALARM\", @components={}, @properties={\"trigger\"=>\"-PT5M\", \"action\"=>\"DISPLAY\", \"description\"=>\"\"}>]
104
+ dtstart_string = ( Time.parse(tevent.dtstart.to_s) + Time.now.utc_offset.to_i.abs ).strftime "%Y%m%dT%H%M%S"
105
+ dtend_string = ( Time.parse(tevent.dtend.to_s) + Time.now.utc_offset.to_i.abs ).strftime "%Y%m%dT%H%M%S"
106
+ alarmText = <<EOL
107
+ BEGIN:VCALENDAR
108
+ VERSION:2.0
109
+ PRODID:Ruby iCalendar
110
+ BEGIN:VEVENT
111
+ UID:#{tevent.uid}
112
+ SUMMARY:#{tevent.summary}
113
+ DESCRIPTION:#{tevent.description}
114
+ DTSTART:#{dtstart_string}
115
+ DTEND:#{dtend_string}
116
+ BEGIN:VALARM
117
+ ACTION:DISPLAY
118
+ TRIGGER;RELATED=START:-PT5M
119
+ DESCRIPTION:Reminder
120
+ END:VALARM
121
+ BEGIN:VALARM
122
+ TRIGGER:-PT5M
123
+ ACTION:EMAIL
124
+ ATTENDEE:#{tevent.organizer}
125
+ SUMMARY:#{tevent.summary}
126
+ DESCRIPTION:#{tevent.description}
127
+ TRIGGER:-PT5M
128
+ END:VALARM
129
+ END:VEVENT
130
+ END:VCALENDAR
131
+ EOL
132
+ p alarmText
133
+ res = nil
134
+ puts "#{@url}/#{tevent.uid}.ics"
135
+ thttp = __create_http.start
136
+ #thttp.set_debug_output $stderr
137
+ req = Net::HTTP::Put.new("#{@url}/#{tevent.uid}.ics", initheader = {'Content-Type'=>'text/calendar'} )
138
+ req.basic_auth @user, @password
139
+ req.body = alarmText
140
+ res = thttp.request( req )
141
+ p res.inspect
142
+
143
+ return tevent.uid
144
+ end
145
+
146
+ def update event
147
+ dings = """BEGIN:VCALENDAR
148
+ PRODID:Caldav.rb
149
+ VERSION:2.0
150
+
151
+ BEGIN:VTIMEZONE
152
+ TZID:/Europe/Vienna
153
+ X-LIC-LOCATION:Europe/Vienna
154
+ BEGIN:DAYLIGHT
155
+ TZOFFSETFROM:+0100
156
+ TZOFFSETTO:+0200
157
+ TZNAME:CEST
158
+ DTSTART:19700329T020000
159
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
160
+ END:DAYLIGHT
161
+ BEGIN:STANDARD
162
+ TZOFFSETFROM:+0200
163
+ TZOFFSETTO:+0100
164
+ TZNAME:CET
165
+ DTSTART:19701025T030000
166
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
167
+ END:STANDARD
168
+ END:VTIMEZONE
169
+
170
+ BEGIN:VEVENT
171
+ CREATED:#{event.created}
172
+ UID:#{event.uid}
173
+ SUMMARY:#{event.summary}
174
+ DTSTART;TZID=Europe/Vienna:#{event.dtstart}
175
+ DTEND;TZID=Europe/Vienna:#{event.dtend.rfc3339}
176
+ END:VEVENT
177
+ END:VCALENDAR"""
178
+
179
+ res = nil
180
+ __create_http.start {|http|
181
+ req = Net::HTTP::Put.new("#{@url}/#{event.uid}.ics", initheader = {'Content-Type'=>'text/calendar'} )
182
+ req.basic_auth @user, @passowrd
183
+ req.body = dings
184
+ res = http.request( req )
185
+ }
186
+ return event.uid
187
+ end
188
+
189
+ def todo
190
+ res = nil
191
+ __create_http.start {|http|
192
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
193
+ req.basic_auth @user, @password
194
+ req.body = CalDAV::Request::ReportVTODO.new.to_xml
195
+ res = http.request( req )
196
+ }
197
+ result = []
198
+ xml = REXML::Document.new( res.body )
199
+ #p res.body
200
+ REXML::XPath.each( xml, '//c:calendar-data/', { "c"=>"urn:ietf:params:xml:ns:caldav"} ){ |c|
201
+ p c.text
202
+ p parse_tasks( c.text )
203
+ result += parse_tasks( c.text )
204
+ }
205
+ return result
206
+ end
207
+
208
+ def parse_tasks( vcal )
209
+ return_tasks = Array.new
210
+ cals = Icalendar.parse(vcal)
211
+ cals.each { |tcal|
212
+ tcal.todos.each { |ttask| # FIXME
213
+ return_tasks << ttask
214
+ }
215
+ }
216
+ return return_tasks
217
+ end
218
+
219
+ def parse_events( vcal )
220
+ return_events = Array.new
221
+ cals = Icalendar.parse(vcal)
222
+ cals.each { |tcal|
223
+ tcal.events.each { |tevent|
224
+ if tevent.recurrence_id.to_s.empty? # skip recurring events
225
+ return_events << tevent
226
+ end
227
+ }
228
+ }
229
+ return return_events
230
+ end
231
+
232
+ def filterTimezone( vcal )
233
+ data = ""
234
+ inTZ = false
235
+ vcal.split("\n").each{ |l|
236
+ inTZ = true if l.index("BEGIN:VTIMEZONE")
237
+ data << l+"\n" unless inTZ
238
+ inTZ = false if l.index("END:VTIMEZONE")
239
+ }
240
+ return data
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,78 @@
1
+ module CalDAV
2
+ module Filter
3
+ class Base
4
+ attr_accessor :parent, :child
5
+
6
+ def to_xml(xml = Builder::XmlMarkup.new(:indent => 2))
7
+ if parent
8
+ parent.to_xml
9
+ else
10
+ build_xml(xml)
11
+ end
12
+ end
13
+
14
+ def build_xml(xml)
15
+ #do nothing
16
+ end
17
+
18
+ def child=(child)
19
+ @child = child
20
+ child.parent = self
21
+ end
22
+ end
23
+
24
+ class Component < Base
25
+ attr_accessor :name
26
+
27
+ def initialize(name, parent = nil)
28
+ self.name = name
29
+ self.parent = parent
30
+ end
31
+
32
+ def time_range(range)
33
+ self.child = TimeRange.new(range, self)
34
+ end
35
+
36
+ def uid(uid)
37
+ self.child = Property.new("UID", uid, self)
38
+ end
39
+
40
+ def build_xml(xml)
41
+ xml.tag! "cal:comp-filter", :name => name do
42
+ child.build_xml(xml) unless child.nil?
43
+ end
44
+ end
45
+ end
46
+
47
+ class TimeRange < Base
48
+ attr_accessor :range
49
+
50
+ def initialize(range, parent = nil)
51
+ self.range = range
52
+ self.parent = parent
53
+ end
54
+
55
+ def build_xml(xml)
56
+ xml.tag! "cal:time-range",
57
+ :start => range.begin.to_ical,
58
+ :end => range.end.to_ical
59
+ end
60
+ end
61
+
62
+ class Property < Base
63
+ attr_accessor :name, :text
64
+
65
+ def initialize(name, text, parent = nil)
66
+ self.name = name
67
+ self.text = text
68
+ self.parent = parent
69
+ end
70
+
71
+ def build_xml(xml)
72
+ xml.tag! "cal:prop-filter", :name => self.name do
73
+ xml.tag! "cal:text-match", self.text, :collation => "i;octet"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,16 @@
1
+ module Net
2
+ class HTTP
3
+ class Report < HTTPRequest
4
+ METHOD = 'REPORT'
5
+ REQUEST_HAS_BODY = true
6
+ RESPONSE_HAS_BODY = true
7
+ end
8
+
9
+ class Mkcalendar < HTTPRequest
10
+ METHOD = 'MKCALENDAR'
11
+ REQUEST_HAS_BODY = true
12
+ RESPONSE_HAS_BODY = true
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,51 @@
1
+ module CalDAV
2
+ class Query
3
+ attr_accessor :child
4
+
5
+ #TODO: raise error if to_xml is called before child is assigned
6
+ def to_xml(xml = Builder::XmlMarkup.new(:indent => 2))
7
+ xml.instruct!
8
+ xml.tag! "cal:calendar-query", CalDAV::NAMESPACES do
9
+ xml.tag! "dav:prop" do
10
+ xml.tag! "dav:getetag"
11
+ xml.tag! "cal:calendar-data"
12
+ end
13
+ xml.tag! "cal:filter" do
14
+ cal = Filter::Component.new("VCALENDAR", self)
15
+ cal.child = self.child
16
+ cal.build_xml(xml)
17
+ end
18
+ end
19
+ end
20
+
21
+ def event(param = nil)
22
+ self.child = Filter::Component.new("VEVENT")
23
+ if param.is_a? Range
24
+ self.child.time_range(param)
25
+ elsif param.is_a? String
26
+ self.child.uid(param)
27
+ else
28
+ self.child
29
+ end
30
+ end
31
+
32
+ def todo(param = nil)
33
+ self.child = Filter::Component.new("VTODO")
34
+ self.child
35
+ end
36
+
37
+ def child=(child)
38
+ child.parent = self
39
+ @child = child
40
+ end
41
+
42
+ def self.event( param=nil )
43
+ self.new.event( param )
44
+ end
45
+
46
+
47
+ def self.todo( param=nil )
48
+ self.new.todo
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ require 'builder'
2
+
3
+ module CalDAV
4
+ NAMESPACES = { "xmlns:d" => 'DAV:', "xmlns:c" => "urn:ietf:params:xml:ns:caldav" }
5
+ module Request
6
+ class Base
7
+ def initialize
8
+ @xml = Builder::XmlMarkup.new(:indent => 2)
9
+ @xml.instruct!
10
+ end
11
+ attr :xml
12
+ end
13
+
14
+ class Mkcalendar < Base
15
+ attr_accessor :displayname, :description
16
+
17
+ def initialize(displayname = nil, description = nil)
18
+ @displayname = displayname
19
+ @description = description
20
+ end
21
+
22
+ def to_xml
23
+ xml.c :mkcalendar, NAMESPACES do
24
+ xml.d :set do
25
+ xml.d :prop do
26
+ xml.d :displayname, displayname unless displayname.to_s.empty?
27
+ xml.tag! "c:calendar-description", description, "xml:lang" => "en" unless description.to_s.empty?
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ class ReportVEVENT < Base
35
+ attr_accessor :tstart, :tend
36
+
37
+ def initialize( tstart=nil, tend=nil )
38
+ @tstart = tstart
39
+ @tend = tend
40
+ super()
41
+ end
42
+
43
+ def to_xml
44
+ xml.c 'calendar-query'.intern, NAMESPACES do
45
+ xml.d :prop do
46
+ xml.d :getetag
47
+ xml.c 'calendar-data'.intern
48
+ end
49
+ xml.c :filter do
50
+ xml.c 'comp-filter'.intern, :name=> 'VCALENDAR' do
51
+ xml.c 'comp-filter'.intern, :name=> 'VEVENT' do
52
+ xml.c 'time-range'.intern, :start=> "#{tstart}Z", :end=> "#{tend}Z"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ class ReportVTODO < Base
61
+ def to_xml
62
+ xml.c 'calendar-query'.intern, NAMESPACES do
63
+ xml.d :prop do
64
+ xml.d :getetag
65
+ xml.c 'calendar-data'.intern
66
+ end
67
+ xml.c :filter do
68
+ xml.c 'comp-filter'.intern, :name=> 'VCALENDAR' do
69
+ xml.c 'comp-filter'.intern, :name=> 'VTODO'
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module CalDAV
2
+ VERSION=0.1
3
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../lib/caldav/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "caldav"
5
+ s.version = CalDAV::VERSION
6
+ s.summary = "Ruby CalDAV client"
7
+ s.description = "Ruby client for CalDAV calendar and tasks."
8
+ s.homepage = "https://github.com/martinpovolny/ruby-caldav"
9
+ s.authors = ["Martin Povolny"]
10
+ s.email = ["martin.povolny@gmail.com"]
11
+
12
+ s.add_runtime_dependency 'rexml'
13
+ s.add_runtime_dependency 'icalendar'
14
+ s.add_runtime_dependency 'uuid'
15
+ s.add_runtime_dependency 'builder'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
19
+ s.require_paths = ["lib"]
20
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: caldav
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Martin Povolny
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-08 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rexml
17
+ requirement: &19375540 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *19375540
26
+ - !ruby/object:Gem::Dependency
27
+ name: icalendar
28
+ requirement: &19375120 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *19375120
37
+ - !ruby/object:Gem::Dependency
38
+ name: uuid
39
+ requirement: &19374700 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *19374700
48
+ - !ruby/object:Gem::Dependency
49
+ name: builder
50
+ requirement: &19374280 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *19374280
59
+ description: Ruby client for CalDAV calendar and tasks.
60
+ email:
61
+ - martin.povolny@gmail.com
62
+ executables:
63
+ - caldav
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - README.rdoc
68
+ - bin/caldav
69
+ - caldavtest.rb
70
+ - lib/caldav.rb
71
+ - lib/caldav/caldaver.rb
72
+ - lib/caldav/client.rb
73
+ - lib/caldav/filter.rb
74
+ - lib/caldav/net.rb
75
+ - lib/caldav/query.rb
76
+ - lib/caldav/request.rb
77
+ - lib/caldav/version.rb
78
+ - ruby-caldav.gemspec
79
+ has_rdoc: true
80
+ homepage: https://github.com/martinpovolny/ruby-caldav
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.6.2
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Ruby CalDAV client
104
+ test_files: []