clio-gcal4ruby 0.3.2

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,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