gcal-unit 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rake"
4
+ gem "httparty", "~>0.8.0"
5
+
6
+ group :test do
7
+ gem "rspec", "~> 2.10.0"
8
+ gem "vcr"
9
+ gem "fakeweb"
10
+ gem "dotenv"
11
+ end
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ G-G-G-GCal-Unit!
2
+ ==========================
3
+ ***
4
+
5
+ ![GCal Unit](https://s3.amazonaws.com/github-gcal-unit/gcal-unit.png)
6
+
7
+ ## GANGSTA SH*T
8
+
9
+ GCal-Unit is a bullet proof vest for the Google Calendar API. It will pimp the hell out of your applications. This rapper is used only for Google Calendar API calls and not any other Google APIs (except for making those Google tokens fresh).
10
+
11
+ ## LOADING THE CLIP
12
+
13
+ To start caping fools, you have to install this sh*t.
14
+
15
+ ```
16
+ gem install gcal-unit
17
+ ```
18
+
19
+ This will also load [httparty](http://github.com/jnunemaker/httparty) in your pimp cup.
20
+
21
+ ## CANDY SHOP
22
+
23
+ I'll break it down for you, baby it's simple.
24
+
25
+ ```ruby
26
+ #token is the user's Google API access token, yo
27
+
28
+ # Get a list of a your Google calendars
29
+ list = Google::CalendarList.new(token).list
30
+
31
+ # Get the user's primary Google calendar
32
+ primary = Google::Calendar.new(token).get("primary")
33
+
34
+ # Get a list of events from a calendar
35
+ events = Google::Event.new(token).nested_for('primary').list
36
+
37
+ # Get a single event from a calendar
38
+ event = Google::Event.new(token).nested_for('primary').get(event_id)
39
+
40
+ # Insert an event for a calendar
41
+ event = Google::Event.new(token).nested_for('primary').insert({
42
+ end: { date: { '1975-07-06' } },
43
+ start: { date: { '1975-07-06' } },
44
+ description: { string: { 'I was born.' } }
45
+ })
46
+
47
+ # Update an event for a calendar
48
+ event = Google::Event.new(token).nested_for('primary').update(event_id, {
49
+ end: { date: { 'I was born with a pimp cup.' } }
50
+ })
51
+
52
+ # Delete an event
53
+ Google::Event.new(token).nested_for('primary').delete(event_id)
54
+
55
+ # Use the quick add feature
56
+ event = Google::Event.new(token).nested_for('primary').quick_add('got shot 9 times yesterday at 2am')
57
+ ```
58
+
59
+ The base URI for all of the API operations is: [http://www.googleapis.com/calendar/v3](http://www.googleapis.com/calendar/v3)
60
+
61
+ If you have an interest in reading other things than c-notes, you can find the Google API Calendar documentation at [http://developers.google.com/google-apps/calendar/v3/reference](http://developers.google.com/google-apps/calendar/v3/reference)
62
+
63
+ Each Google Calendar API model requires an API token. This token (as well as a refresh token) can be retrieved by using OAuth (we recommend [OmniAuth](https://github.com/intridea/omniauth)). If the token expires, you can use the authentication method in this library and your refresh token to refresh the API token for future calls.
64
+
65
+ ```ruby
66
+ # refresh_token is your Google API refresh token for your registered application
67
+
68
+ client = Google::Authorization.new(refresh_token)
69
+ token = client.refresh()['access_token'] # use this token for all the api calls
70
+ ```
71
+
72
+ ## WANKSTA
73
+
74
+ Configure the Google API wrapper with the sensitive data needed to make Google API calls.
75
+
76
+ ```ruby
77
+ Google.configure do |config|
78
+ config.api_key = ENV['GOOGLE_API_KEY']
79
+ config.client_id = ENV['GOOGLE_CLIENT_ID']
80
+ config.client_secret = ENV['GOOGLE_CLIENT_SECRET']
81
+ # You can also set the refresh token here as well for later retreival but
82
+ # refresh tokens are not app specific but user specific, yo
83
+ end
84
+ ```
85
+
86
+ ## KNOW YOUR HOOD
87
+
88
+ When contributing use [dotenv](https://github.com/bkeepers/dotenv) to hold your sensitive data. The gem contains an example `.env` file. The `.env` file is in the .gitignore so your sensitive information will not be exposed.
89
+
90
+ ## IN DA CLUB
91
+
92
+ To run the tests, there is some configuration that probably needs to occur.
93
+ The test suite uses VCR to make the initial calls to Google and then records the response.
94
+ In order to make that initial call to Google work, we included a simple Sinatra application that you can
95
+ run to record the correct refresh token for authentication.
96
+
97
+ Here are the steps to make yo sh*t tight.
98
+
99
+ 1. visit https://developers.google.com/google-apps/calendar/
100
+ 1. sign in (upper right) with a Google account you want to use for testing
101
+ 1. visit https://code.google.com/apis/console
102
+ 1. create a project
103
+ 1. turn on calendar api
104
+ 1. visit api access
105
+ 1. create an oauth 2.0 client id
106
+ 1. choose web application
107
+ 1. choose http://localhost for hostname
108
+ 1. click on create client id
109
+ 1. edit settings and change redirect uri to http://localhost:3000/auth/google_oauth2/callback
110
+ 1. cp example.env .env
111
+ 1. change the GOOGLE_CLIENT_ID key to your client id in the .env file
112
+ 1. change the GOOGLE_CLIENT_SECRET key to your client secret in the .env file
113
+ 1. change the GOOGLE_API_KEY key to your API key in the .env file
114
+ 1. change the GOOGLE_TEST_USER key to who you are going to sign in as below when you run the Sinatra app
115
+ 1. bundle install --gemfile=bootstrapper/Gemfile
116
+ 1. rackup bootstrapper/config.ru -p 3000
117
+ 1. open your browser to http://localhost:3000 and authenticate with Google
118
+ 1. the GOOGLE_REFRESH_TOKEN will be added to your .env file
119
+ 1. add the user email that you signed in with to the .env file for the GOOGLE_TEST_USER key
120
+
121
+ (Welcome to OAuth. So many steps, my grill is tarnishing)
122
+
123
+ ## GET RICH OR DIE TRYIN'
124
+
125
+ To contribute, just take a hit and do the rest.
126
+
127
+ * Fork the project
128
+ * Create a test that indicates your behavior
129
+ * If you are using an http call, wrap your call with VCR
130
+ * Code your feature or fix the bug
131
+ * Commit (do not bump the version)
132
+ * Send pull request
133
+
134
+ Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
data/Rakefile ADDED
@@ -0,0 +1,131 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def dir_name
16
+ name.gsub('-', '_')
17
+ end
18
+
19
+ def version
20
+ line = File.read("lib/#{dir_name}.rb")[/^\s*VERSION\s*=\s*.*/]
21
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
22
+ end
23
+
24
+ def date
25
+ Date.today.to_s
26
+ end
27
+
28
+ def rubyforge_project
29
+ name
30
+ end
31
+
32
+ def gemspec_file
33
+ "#{name}.gemspec"
34
+ end
35
+
36
+ def gem_file
37
+ "#{name}-#{version}.gem"
38
+ end
39
+
40
+ def replace_header(head, header_name)
41
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
42
+ end
43
+
44
+ #############################################################################
45
+ #
46
+ # Standard tasks
47
+ #
48
+ #############################################################################
49
+
50
+ task :default => :validate
51
+
52
+ desc "Open an irb session preloaded with this library"
53
+ task :console do
54
+ sh "irb -rubygems -r ./lib/#{dir_name}.rb"
55
+ end
56
+
57
+ #############################################################################
58
+ #
59
+ # Custom tasks (add your own tasks here)
60
+ #
61
+ #############################################################################
62
+
63
+
64
+
65
+ #############################################################################
66
+ #
67
+ # Packaging tasks
68
+ #
69
+ #############################################################################
70
+
71
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
72
+ task :release => :build do
73
+ unless `git branch` =~ /^\* master$/
74
+ puts "You must be on the master branch to release!"
75
+ exit!
76
+ end
77
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
78
+ sh "git tag v#{version}"
79
+ sh "git push bf master"
80
+ sh "git push bf v#{version}"
81
+ sh "gem push pkg/#{name}-#{version}.gem"
82
+ end
83
+
84
+ desc "Build #{gem_file} into the pkg directory"
85
+ task :build => :gemspec do
86
+ sh "mkdir -p pkg"
87
+ sh "gem build #{gemspec_file}"
88
+ sh "mv #{gem_file} pkg"
89
+ end
90
+
91
+ desc "Generate #{gemspec_file}"
92
+ task :gemspec => :validate do
93
+ # read spec file and split out manifest section
94
+ spec = File.read(gemspec_file)
95
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
96
+
97
+ # replace name version and date
98
+ replace_header(head, :name)
99
+ replace_header(head, :version)
100
+ replace_header(head, :date)
101
+ #comment this out if your rubyforge_project has a different name
102
+ replace_header(head, :rubyforge_project)
103
+
104
+ # determine file list from git ls-files
105
+ files = `git ls-files`.
106
+ split("\n").
107
+ sort.
108
+ reject { |file| file =~ /^\./ }.
109
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
110
+ map { |file| " #{file}" }.
111
+ join("\n")
112
+
113
+ # piece file back together and write
114
+ manifest = " s.files = %w[\n#{files}\n ]\n"
115
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
116
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
117
+ puts "Updated #{gemspec_file}"
118
+ end
119
+
120
+ desc "Validate #{gemspec_file}"
121
+ task :validate do
122
+ libfiles = Dir['lib/*'] - ["lib/#{dir_name}.rb", "lib/#{dir_name}"]
123
+ unless libfiles.empty?
124
+ puts "Directory `lib` should only contain a `#{dir_name}.rb` file and `#{dir_name}` dir."
125
+ exit!
126
+ end
127
+ unless Dir['VERSION*'].empty?
128
+ puts "A `VERSION` file at root level violates Gem best practices."
129
+ exit!
130
+ end
131
+ end
data/gcal-unit.gemspec ADDED
@@ -0,0 +1,57 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.specification_version = 2 if s.respond_to? :specification_version=
4
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
5
+ s.rubygems_version = '1.3.5'
6
+
7
+ s.name = 'gcal-unit'
8
+ s.version = '1.0.0'
9
+ s.date = '2012-08-30'
10
+ s.rubyforge_project = 'gcal-unit'
11
+
12
+ s.summary = "Pimp ass API wrapper for the Google Calendar API."
13
+ s.description = "Wraps HTTP calls for the Google Calendar API."
14
+
15
+ s.authors = ["Brilliant Fantastic"]
16
+ s.email = 'support@brilliantfantastic.com'
17
+ s.homepage = 'http://github.com/brilliantfantastic/gcal-unit'
18
+
19
+ s.require_paths = %w[lib]
20
+ s.executables = []
21
+
22
+ s.rdoc_options = ["--charset=UTF-8"]
23
+ s.extra_rdoc_files = %w[README.md]
24
+
25
+ s.add_dependency('httparty', "~> 0.7.8")
26
+
27
+ ## Leave this section as-is. It will be automatically generated from the
28
+ ## contents of your Git repository via the gemspec task. DO NOT REMOVE
29
+ ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
30
+ # = MANIFEST =
31
+ s.files = %w[
32
+ Gemfile
33
+ README.md
34
+ Rakefile
35
+ bootstrapper/Gemfile
36
+ bootstrapper/config.ru
37
+ example.env
38
+ gcal-unit.gemspec
39
+ lib/gcal_unit.rb
40
+ lib/gcal_unit/authorization.rb
41
+ lib/gcal_unit/calendar.rb
42
+ lib/gcal_unit/calendar_list.rb
43
+ lib/gcal_unit/client.rb
44
+ lib/gcal_unit/configuration.rb
45
+ lib/gcal_unit/errors.rb
46
+ lib/gcal_unit/event.rb
47
+ spec/lib/gcal_unit/authorization_spec.rb
48
+ spec/lib/gcal_unit/calendar_list_spec.rb
49
+ spec/lib/gcal_unit/calendar_spec.rb
50
+ spec/lib/gcal_unit/configuration_spec.rb
51
+ spec/lib/gcal_unit/event_spec.rb
52
+ spec/spec_helper.rb
53
+ ]
54
+ # = MANIFEST =
55
+
56
+ s.files.reject! { |f| f.include?("bootstrapper") || f.include?("example.env") }
57
+ end
@@ -0,0 +1,25 @@
1
+ module Google
2
+ # Public: API methods to authorize a specific user for
3
+ # the API calls.
4
+ class Authorization < Client
5
+ base_uri 'https://accounts.google.com'
6
+
7
+ # Public: Returns a new access token for a specific refresh token.
8
+ #
9
+ # Returns a hash of metadata for refreshed user access.
10
+ def refresh
11
+ options = {
12
+ :body => {
13
+ :client_id => Google.configuration.client_id,
14
+ :client_secret => Google.configuration.client_secret,
15
+ :refresh_token => @token,
16
+ :grant_type => 'refresh_token'
17
+ },
18
+ :headers => {
19
+ 'Content-Type' => 'application/x-www-form-urlencoded'
20
+ }
21
+ }
22
+ http_post "/o/oauth2/token", options
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module Google
2
+ # Public: API methods for a singular calendar.
3
+ class Calendar < Client
4
+ # Public: Gets a single calendar.
5
+ #
6
+ # id - The id of the calendar to retrieve.
7
+ #
8
+ # Returns a hash of metadata for a calendar.
9
+ def get(id)
10
+ response = http_get "/calendars/#{id}"
11
+ JSON.parse(response.body)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Google
2
+ # Public: API methods for a list of calendars.
3
+ class CalendarList < Client
4
+ # Public: Gets a list of calendars for the user specified
5
+ # with the provided token.
6
+ #
7
+ # Returns a hash of metadata for a calendar list.
8
+ def list
9
+ response = http_get '/users/me/calendarList'
10
+ JSON.parse response.body
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,135 @@
1
+ module Google
2
+ # Public : Performs http calls to the Google APIs.
3
+ class Client
4
+ include HTTParty
5
+ base_uri 'https://www.googleapis.com/calendar/v3'
6
+
7
+ # Public: Initializes a new instance of a client.
8
+ #
9
+ # token - The user token to make the call.
10
+ def initialize(token)
11
+ @token = token
12
+ end
13
+
14
+ # Public: Performs a GET request for an http call with the correct queries
15
+ # and headers applied.
16
+ #
17
+ # path - the url for the GET request
18
+ # options - additional options for the GET request
19
+ # block - called after the GET request is complete
20
+ #
21
+ # Raises Google::AuthenticationRequiredError if the token is no longer valid
22
+ #
23
+ # Returns the response
24
+ def http_get(path, options={}, &block)
25
+ apply_api_key options # apply the API key if it is not there
26
+ apply_auth_header options # apply the authorization header if it is not there
27
+
28
+ response = self.class.get path, options, &block
29
+ raise AuthenticationRequiredError.new if response.code == 401
30
+ response
31
+ end
32
+
33
+ # Public: Performs a POST request for an http call with the correct queries
34
+ # and headers applied.
35
+ #
36
+ # path - the url for the POST request
37
+ # options - additional options for the POST request
38
+ # block - called after the POST request is complete
39
+ #
40
+ # Raises Google::AuthenticationRequiredError if the token is no longer valid
41
+ #
42
+ # Returns the response
43
+ def http_post(path, options={}, &block)
44
+ apply_api_key options # apply the API key if it is not there
45
+ apply_auth_header options # apply the authorization header if it is not there
46
+ apply_content_type_header options
47
+ apply_content_length_header options
48
+
49
+ response = self.class.post path, options, &block
50
+ raise AuthenticationRequiredError.new if response.code == 401
51
+ response
52
+ end
53
+
54
+ # Public: Performs a PUT request for an http call with the correct queries and
55
+ # headers applied.
56
+ #
57
+ # path - the url for the PUT request
58
+ # options - additional options for the PUT request
59
+ # block - called after the PUT request is complete
60
+ #
61
+ # Raises Google::AuthenticationRequiredError if the token is no longer valid
62
+ #
63
+ # Returns the response
64
+ def http_put(path, options={}, &block)
65
+ apply_api_key options # apply the API key if it is not there
66
+ apply_auth_header options # apply the authorization header if it is not there
67
+ apply_content_type_header options
68
+
69
+ response = self.class.put path, options, &block
70
+ raise AuthenticationRequiredError.new if response.code == 401
71
+ response
72
+ end
73
+
74
+ # Public: Performs a DELETE request for an http call with the correct queries and
75
+ # headers applied.
76
+ #
77
+ # path - the url for a DELETE request
78
+ # options - additional options for the DELETE request
79
+ # block - called after the DELETE request is complete
80
+ #
81
+ # Raises Google::AuthenticationRequiredError if the token is no longer valid
82
+ #
83
+ # Returns the response
84
+ def http_delete(path, options={}, &block)
85
+ apply_api_key options # apply the API key if it is not there
86
+ apply_auth_header options # apply the authorization header if it is not there
87
+
88
+ response = self.class.delete path, options, &block
89
+ raise AuthenticationRequiredError.new if response.code == 401
90
+ response
91
+ end
92
+
93
+ protected
94
+ # Protected: Adds the api key value to the query options
95
+ #
96
+ # Returns nothing
97
+ def apply_api_key(options)
98
+ options.merge! :query => {} unless options.has_key? :query
99
+ options[:query].merge!({ :key => Google.configuration.api_key })
100
+ end
101
+
102
+ # Protected: Adds the authorization token to the header
103
+ #
104
+ # Returns nothing
105
+ def apply_auth_header(options)
106
+ apply_header options, "Authorization", "Bearer #{@token}"
107
+ end
108
+
109
+ # Protected: Adds a default of application/json to the Content-Type unless
110
+ # one is already specified.
111
+ #
112
+ # Returns nothing
113
+ def apply_content_type_header(options)
114
+ if !options.has_key?(:headers) || !options[:headers].has_key?("Content-Type")
115
+ apply_header options, "Content-Type", "application/json"
116
+ end
117
+ end
118
+
119
+ # Protected: Applies the Content-Length of the body
120
+ #
121
+ # Returns nothing
122
+ def apply_content_length_header(options)
123
+ options[:body].nil? ? length = 0 : options[:body].length
124
+ apply_header options, "Content-Length", length.to_s
125
+ end
126
+
127
+ # Protected: Applies the specified key and value combination to the header
128
+ #
129
+ # Returns nothing
130
+ def apply_header(options, key, value)
131
+ options.merge! :headers => {} unless options.has_key? :headers
132
+ options[:headers].merge!({ key => value })
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,10 @@
1
+ module Google
2
+ # Public: Holds the configuration options for accessing
3
+ # a Google API.
4
+ class Configuration
5
+ attr_accessor :api_key,
6
+ :client_id,
7
+ :client_secret,
8
+ :refresh_token
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Google
2
+ # Public: Error that specifies the specified token is no longer valid.
3
+ class AuthenticationRequiredError < SecurityError; end
4
+
5
+ # Public: Error that specifies a calendar id was not provided.
6
+ class CalendarRequiredError < RuntimeError; end
7
+ end
@@ -0,0 +1,97 @@
1
+ module Google
2
+ # Public: API methods for a calendar events.
3
+ class Event < Client
4
+ # Public: Saves a calendar id for methods that require a
5
+ # calendar for the API methods.
6
+ #
7
+ # calendar_id - Identifier for a specific calendar.
8
+ #
9
+ # Returns the event object for chaining
10
+ def nested_for(calendar_id)
11
+ @calendar_id = calendar_id
12
+ self
13
+ end
14
+
15
+ # Public: Gets a list of events for a specific calendar.
16
+ #
17
+ # Raises a Google::CalendarRequiredError if a calendar is not
18
+ # supplied (nested_for) was not called.
19
+ #
20
+ # Returns a hash of metadata for the calendars events.
21
+ def list
22
+ raise Google::CalendarRequiredError if @calendar_id.nil?
23
+ response = http_get "/calendars/#{@calendar_id}/events"
24
+ JSON.parse(response.body)
25
+ end
26
+
27
+ # Public: Gets a single event.
28
+ #
29
+ # id - The id of the event to retrieve.
30
+ #
31
+ # Raises a Google::CalendarRequiredError if a calendar is not
32
+ # supplied (nested_for) was not called.
33
+ #
34
+ # Returns a hash of metadata for an event.
35
+ def get(id)
36
+ raise Google::CalendarRequiredError if @calendar_id.nil?
37
+ response = http_get "/calendars/#{@calendar_id}/events/#{id}"
38
+ JSON.parse(response.body)
39
+ end
40
+
41
+ # Public: Inserts a new single event.
42
+ #
43
+ # event - A hash containing the data for an event to add.
44
+ #
45
+ # Raises a Google::CalendarRequiredError if a calendar is not
46
+ # supplied (nested_for) was not called.
47
+ #
48
+ # Returns a hash of metadata for the event that was added.
49
+ def insert(event)
50
+ raise Google::CalendarRequiredError if @calendar_id.nil?
51
+ response = http_post "/calendars/#{@calendar_id}/events", :body => JSON.dump(event)
52
+ JSON.parse(response.body)
53
+ end
54
+
55
+ # Public: Inserts a new single event that is parsed from a line of text.
56
+ #
57
+ # text - A sentence that is parsed into a single event.
58
+ #
59
+ # Raises a Google::CalendarRequiredError if a calendar is not
60
+ # supplied (nested_for) was not called.
61
+ #
62
+ # Returns a hash of metadata for the event that was added.
63
+ def quick_add(text)
64
+ raise Google::CalendarRequiredError if @calendar_id.nil?
65
+ response = http_post "/calendars/#{@calendar_id}/events/quickAdd", :query => { :text => text }
66
+ JSON.parse(response.body)
67
+ end
68
+
69
+ # Public: Updates a single event.
70
+ #
71
+ # id - The id of the event to update.
72
+ # event - A hash containing the data for the event to update.
73
+ #
74
+ # Raises a Google::CalendarRequiredError if a calendar is not
75
+ # supplied (nested_for) was not called.
76
+ #
77
+ # Returns a hash of metadata for the event that was updated.
78
+ def update(id, event)
79
+ raise Google::CalendarRequiredError if @calendar_id.nil?
80
+ response = http_put "/calendars/#{@calendar_id}/events/#{id}", :body => JSON.dump(event)
81
+ JSON.parse(response.body)
82
+ end
83
+
84
+ # Public: Deletes a single event.
85
+ #
86
+ # id - The id of the event to delete.
87
+ #
88
+ # Raises a Google::CalendarRequiredError if a calendar is not
89
+ # supplied (nested_for) was not called.
90
+ #
91
+ # Returns the response from the delete operation.
92
+ def delete(id)
93
+ raise Google::CalendarRequiredError if @calendar_id.nil?
94
+ http_delete "/calendars/#{@calendar_id}/events/#{id}"
95
+ end
96
+ end
97
+ end
data/lib/gcal_unit.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'open-uri'
4
+
5
+ require 'gcal_unit/client'
6
+ require 'gcal_unit/calendar_list'
7
+ require 'gcal_unit/calendar'
8
+ require 'gcal_unit/event'
9
+ require 'gcal_unit/errors'
10
+ require 'gcal_unit/authorization'
11
+ require 'gcal_unit/configuration'
12
+
13
+ # Public: Contains all the methods for wrapping the Google API.
14
+ module Google
15
+ # Public: Holds the configuration for calling methods within
16
+ # the Google API.
17
+ #
18
+ # Returns a hash used for configuring the calls.
19
+ # :api_key - Identifies the application requesting the call.
20
+ # Sent with all the API calls.
21
+ # :client_id - The OAuth client id that is used to generate
22
+ # access tokens.
23
+ # :client_secret - The OAuth client secret that is used to
24
+ # generate access tokens.
25
+ # :refresh_token - A token that is used to retrieve an access
26
+ # token when the original token expires.
27
+ def self.configuration
28
+ @configuration ||= Google::Configuration.new
29
+ end
30
+
31
+ # Public: Allows configuration for the Google API.
32
+ #
33
+ # Yields the configuration for the Google API.
34
+ def self.configure
35
+ yield configuration if block_given?
36
+ end
37
+
38
+ VERSION = "1.0.0"
39
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Google::Authorization do
4
+ let(:client) { Google::Authorization.new(Google.configuration.refresh_token) }
5
+
6
+ describe ".refresh" do
7
+ use_vcr_cassette "authorization_success"
8
+ it "should return a new access token" do
9
+ client.refresh()['access_token'].should_not be_nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Google::CalendarList do
4
+ let(:client) { Google::CalendarList.new(token) }
5
+
6
+ describe ".list" do
7
+ context "without a token" do
8
+ use_vcr_cassette "authentication_needed"
9
+ it "should raise an error if authentication is needed" do
10
+ expect { Google::CalendarList.new(nil).list }.to raise_error Google::AuthenticationRequiredError
11
+ end
12
+ end
13
+
14
+ context "with a token" do
15
+ use_vcr_cassette "calendar_list_success"
16
+ it "should return a list of calendars for the authorized user" do
17
+ client.list["items"].count.should > 0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Google::Calendar do
4
+ let(:client) { Google::Calendar.new(token) }
5
+
6
+ describe ".get" do
7
+ context "without a token" do
8
+ use_vcr_cassette "authorization_success"
9
+ it "should raise an error if authentication is needed" do
10
+ expect { Google::Calendar.new(nil).get('any_string') }.to raise_error Google::AuthenticationRequiredError
11
+ end
12
+ end
13
+
14
+ context "with a token" do
15
+ use_vcr_cassette "calendar_success"
16
+ it "should return a primary google calendar" do
17
+ client.get("primary")["id"].should == ENV['GOOGLE_TEST_USER']
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Google do
4
+ after :all do
5
+ configure
6
+ end
7
+
8
+ describe ".configure" do
9
+ it "assigns a value to the api_key" do
10
+ Google.configure do |config|
11
+ config.api_key = '1234'
12
+ end
13
+ Google.configuration.api_key.should == '1234'
14
+ end
15
+
16
+ it "assigns a value to the client_id" do
17
+ Google.configure do |config|
18
+ config.client_id = '6789'
19
+ end
20
+ Google.configuration.client_id.should == '6789'
21
+ end
22
+
23
+ it "assigns a value to the client_secret" do
24
+ Google.configure do |config|
25
+ config.client_secret = '36748'
26
+ end
27
+ Google.configuration.client_secret.should == '36748'
28
+ end
29
+
30
+ it "assigns a value to the refresh_token" do
31
+ Google.configure do |config|
32
+ config.refresh_token = '1009'
33
+ end
34
+ Google.configuration.refresh_token = '1009'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ describe Google::Event do
4
+ let(:client) { Google::Event.new(token) }
5
+ let(:event) do
6
+ {
7
+ end: { date: (Date.today + 1).strftime("%Y-%m-%d") },
8
+ start: { date: Date.today.strftime("%Y-%m-%d") }
9
+ }
10
+ end
11
+
12
+ describe ".update" do
13
+ it "should raise an error if the calendar id was not provided" do
14
+ expect { client.update(nil, nil) }.to raise_error Google::CalendarRequiredError
15
+ end
16
+
17
+ context "needs an existing event" do
18
+ before :all do
19
+ VCR.use_cassette "event_insert_success" do
20
+ @event = client.nested_for('primary').insert event
21
+ end
22
+ end
23
+
24
+ after :all do
25
+ VCR.use_cassette "event_delete_success" do
26
+ client.nested_for('primary').delete @event['id']
27
+ end
28
+ end
29
+
30
+ context "has a calendar" do
31
+ use_vcr_cassette "event_update_success"
32
+
33
+ it "should change the event when the data is valid" do
34
+ @event['description'] = "This has changed"
35
+ response = client.nested_for('primary').update(@event['id'], @event)
36
+ response['description'].should == "This has changed"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe ".list" do
43
+ it "should raise an error if the calendar id was not provided" do
44
+ expect { client.list }.to raise_error Google::CalendarRequiredError
45
+ end
46
+
47
+ context "without a token" do
48
+ use_vcr_cassette "authentication_needed"
49
+ it "should raise an error if authentication is needed" do
50
+ expect { Google::Event.new('').nested_for('primary').list }.to raise_error Google::AuthenticationRequiredError
51
+ end
52
+ end
53
+
54
+ context "with a token" do
55
+ use_vcr_cassette "event_list_success"
56
+ it "should return a list of events" do
57
+ client.nested_for('primary').list['kind'].should == 'calendar#events'
58
+ end
59
+ end
60
+ end
61
+
62
+ describe ".get" do
63
+ it "should raise an error if the calendar id was not provided" do
64
+ expect { client.get(nil) }.to raise_error Google::CalendarRequiredError
65
+ end
66
+
67
+ context "needs an existing event" do
68
+ before :all do
69
+ VCR.use_cassette "event_insert_success" do
70
+ @event = client.nested_for('primary').insert event
71
+ end
72
+ end
73
+
74
+ after :all do
75
+ VCR.use_cassette "event_delete_success" do
76
+ client.nested_for('primary').delete @event['id']
77
+ end
78
+ end
79
+
80
+ context "with a calendar" do
81
+ use_vcr_cassette "event_get_success"
82
+ it "should return a single event" do
83
+ client.nested_for('primary').get(@event['id'])['kind'].should == 'calendar#event'
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe ".delete" do
90
+ it "should raise an error if the calendar id was not provided" do
91
+ expect { client.delete(nil) }.to raise_error Google::CalendarRequiredError
92
+ end
93
+
94
+ context "needs an existing event" do
95
+ before :all do
96
+ VCR.use_cassette "event_insert_success" do
97
+ @event = client.nested_for('primary').insert event
98
+ end
99
+ end
100
+
101
+ context "has a calendar" do
102
+ use_vcr_cassette "event_delete_success"
103
+ it "should delete an event by id" do
104
+ client.nested_for('primary').delete(@event['id']).code.should == 204
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ describe ".insert" do
111
+ it "should raise an error if the calendar id was not provided" do
112
+ expect { client.insert(nil) }.to raise_error Google::CalendarRequiredError
113
+ end
114
+
115
+ context "with a calendar" do
116
+ use_vcr_cassette "event_insert_success"
117
+ it "should insert a new event when the data is valid" do
118
+ response = client.nested_for('primary').insert(event)
119
+ response['kind'].should == 'calendar#event'
120
+ client.nested_for('primary').delete response['id'] # clean up
121
+ end
122
+ end
123
+ end
124
+
125
+ describe ".quickAdd" do
126
+ use_vcr_cassette "event_quickadd_success"
127
+
128
+ it "should raise an error if the calendar id was not provided" do
129
+ expect { client.quick_add(nil) }.to raise_error Google::CalendarRequiredError
130
+ end
131
+
132
+ it "should insert a new event when the data is valid" do
133
+ response = client.nested_for('primary').quick_add("meet Jimmy today at 1pm")
134
+ response['kind'].should == 'calendar#event'
135
+ client.nested_for('primary').delete response['id'] # clean up
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,35 @@
1
+ require './lib/gcal_unit'
2
+ require 'dotenv'
3
+ require 'vcr'
4
+
5
+ def token
6
+ @token
7
+ end
8
+
9
+ def configure
10
+ Google.configure do |config|
11
+ Dotenv.load
12
+ config.api_key = ENV['GOOGLE_API_KEY']
13
+ config.client_id = ENV['GOOGLE_CLIENT_ID']
14
+ config.client_secret = ENV['GOOGLE_CLIENT_SECRET']
15
+ config.refresh_token = ENV['GOOGLE_REFRESH_TOKEN']
16
+ end
17
+ end
18
+
19
+ VCR.configure do |c|
20
+ c.cassette_library_dir = 'spec/cassettes'
21
+ c.hook_into :fakeweb
22
+ c.default_cassette_options = { :record => :new_episodes }
23
+ end
24
+
25
+ RSpec.configure do |config|
26
+ config.before(:all) do
27
+ configure
28
+ VCR.use_cassette "authorization_success" do
29
+ # Ensure a new access token before all the specs are run
30
+ client = Google::Authorization.new Google.configuration.refresh_token
31
+ @token = client.refresh()['access_token']
32
+ end
33
+ end
34
+ config.extend VCR::RSpec::Macros
35
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gcal-unit
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brilliant Fantastic
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.7.8
30
+ description: Wraps HTTP calls for the Google Calendar API.
31
+ email: support@brilliantfantastic.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - README.md
36
+ files:
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - gcal-unit.gemspec
41
+ - lib/gcal_unit.rb
42
+ - lib/gcal_unit/authorization.rb
43
+ - lib/gcal_unit/calendar.rb
44
+ - lib/gcal_unit/calendar_list.rb
45
+ - lib/gcal_unit/client.rb
46
+ - lib/gcal_unit/configuration.rb
47
+ - lib/gcal_unit/errors.rb
48
+ - lib/gcal_unit/event.rb
49
+ - spec/lib/gcal_unit/authorization_spec.rb
50
+ - spec/lib/gcal_unit/calendar_list_spec.rb
51
+ - spec/lib/gcal_unit/calendar_spec.rb
52
+ - spec/lib/gcal_unit/configuration_spec.rb
53
+ - spec/lib/gcal_unit/event_spec.rb
54
+ - spec/spec_helper.rb
55
+ homepage: http://github.com/brilliantfantastic/gcal-unit
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: gcal-unit
76
+ rubygems_version: 1.8.23
77
+ signing_key:
78
+ specification_version: 2
79
+ summary: Pimp ass API wrapper for the Google Calendar API.
80
+ test_files: []