amr_google_calendar 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Gems required to use google_calendar.
4
+ gem "nokogiri", ">= 1.4.4"
5
+ gem "addressable", ">= 2.2.2"
6
+
7
+ # Gem development dependencies.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.1"
12
+ gem "rcov", ">= 0"
13
+ gem "mocha", ">= 0"
14
+ end
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.2.2)
5
+ git (1.2.5)
6
+ jeweler (1.5.1)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ mocha (0.9.10)
11
+ rake
12
+ nokogiri (1.4.4)
13
+ rake (0.8.7)
14
+ rcov (0.9.9)
15
+ shoulda (2.11.3)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ addressable (>= 2.2.2)
22
+ bundler (~> 1.0.0)
23
+ jeweler (~> 1.5.1)
24
+ mocha
25
+ nokogiri (>= 1.4.4)
26
+ rcov
27
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Steve Zich
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ = Google Calendar
2
+
3
+ A fast lightweight and minimalist wrapper around the google calendar api.
4
+
5
+ == Install
6
+ [sudo] gem install 'google_calendar'
7
+
8
+ Note: Google requests that you set the name of your application so they can better monitor the use of their services.
9
+
10
+ == Usage
11
+ require 'rubygems'
12
+ require 'google_calendar'
13
+
14
+ cal = Google::Calendar.new(:username => 'some.person@gmail.com',
15
+ :password => 'super-secret',
16
+ :app_name => 'mycompany.com-googlecalendar-integration')
17
+
18
+ event = cal.create_event do |e|
19
+ e.title = 'A Cool Event'
20
+ e.start_time = Time.now
21
+ e.end_time = Time.now + (60 * 60) # seconds * min
22
+ end
23
+
24
+ puts event
25
+
26
+ event = cal.find_or_create_event_by_id(event.id) do |e|
27
+ e.title = 'An Updated Cool Event'
28
+ e.end_time = Time.now + (60 * 60 * 2) # seconds * min * hours
29
+ end
30
+
31
+ puts event
32
+
33
+ # All events
34
+ puts cal.events
35
+
36
+ # Query events
37
+ puts cal.find_events('my search string')
38
+
39
+
40
+ Note: This is not a complete implementation of the calendar api, it just includes the features I needed to support our internal calendar integration.
41
+
42
+ == Contributing to google_calendar
43
+
44
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
45
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
46
+ * Fork the project
47
+ * Start a feature/bugfix branch
48
+ * Commit and push until you are happy with your contribution
49
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
50
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
51
+
52
+ == Copyright
53
+
54
+ Copyright (c) 2010 Steve Zich. See LICENSE.txt for further details.
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "google_calendar"
16
+ gem.homepage = "http://github.com/northworld/google_calendar"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A lightweight google calendar API wrapper}
19
+ gem.description = %Q{A minimal wrapper around the google calendar API, which uses nokogiri for fast parsing.}
20
+ gem.email = "steve.zich@gmail.com"
21
+ gem.authors = ["Steve Zich"]
22
+ end
23
+ Jeweler::RubygemsDotOrgTasks.new
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/test_*.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ require 'rcov/rcovtask'
33
+ Rcov::RcovTask.new do |test|
34
+ test.libs << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ task :default => :test
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "google_calendar #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
@@ -0,0 +1,84 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{amr_google_calendar}
8
+ s.version = "0.2.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Amr Al-Hindi"]
12
+ s.date = %q{2012-11-18}
13
+ s.description = %q{A fork of the Google Calendar gem by Steve Zich. The gem is a minimal wrapper around the google calendar API, which uses nokogiri for fast parsing.}
14
+ s.email = %q{amr.alhindi@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "google_calendar.gemspec",
28
+ "lib/google/calendar.rb",
29
+ "lib/google/connection.rb",
30
+ "lib/google/errors.rb",
31
+ "lib/google/event.rb",
32
+ "lib/google/net/https.rb",
33
+ "lib/google_calendar.rb",
34
+ "test/helper.rb",
35
+ "test/mocks/create_event.xml",
36
+ "test/mocks/create_quickadd_event.xml",
37
+ "test/mocks/events.xml",
38
+ "test/mocks/find_event_by_id.xml",
39
+ "test/mocks/list_calendars.xml",
40
+ "test/mocks/query_events.xml",
41
+ "test/mocks/successful_login.txt",
42
+ "test/test_google_calendar.rb"
43
+ ]
44
+ s.homepage = %q{https://github.com/shami/google_calendar}
45
+ s.licenses = ["MIT"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.6.2}
48
+ s.summary = %q{A lightweight google calendar API wrapper}
49
+ s.test_files = [
50
+ "test/helper.rb",
51
+ "test/test_google_calendar.rb"
52
+ ]
53
+
54
+ if s.respond_to? :specification_version then
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.4"])
59
+ s.add_runtime_dependency(%q<addressable>, [">= 2.2.2"])
60
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
61
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
63
+ s.add_development_dependency(%q<rcov>, [">= 0"])
64
+ s.add_development_dependency(%q<mocha>, [">= 0"])
65
+ else
66
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
67
+ s.add_dependency(%q<addressable>, [">= 2.2.2"])
68
+ s.add_dependency(%q<shoulda>, [">= 0"])
69
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
71
+ s.add_dependency(%q<rcov>, [">= 0"])
72
+ s.add_dependency(%q<mocha>, [">= 0"])
73
+ end
74
+ else
75
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
76
+ s.add_dependency(%q<addressable>, [">= 2.2.2"])
77
+ s.add_dependency(%q<shoulda>, [">= 0"])
78
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
80
+ s.add_dependency(%q<rcov>, [">= 0"])
81
+ s.add_dependency(%q<mocha>, [">= 0"])
82
+ end
83
+ end
84
+
@@ -0,0 +1,223 @@
1
+ require 'nokogiri'
2
+
3
+ module Google
4
+
5
+ # Calendar is the main object you use to interact with events.
6
+ # use it to find, create, update and delete them.
7
+ #
8
+ class Calendar
9
+
10
+ # Setup and connect to the specified google calendar.
11
+ # the +params+ paramater accepts
12
+ # * :username => the username of the specified calendar (i.e. some.guy@gmail.com)
13
+ # * :password => the password for the specified user (i.e. super-secret)
14
+ # * :calendar => the name of the calendar you would like to work with (optional, defaults to the calendar the user setup as their default one.)
15
+ # * :app_name => the name of your application (defaults to 'northworld.com-googlecalendar-integration')
16
+ # * :auth_url => the base url that is used to connect to google (defaults to 'https://www.google.com/accounts/ClientLogin')
17
+ #
18
+ # After creating an instace you are immediatly logged on and ready to go.
19
+ #
20
+ # ==== Examples
21
+ # # Use the default calendar
22
+ # Calendar.new(:username => 'some.guy@gmail.com', :password => 'ilovepie!')
23
+ #
24
+ # # Specify the calendar
25
+ # Calendar.new(:username => 'some.guy@gmail.com', :password => 'ilovepie!', :calendar => 'my.company@gmail.com')
26
+ #
27
+ # # Specify the app_name
28
+ # Calendar.new(:username => 'some.guy@gmail.com', :password => 'ilovepie!', :app_name => 'mycompany.com-googlecalendar-integration')
29
+ #
30
+
31
+ # Calendar attributes
32
+ attr_accessor :username, :password, :app_name, :auth_url, :connection, :calendar
33
+
34
+ def initialize(params)
35
+ self.username = params[:username]
36
+ self.password = params[:password]
37
+ self.calendar = params[:calendar]
38
+ self.app_name = params[:app_name]
39
+ self.auth_url = params[:auth_url]
40
+
41
+ self.connection = Connection.new(:username => username,
42
+ :password => password,
43
+ :app_name => app_name,
44
+ :auth_url => auth_url)
45
+ end
46
+
47
+ # Find all of the events associated with this calendar.
48
+ # Returns:
49
+ # nil if nothing found.
50
+ # a single event if only one found.
51
+ # an array of events if many found.
52
+ #
53
+ def events
54
+ event_lookup()
55
+ end
56
+
57
+ # This is equivalnt to running a search in
58
+ # the google calendar web application. Google does not provide a way to easily specify
59
+ # what attributes you would like to search (i.e. title), by default it searches everything.
60
+ # If you would like to find specific attribute value (i.e. title=Picnic), run a query
61
+ # and parse the results.
62
+ # Returns:
63
+ # nil if nothing found.
64
+ # a single event if only one found.
65
+ # an array of events if many found.
66
+ #
67
+ def find_events(query)
68
+ event_lookup("?q=#{query}")
69
+ end
70
+
71
+ # Find all of the events associated with this calendar that start in the given time frame.
72
+ # The lower bound is inclusive, whereas the upper bound is exclusive.
73
+ # Events that overlap the range are included.
74
+ # Returns:
75
+ # nil if nothing found.
76
+ # a single event if only one found.
77
+ # an array of events if many found.
78
+ #
79
+ def find_events_in_range(start_min, start_max,query = nil)
80
+ formatted_start_min = start_min.strftime("%Y-%m-%dT%H:%M:%S")
81
+ formatted_start_max = start_max.strftime("%Y-%m-%dT%H:%M:%S")
82
+ if query
83
+ event_lookup("?q=#{query}&start-min=#{formatted_start_min}&start-max=#{formatted_start_max}&recurrence-expansion-start=#{formatted_start_min}&recurrence-expansion-end=#{formatted_start_max}")
84
+ else
85
+ event_lookup("?start-min=#{formatted_start_min}&start-max=#{formatted_start_max}&recurrence-expansion-start=#{formatted_start_min}&recurrence-expansion-end=#{formatted_start_max}")
86
+ end
87
+ end
88
+
89
+ # Attempts to find the event specified by the id
90
+ # Returns:
91
+ # nil if nothing found.
92
+ # a single event if only one found.
93
+ # an array of events if many found.
94
+ #
95
+ def find_event_by_id(id)
96
+ return nil unless id && id.strip != ''
97
+ event_lookup("/#{id}")
98
+ end
99
+
100
+ # Creates a new event and immediatly saves it.
101
+ # returns the event
102
+ #
103
+ # ==== Examples
104
+ # # Use a block
105
+ # cal.create_event do |e|
106
+ # e.title = "A New Event"
107
+ # e.where = "Room 101"
108
+ # end
109
+ #
110
+ # # Don't use a block (need to call save maunally)
111
+ # event = cal.create_event
112
+ # event.title = "A New Event"
113
+ # event.where = "Room 101"
114
+ # event.save
115
+ #
116
+ def create_event(&blk)
117
+ setup_event(Event.new, &blk)
118
+ end
119
+
120
+ # looks for the spedified event id.
121
+ # If it is found it, updates it's vales and returns it.
122
+ # If the event is no longer on the server it creates a new one with the specified values.
123
+ # Works like the create_event method.
124
+ #
125
+ def find_or_create_event_by_id(id, &blk)
126
+ setup_event(find_event_by_id(id) || Event.new, &blk)
127
+ end
128
+
129
+ # Saves the specified event.
130
+ # This is a callback used by the Event class.
131
+ #
132
+ def save_event(event)
133
+ method = (event.id == nil || event.id == '') ? :post : :put
134
+ query_string = (method == :put) ? "/#{event.id}" : ''
135
+ @connection.send(Addressable::URI.parse(events_url + query_string), method, event.to_xml)
136
+ end
137
+
138
+ # Deletes the specified event.
139
+ # This is a callback used by the Event class.
140
+ #
141
+ def delete_event(event)
142
+ @connection.send(Addressable::URI.parse(events_url + "/#{event.id}"), :delete)
143
+ end
144
+
145
+ # Explicitly reload the connection to google calendar
146
+ #
147
+ # Examples
148
+ # class User
149
+ # def calendar
150
+ # @calendar ||= Google::Calendar.new :username => "foo@gmail.com", :password => "bar"
151
+ # end
152
+ # end
153
+ # user = User.new
154
+ # 2.times { user.calendar } #only one HTTP authentication request to google
155
+ # user.calendar.reload #new HTTP authentication request to google
156
+ #
157
+ # Returns Google::Calendar instance
158
+ def reload
159
+ self.connection = Connection.new(:username => username,
160
+ :password => password,
161
+ :app_name => app_name,
162
+ :auth_url => auth_url)
163
+ self
164
+ end
165
+
166
+ def display_color
167
+ calendar_data.xpath("//entry[title='#{@calendar}']/color/@value").first.value
168
+ end
169
+
170
+ protected
171
+
172
+ def event_lookup(query_string = '') #:nodoc:
173
+ begin
174
+ response = @connection.send(Addressable::URI.parse(events_url + query_string), :get)
175
+ events = Event.build_from_google_feed(response.body, self)
176
+ events.length > 1 ? events : events[0]
177
+ rescue Google::HTTPNotFound
178
+ return nil
179
+ end
180
+ end
181
+
182
+ def calendar_id #:nodoc:
183
+ @calendar || "default"
184
+ end
185
+
186
+ # Initialize the events URL given String attribute @calendar value :
187
+ #
188
+ # contains a '@' : construct the feed url with @calendar.
189
+ # does not contain '@' : fetch user's all calendars (http://code.google.com/apis/calendar/data/2.0/developers_guide_protocol.html#RetrievingAllCalendars)
190
+ # and return feed url matching @calendar.
191
+ # nil : default feed url.
192
+ #
193
+ # Returns:
194
+ # a String url for a calendar feeds.
195
+ # raise a Google::InvalidCalendar error if @calendar is invalid.
196
+ #
197
+ def events_url
198
+ if @calendar and !@calendar.include?("@")
199
+ link = calendar_data.xpath("//entry[title='#{@calendar}']/link[contains(@rel, '#eventFeed')]/@href").to_s
200
+ link.empty? ? raise(Google::InvalidCalendar) : link
201
+ else
202
+ "https://www.google.com/calendar/feeds/#{calendar_id}/private/full"
203
+ end
204
+ end
205
+
206
+ def calendar_data
207
+ unless @calendar_data
208
+ xml = @connection.send(Addressable::URI.parse("https://www.google.com/calendar/feeds/default/allcalendars/full"), :get)
209
+ @calendar_data = Nokogiri::XML(xml.body)
210
+ @calendar_data.remove_namespaces!
211
+ end
212
+ @calendar_data
213
+ end
214
+
215
+ def setup_event(event) #:nodoc:
216
+ event.calendar = self
217
+ yield(event) if block_given?
218
+ event.save
219
+ event
220
+ end
221
+ end
222
+
223
+ end