clio-gcal4ruby 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,213 @@
1
+ class Time
2
+ #Returns a ISO 8601 complete formatted string of the time
3
+ def complete
4
+ self.utc.strftime("%Y%m%dT%H%M%S")
5
+ end
6
+
7
+ def self.parse_complete(value)
8
+ d, h = value.split("T")
9
+ return Time.parse(d+" "+h.gsub("Z", ""))
10
+ end
11
+ end
12
+
13
+ module GCal4Ruby
14
+ #The Recurrence class stores information on an Event's recurrence. The class implements
15
+ #the RFC 2445 iCalendar recurrence description.
16
+ class Recurrence
17
+ #The event start date/time
18
+ attr_reader :start
19
+ #The event end date/time
20
+ attr_reader :end
21
+ #the event reference
22
+ attr_reader :event
23
+ #The date until which the event will be repeated
24
+ attr_reader :repeat_until
25
+ #The event frequency
26
+ attr_reader :frequency
27
+ #True if the event is all day (i.e. no start/end time)
28
+ attr_accessor :all_day
29
+
30
+ #Accepts an optional attributes hash or a string containing a properly formatted ISO 8601 recurrence rule. Returns a new Recurrence object
31
+ def initialize(vars = {})
32
+ if vars.is_a? Hash
33
+ vars.each do |key, value|
34
+ self.send("#{key}=", value)
35
+ end
36
+ elsif vars.is_a? String
37
+ self.load(vars)
38
+ end
39
+ @all_day ||= false
40
+ end
41
+
42
+ #Accepts a string containing a properly formatted ISO 8601 recurrence rule and loads it into the recurrence object
43
+ def load(rec)
44
+ attrs = rec.split("\n")
45
+ attrs.each do |val|
46
+ key, value = val.split(":")
47
+ case key
48
+ when 'DTSTART'
49
+ @start = Time.parse_complete(value)
50
+ when 'DTSTART;VALUE=DATE'
51
+ @start = Time.parse(value)
52
+ @all_day = true
53
+ when 'DTSTART;VALUE=DATE-TIME'
54
+ @start = Time.parse_complete(value)
55
+ when 'DTEND'
56
+ @end = Time.parse_complete(value)
57
+ when 'DTEND;VALUE=DATE'
58
+ @end = Time.parse(value)
59
+ when 'DTEND;VALUE=DATE-TIME'
60
+ @end = Time.parse_complete(value)
61
+ when 'RRULE'
62
+ vals = value.split(";")
63
+ key = ''
64
+ by = ''
65
+ int = nil
66
+ vals.each do |rr|
67
+ a, h = rr.split("=")
68
+ case a
69
+ when 'FREQ'
70
+ key = h.downcase.capitalize
71
+ when 'INTERVAL'
72
+ int = h
73
+ when 'UNTIL'
74
+ @repeat_until = Time.parse(value)
75
+ else
76
+ by = h.split(",")
77
+ end
78
+ end
79
+ @frequency = {key => by}
80
+ @frequency.merge({'interval' => int}) if int
81
+ end
82
+ end
83
+ end
84
+
85
+ #Returns a string with the correctly formatted ISO 8601 recurrence rule
86
+ def to_s
87
+
88
+ output = ''
89
+ if @all_day
90
+ output += "DTSTART;VALUE=DATE:#{@start.utc.strftime("%Y%m%d")}\n"
91
+ else
92
+ output += "DTSTART;VALUE=DATE-TIME:#{@start.complete}\n"
93
+ end
94
+ if @all_day
95
+ output += "DTEND;VALUE=DATE:#{@end.utc.strftime("%Y%m%d")}\n"
96
+ else
97
+ output += "DTEND;VALUE=DATE-TIME:#{@end.complete}\n"
98
+ end
99
+ output += "RRULE:"
100
+ if @frequency
101
+ f = 'FREQ='
102
+ i = ''
103
+ by = ''
104
+ @frequency.each do |key, v|
105
+ if v.is_a?(Array)
106
+ if v.size > 0
107
+ value = v.join(",")
108
+ else
109
+ value = nil
110
+ end
111
+ else
112
+ value = v
113
+ end
114
+ f += "#{key.upcase};" if key != 'interval'
115
+ case key.downcase
116
+ when "secondly"
117
+ by += "BYSECOND=#{value};"
118
+ when "minutely"
119
+ by += "BYMINUTE=#{value};"
120
+ when "hourly"
121
+ by += "BYHOUR=#{value};"
122
+ when "weekly"
123
+ by += "BYDAY=#{value};" if value
124
+ when "monthly"
125
+ by += "BYDAY=#{value};"
126
+ when "yearly"
127
+ by += "BYYEARDAY=#{value};"
128
+ when 'interval'
129
+ i += "INTERVAL=#{value};"
130
+ end
131
+ end
132
+ output += f+i+by
133
+ end
134
+ if @repeat_until
135
+ output += "UNTIL=#{@repeat_until.strftime("%Y%m%d")}"
136
+ end
137
+
138
+ output += "\n"
139
+ end
140
+
141
+ #Sets the start date/time. Must be a Time object.
142
+ def start=(s)
143
+ if not s.is_a?(Time)
144
+ raise RecurrenceValueError, "Start must be a date or a time"
145
+ else
146
+ @start = s
147
+ end
148
+ end
149
+
150
+ #Sets the end Date/Time. Must be a Time object.
151
+ def end=(e)
152
+ if not e.is_a?(Time)
153
+ raise RecurrenceValueError, "End must be a date or a time"
154
+ else
155
+ @end = e
156
+ end
157
+ end
158
+
159
+ #Sets the parent event reference
160
+ def event=(e)
161
+ if not e.is_a?(Event)
162
+ raise RecurrenceValueError, "Event must be an event"
163
+ else
164
+ @event = e
165
+ end
166
+ end
167
+
168
+ #Sets the end date for the recurrence
169
+ def repeat_until=(r)
170
+ if not r.is_a?(Date)
171
+ raise RecurrenceValueError, "Repeat_until must be a date"
172
+ else
173
+ @repeat_until = r
174
+ end
175
+ end
176
+
177
+ #Sets the frequency of the recurrence. Should be a hash with one of
178
+ #"SECONDLY", "MINUTELY", "HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" as the key,
179
+ #and as the value, an array containing zero to n of the following:
180
+ #- *Secondly*: A value between 0 and 59. Causes the event to repeat on that second of each minut.
181
+ #- *Minutely*: A value between 0 and 59. Causes the event to repeat on that minute of every hour.
182
+ #- *Hourly*: A value between 0 and 23. Causes the even to repeat on that hour of every day.
183
+ #- *Daily*: No value needed - will cause the event to repeat every day until the repeat_until date.
184
+ #- *Weekly*: A value of the first two letters of a day of the week. Causes the event to repeat on that day.
185
+ #- *Monthly*: A value of a positive or negative integer (i.e. +1) prepended to a day-of-week string ('TU') to indicate the position of the day within the month. E.g. +1TU would be the first tuesday of the month.
186
+ #- *Yearly*: A value of 1 to 366 indicating the day of the year. May be negative to indicate counting down from the last day of the year.
187
+ #
188
+ #Optionally, you may specific a second hash pair to set the interval the event repeats:
189
+ # "interval" => '2'
190
+ #If the interval is missing, it is assumed to be 1.
191
+ #
192
+ #===Examples
193
+ #Repeat event every Tuesday:
194
+ # frequency = {"Weekly" => ["TU"]}
195
+ #
196
+ #Repeat every first and third Monday of the month
197
+ # frequency = {"Monthly" => ["+1MO", "+3MO"]}
198
+ #
199
+ #Repeat on the last day of every year
200
+ # frequency = {"Yearly" => [366]}
201
+ #
202
+ #Repeat every other week on Friday
203
+ # frequency = {"Weekly" => ["FR"], "interval" => "2"}
204
+
205
+ def frequency=(f)
206
+ if f.is_a?(Hash)
207
+ @frequency = f
208
+ else
209
+ raise RecurrenceValueError, "Frequency must be a hash (see documentation)"
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,100 @@
1
+ require 'gcal4ruby/base'
2
+ require 'gcal4ruby/calendar'
3
+
4
+ module GCal4Ruby
5
+
6
+ #The service class is the main handler for all direct interactions with the
7
+ #Google Calendar API. A service represents a single user account. Each user
8
+ #account can have multiple calendars, so you'll need to find the calendar you
9
+ #want from the service, using the Calendar#find class method.
10
+ #=Usage
11
+ #
12
+ #1. Authenticate
13
+ # service = Service.new
14
+ # service.authenticate("user@gmail.com", "password")
15
+ #
16
+ #2. Get Calendar List
17
+ # calendars = service.calendars
18
+ #
19
+
20
+ class Service < Base
21
+ AUTH_TYPE_OAUTH = 'OAuth'
22
+
23
+ attr_accessor :account, :auth_token, :check_public, :auth_type
24
+
25
+ def initialize(attributes = {})
26
+ super()
27
+ attributes.each do |key, value|
28
+ self.send("#{key}=", value)
29
+ end
30
+ @check_public ||= true
31
+ end
32
+
33
+ def authenticate(username, password)
34
+ ret = nil
35
+ ret = send_post(AUTH_URL, "Email=#{username}&Passwd=#{password}&source=GCal4Ruby&service=cl&accountType=HOSTED_OR_GOOGLE")
36
+ if ret.class == Net::HTTPOK
37
+ @auth_token = ret.read_body.to_a[2].gsub("Auth=", "").strip
38
+ @account = username
39
+ @auth_type = 'ClientLogin'
40
+ return true
41
+ else
42
+ raise AuthenticationFailed
43
+ end
44
+ end
45
+
46
+ # added authsub authentication. pass in the upgraded authsub token and the username/email address
47
+ def authsub_authenticate(authsub_token, account)
48
+ @auth_token = authsub_token
49
+ @account = account
50
+ @auth_type = 'AuthSub'
51
+ return true
52
+ end
53
+
54
+ def oauth_authenticate(access_token)
55
+ @auth_token = access_token
56
+ @auth_type = AUTH_TYPE_OAUTH
57
+ end
58
+
59
+ def oauth?
60
+ @auth_type == AUTH_TYPE_OAUTH
61
+ end
62
+
63
+ #Returns an array of Calendar objects for each calendar associated with
64
+ #the authenticated account.
65
+ def calendars
66
+ unless @auth_token
67
+ raise NotAuthenticated
68
+ end
69
+ ret = send_get(CALENDAR_LIST_FEED+"?max-results=100")
70
+ cals = []
71
+ REXML::Document.new(ret.body).root.elements.each("entry"){}.map do |entry|
72
+ entry.attributes["xmlns:gCal"] = "http://schemas.google.com/gCal/2005"
73
+ entry.attributes["xmlns:gd"] = "http://schemas.google.com/g/2005"
74
+ entry.attributes["xmlns:app"] = "http://www.w3.org/2007/app"
75
+ entry.attributes["xmlns"] = "http://www.w3.org/2005/Atom"
76
+ cal = Calendar.new(self)
77
+ cal.load("<?xml version='1.0' encoding='UTF-8'?>#{entry.to_s}")
78
+ cals << cal
79
+ end
80
+ return cals
81
+ end
82
+
83
+ # This is for building a composite calendar
84
+ # I'm sure it doesn't work, needs review!
85
+ def to_iframe(cals, params = {})
86
+ calendar_set = case cals
87
+ when :all then calendars
88
+ when :first then calendars[0]
89
+ else cals
90
+ end
91
+
92
+ units = calendar_set.collect do |cal|
93
+ "src=#{cal.id}" + cal.build_options_set(params).join("&amp;")
94
+ end
95
+
96
+ "<iframe src='http://www.google.com/calendar/embed?#{units.join("&amp;")}' width='#{params[:width]}' height='#{params[:height]}' frameborder='#{params[:border]}' scrolling='no'></iframe>"
97
+ end
98
+ end
99
+
100
+ end
data/test/unit.rb ADDED
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'gcal4ruby'
5
+ include GCal4Ruby
6
+
7
+ @service = Service.new
8
+ @username = nil
9
+ @password = nil
10
+
11
+ def tester
12
+ if ARGV.include?("-d")
13
+ @service.debug = true
14
+ end
15
+ ARGV.each do |ar|
16
+ if ar.match("username=")
17
+ @username = ar.gsub("username=", "")
18
+ end
19
+ if ar.match("password=")
20
+ @password = ar.gsub("password=", "")
21
+ end
22
+ end
23
+ service_test
24
+ calendar_test
25
+ event_test
26
+ event_recurrence_test
27
+ end
28
+
29
+ def service_test
30
+ puts "---Starting Service Test---"
31
+ puts "1. Authenticate"
32
+ if @service.authenticate(@username, @password)
33
+ successful
34
+ else
35
+ failed
36
+ end
37
+
38
+ puts "2. Calendar List"
39
+ cals = @service.calendars
40
+ if cals
41
+ successful "Calendars for this Account:"
42
+ cals.each do |cal|
43
+ puts cal.title
44
+ end
45
+ else
46
+ failed
47
+ end
48
+ end
49
+
50
+ def calendar_test
51
+ puts "---Starting Calendar Test---"
52
+
53
+ puts "1. Create Calendar"
54
+ cal = Calendar.new(@service)
55
+ cal.title = "test calendar"+Time.now.to_s
56
+ puts "Calender exists = "+cal.exists?.to_s
57
+ if cal.save
58
+ successful cal.to_xml
59
+ else
60
+ failed
61
+ end
62
+
63
+ puts "2. Edit Calendar"
64
+ cal.title = "renamed title"
65
+ if cal.save
66
+ successful cal.to_xml
67
+ else
68
+ puts "Test 2 Failed"
69
+ end
70
+
71
+ puts "3. Find Calendar by ID"
72
+ c = Calendar.find(@service, cal.id)
73
+ if c.title == cal.title
74
+ successful
75
+ else
76
+ failed "#{c.title} not equal to #{cal.title}"
77
+ end
78
+
79
+ puts "4. Delete Calendar"
80
+ if cal.delete and not cal.title
81
+ successful
82
+ else
83
+ failed
84
+ end
85
+ end
86
+
87
+ def event_test
88
+ puts "---Starting Event Test---"
89
+
90
+ puts "1. Create Event"
91
+ event = Event.new(@service.calendars[0])
92
+ event.title = "Test Event"
93
+ event.content = "Test event content"
94
+ event.start = Time.now+1800
95
+ event.end = Time.now+5400
96
+ if event.save
97
+ successful event.to_xml
98
+ else
99
+ failed
100
+ end
101
+
102
+ puts "2. Edit Event"
103
+ event.title = "Edited title"
104
+ if event.save
105
+ successful event.to_xml
106
+ else
107
+ failed
108
+ end
109
+
110
+ puts "3. Reload Event"
111
+ if event.reload
112
+ successful
113
+ end
114
+
115
+ puts "4. Find Event by id"
116
+ e = Event.find(@service.calendars[0], event.id)
117
+ if e.title == event.title
118
+ successful
119
+ else
120
+ failed "Found event doesn't match existing event"
121
+ end
122
+
123
+ puts "5. Delete Event"
124
+ if event.delete
125
+ successful
126
+ else
127
+ failed
128
+ end
129
+ end
130
+
131
+ def event_recurrence_test
132
+ puts "---Starting Event Recurrence Test---"
133
+
134
+ @first_start = Time.now
135
+ @first_end = Time.now+3600
136
+ @first_freq = {'weekly' => ['TU']}
137
+ @second_start = Time.now+86000
138
+ @second_end = Time.now+89600
139
+ @second_freq = {'weekly' => ['SA']}
140
+
141
+ puts "1. Create Recurring Event"
142
+ event = Event.new(@service.calendars[0])
143
+ event.title = "Test Recurring Event"
144
+ event.content = "Test event content"
145
+ event.recurrence = Recurrence.new({:start => @first_start, :end => @first_end, :frequency => @first_freq})
146
+ if event.save
147
+ successful event.to_xml
148
+ else
149
+ failed("recurrence = "+event.recurrence.to_s)
150
+ end
151
+
152
+ puts "2. Edit Recurrence"
153
+ event.title = "Edited recurring title"
154
+ event.recurrence = Recurrence.new({:start => @second_start, :end => @second_end, :frequency => @second_freq})
155
+ if event.save
156
+ successful event.to_xml
157
+ else
158
+ failed
159
+ end
160
+
161
+ puts "3. Delete Event"
162
+ if event.delete
163
+ successful
164
+ else
165
+ failed
166
+ end
167
+ end
168
+
169
+ def failed(m = nil)
170
+ puts "Test Failed"
171
+ puts m if m
172
+ exit()
173
+ end
174
+
175
+ def successful(m = nil)
176
+ puts "Test Successful"
177
+ puts m if m
178
+ end
179
+
180
+ tester