googl 0.4.3 → 0.5.0

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.
@@ -1,7 +1,11 @@
1
- = googl
1
+ = googl
2
2
 
3
3
  Google URL Shortener API in Ruby
4
4
 
5
+ == Continuous Integration
6
+
7
+ http://travis-ci.org/zigotto/googl.png Travis[http://travis-ci.org/zigotto/googl]
8
+
5
9
  == Basic Usage
6
10
 
7
11
  === Shorten a long URL
@@ -45,7 +49,60 @@ Go to http://goo.gl to see URL statistics.
45
49
 
46
50
  === OAuth
47
51
 
48
- TODO
52
+ Google supports three flows of OAuth 2.0
53
+
54
+ * The <b>client-side</b> flow for JavaScript applications running in a browser. (TODO)
55
+ * The <b>server-side</b> flow for web applications with servers that can securely store persistent information.
56
+ * The <b>native application</b> flow for desktop and mobile applications.
57
+
58
+ Today, gem googl support only <b>server-side</b> and <b>native application</b>.
59
+
60
+ ==== Server-side web applications
61
+
62
+ You'll need to register your application with Google to get a <em>client_id</em> and record the <em>redirect_uri</em> you'd like to use.
63
+ See the Registering[http://code.google.com/apis/accounts/docs/OAuth2.html#Registering] your app with Google section for details on how to register.
64
+
65
+ client = Googl::OAuth2.server("client_id", "client_secret", "redirect_uri")
66
+
67
+ Redirect your users to
68
+
69
+ client.authorize_url
70
+
71
+ After the user approves access or chooses not to, we'll redirect to the <em>redirect_uri</em> you passed us and append either an authorization code.
72
+
73
+ client.request_access_token(params["code"])
74
+ client.authorized?
75
+ => true
76
+
77
+ Now you can get a user's history, shorten url, etc...
78
+
79
+ history = client.history
80
+ history.total_items
81
+ => 19
82
+
83
+ url = Googl.shorten('https://github.com/zigotto/googl')
84
+ url.short_url
85
+ => http://goo.gl/DWDfi
86
+
87
+ ==== Native applications
88
+
89
+ You'll need to register your application with Google to get a <em>client_id</em> and record the special <em>redirect_uri: urn:ietf:wg:oauth:2.0:oob</em>.
90
+ See the Registering[http://code.google.com/apis/accounts/docs/OAuth2.html#Registering] your app with Google section for details on how to register.
91
+
92
+ When you use this redirect_uri, instead of redirecting the user's browser to a page on your site with an authorization code, Google will display the authorization code or error response in the title of the page and a text field with instructions for the user to copy and paste it in to your application. Your application can either monitor the window title of a web-view or browser window it launches and close it once the authorization code appears or prompt the user to copy and paste the code.
93
+
94
+ client = Googl::OAuth2.native("client_id", "client_secret")
95
+
96
+ Open a browser or a webview to the OAuth dialog at
97
+
98
+ client.authorize_url
99
+
100
+ In the native app flow, after the user approves access, we'll display the authorization code in the title of the page and in a text input with instructions for the user to copy and paste the code to your application
101
+
102
+ code = "copied code"
103
+ client.request_access_token(code)
104
+ client.authorized?
105
+ => true
49
106
 
50
107
  == Analytics
51
108
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.3
1
+ 0.5.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{googl}
8
- s.version = "0.4.3"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jesus Lopes"]
12
- s.date = %q{2011-03-16}
12
+ s.date = %q{2011-05-01}
13
13
  s.description = %q{Small library for Google URL Shortener API}
14
14
  s.email = %q{jlopes@zigotto.com.br}
15
15
  s.extra_rdoc_files = [
@@ -27,11 +27,13 @@ Gem::Specification.new do |s|
27
27
  "lib/googl/base.rb",
28
28
  "lib/googl/client_login.rb",
29
29
  "lib/googl/expand.rb",
30
+ "lib/googl/oauth2/native.rb",
31
+ "lib/googl/oauth2/server.rb",
32
+ "lib/googl/oauth2/utils.rb",
30
33
  "lib/googl/request.rb",
31
34
  "lib/googl/ruby_extensions.rb",
32
35
  "lib/googl/shorten.rb",
33
- "spec/client_spec.rb",
34
- "spec/expand_spec.rb",
36
+ "lib/googl/utils.rb",
35
37
  "spec/fixtures/client_login_invalid.json",
36
38
  "spec/fixtures/client_login_valid.json",
37
39
  "spec/fixtures/expand.json",
@@ -42,25 +44,37 @@ Gem::Specification.new do |s|
42
44
  "spec/fixtures/expand_removed.json",
43
45
  "spec/fixtures/history.json",
44
46
  "spec/fixtures/history_projection_clicks.json",
47
+ "spec/fixtures/oauth2/native.json",
48
+ "spec/fixtures/oauth2/native_invalid.json",
49
+ "spec/fixtures/oauth2/native_token_expires.json",
50
+ "spec/fixtures/oauth2/server.json",
51
+ "spec/fixtures/oauth2/server_invalid.json",
52
+ "spec/fixtures/oauth2/server_token_expires.json",
45
53
  "spec/fixtures/shorten.json",
46
54
  "spec/fixtures/shorten_authenticated.json",
47
55
  "spec/fixtures/shorten_invalid_content_type.json",
48
- "spec/request_spec.rb",
56
+ "spec/googl/client_spec.rb",
57
+ "spec/googl/expand_spec.rb",
58
+ "spec/googl/oauth2/native_spec.rb",
59
+ "spec/googl/oauth2/server_spec.rb",
60
+ "spec/googl/request_spec.rb",
61
+ "spec/googl/shorten_spec.rb",
49
62
  "spec/shared_examples.rb",
50
- "spec/shorten_spec.rb",
51
63
  "spec/spec_helper.rb"
52
64
  ]
53
65
  s.homepage = %q{http://github.com/zigotto/googl}
54
66
  s.licenses = ["MIT"]
55
67
  s.require_paths = ["lib"]
56
- s.rubygems_version = %q{1.5.3}
68
+ s.rubygems_version = %q{1.4.2}
57
69
  s.summary = %q{Wrapper for Google URL Shortener API}
58
70
  s.test_files = [
59
- "spec/client_spec.rb",
60
- "spec/expand_spec.rb",
61
- "spec/request_spec.rb",
71
+ "spec/googl/client_spec.rb",
72
+ "spec/googl/expand_spec.rb",
73
+ "spec/googl/oauth2/native_spec.rb",
74
+ "spec/googl/oauth2/server_spec.rb",
75
+ "spec/googl/request_spec.rb",
76
+ "spec/googl/shorten_spec.rb",
62
77
  "spec/shared_examples.rb",
63
- "spec/shorten_spec.rb",
64
78
  "spec/spec_helper.rb"
65
79
  ]
66
80
 
@@ -2,6 +2,7 @@ require 'httparty'
2
2
  require 'ostruct'
3
3
  require 'json'
4
4
 
5
+ require 'googl/utils'
5
6
  require 'googl/base'
6
7
  require 'googl/request'
7
8
  require 'googl/shorten'
@@ -9,7 +10,12 @@ require 'googl/expand'
9
10
  require 'googl/client_login'
10
11
  require 'googl/ruby_extensions'
11
12
 
13
+ require 'googl/oauth2/utils'
14
+ require 'googl/oauth2/native'
15
+ require 'googl/oauth2/server'
16
+
12
17
  module Googl
18
+ extend self
13
19
 
14
20
  # Creates a new short URL
15
21
  #
@@ -17,7 +23,7 @@ module Googl
17
23
  # url.short_url
18
24
  # => "http://goo.gl/ump4S"
19
25
  #
20
- def self.shorten(url=nil)
26
+ def shorten(url=nil)
21
27
  raise ArgumentError.new("URL to shorten is required") if url.blank?
22
28
  Googl::Shorten.new(url)
23
29
  end
@@ -79,7 +85,7 @@ module Googl
79
85
  #
80
86
  # For mor details, see http://code.google.com/intl/pt-BR/apis/urlshortener/v1/reference.html#resource_url
81
87
  #
82
- def self.expand(url=nil, options={})
88
+ def expand(url=nil, options={})
83
89
  raise ArgumentError.new("URL to expand is required") if url.blank?
84
90
  options = {:shortUrl => url, :projection => nil}.merge!(options)
85
91
  Googl::Expand.new(options)
@@ -95,8 +101,43 @@ module Googl
95
101
  #
96
102
  # Go to http://goo.gl to see URL statistics.
97
103
  #
98
- def self.client(email, passwd)
104
+ def client(email, passwd)
99
105
  Googl::ClientLogin.new(email, passwd)
100
106
  end
101
107
 
108
+ # OAuth 2.0
109
+ #
110
+ # Google supports three flows of OAuth 2.0
111
+ #
112
+ # * client-side
113
+ # * server-side
114
+ # * native application
115
+ #
116
+ # Now, gem googl support only client-side and native application.
117
+ #
118
+ module OAuth2
119
+ extend self
120
+
121
+ # OAuth 2.0 for server-side web applications
122
+ #
123
+ # The server-side flow for web applications with servers that can securely store persistent information
124
+ #
125
+ # client = Googl::OAuth2.server("client_id", "client_secret", "redirect_uri")
126
+ #
127
+ def server(client_id, client_secret, redirect_uri)
128
+ Googl::OAuth2::Server.new(client_id, client_secret, redirect_uri)
129
+ end
130
+
131
+ # OAuth 2.0 for native applications
132
+ #
133
+ # The native application flow for desktop and mobile applications
134
+ #
135
+ # client = Googl::OAuth2.native("client_id", "client_secret")
136
+ #
137
+ def native(client_id, client_secret)
138
+ Googl::OAuth2::Native.new(client_id, client_secret)
139
+ end
140
+
141
+ end
142
+
102
143
  end
@@ -26,12 +26,6 @@ module Googl
26
26
  "#{short_url}.info"
27
27
  end
28
28
 
29
- private
30
-
31
- def modify_headers(item)
32
- Googl::Request.headers.merge!(item)
33
- end
34
-
35
29
  end
36
30
 
37
31
  end
@@ -1,9 +1,8 @@
1
1
  module Googl
2
2
 
3
- class ClientLogin < Base
3
+ class ClientLogin
4
4
 
5
- API_URL = "https://www.google.com/accounts/ClientLogin"
6
- API_HISTORY_URL = "https://www.googleapis.com/urlshortener/v1/url/history"
5
+ include Googl::Utils
7
6
 
8
7
  attr_accessor :code, :items
9
8
 
@@ -11,13 +10,13 @@ module Googl
11
10
  #
12
11
  def initialize(email, passwd)
13
12
  modify_headers('Content-Type' => 'application/x-www-form-urlencoded')
14
- resp = Googl::Request.post(API_URL, :body => params.merge!('Email' => email, 'Passwd' => passwd))
15
- @code = resp.code
13
+ resp = post(API_CLIENT_LOGIN_URL, :body => params.merge!('Email' => email, 'Passwd' => passwd))
14
+ self.code = resp.code
16
15
  if resp.code == 200
17
16
  token = resp.split('=').last.gsub(/\n/, '')
18
17
  modify_headers("Authorization" => "GoogleLogin auth=#{token}")
19
18
  else
20
- raise Exception.new("#{resp.code} #{resp.parsed_response}")
19
+ raise exception("#{resp.code} #{resp.parsed_response}")
21
20
  end
22
21
  end
23
22
 
@@ -26,7 +25,7 @@ module Googl
26
25
  # See Googl.client
27
26
  #
28
27
  def shorten(url)
29
- Googl::Shorten.new(url)
28
+ Googl.shorten(url)
30
29
  end
31
30
 
32
31
  # Gets a user's history of shortened URLs. (Authenticated)
@@ -46,11 +45,12 @@ module Googl
46
45
  # history = client.history(:projection => :analytics_clicks)
47
46
  #
48
47
  def history(options={})
49
- resp = options.blank? ? Googl::Request.get(API_HISTORY_URL) : Googl::Request.get(API_HISTORY_URL, :query => options)
50
- if resp.code == 200
51
- @items = resp.parsed_response.to_openstruct
48
+ resp = options.blank? ? get(Googl::Utils::API_HISTORY_URL) : get(Googl::Utils::API_HISTORY_URL, :query => options)
49
+ case resp.code
50
+ when 200
51
+ self.items = resp.parsed_response.to_openstruct
52
52
  else
53
- raise Exception.new("#{resp.code} #{resp.parsed_response}")
53
+ raise exception("#{resp.code} #{resp.parsed_response}")
54
54
  end
55
55
  end
56
56
 
@@ -2,9 +2,9 @@ module Googl
2
2
 
3
3
  class Expand < Base
4
4
 
5
- API_URL = "https://www.googleapis.com/urlshortener/v1/url"
5
+ include Googl::Utils
6
6
 
7
- attr_accessor :long_url, :analytics, :status, :short_url
7
+ attr_accessor :long_url, :analytics, :status, :short_url, :created
8
8
 
9
9
  # Expands a short URL or gets creation time and analytics. See Googl.expand
10
10
  #
@@ -12,14 +12,15 @@ module Googl
12
12
 
13
13
  options.delete_if {|key, value| value.nil?}
14
14
 
15
- resp = Googl::Request.get(API_URL, :query => options)
15
+ resp = get(API_URL, :query => options)
16
16
  if resp.code == 200
17
- @long_url = resp['longUrl']
18
- @analytics = resp['analytics'].to_openstruct if resp.has_key?('analytics')
19
- @status = resp['status']
20
- @short_url = resp['id']
17
+ self.created = resp['created'] if resp.has_key?('created')
18
+ self.long_url = resp['longUrl']
19
+ self.analytics = resp['analytics'].to_openstruct if resp.has_key?('analytics')
20
+ self.status = resp['status']
21
+ self.short_url = resp['id']
21
22
  else
22
- raise Exception.new("#{resp.code} #{resp.message}")
23
+ raise exception("#{resp.code} #{resp.message}")
23
24
  end
24
25
  end
25
26
 
@@ -0,0 +1,25 @@
1
+ module Googl
2
+ module OAuth2
3
+
4
+ class Native
5
+
6
+ include Googl::Utils
7
+ include Googl::OAuth2::Utils
8
+
9
+ def initialize(client_id, client_secret)
10
+ self.client_id = client_id
11
+ self.client_secret = client_secret
12
+ end
13
+
14
+ def authorize_url
15
+ make_authorize_url("urn:ietf:wg:oauth:2.0:oob")
16
+ end
17
+
18
+ def request_access_token(code)
19
+ request_token(code)
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module Googl
2
+ module OAuth2
3
+
4
+ class Server
5
+
6
+ include Googl::Utils
7
+ include Googl::OAuth2::Utils
8
+
9
+ attr_accessor :redirect_uri
10
+
11
+ def initialize(client_id, client_secret, redirect_uri)
12
+ self.client_id = client_id
13
+ self.client_secret = client_secret
14
+ self.redirect_uri = redirect_uri
15
+ end
16
+
17
+ def authorize_url
18
+ make_authorize_url(redirect_uri)
19
+ end
20
+
21
+ def request_access_token(code)
22
+ request_token(code, redirect_uri)
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,58 @@
1
+ module Googl
2
+ module OAuth2
3
+
4
+ module Utils
5
+
6
+ attr_accessor :client_id, :client_secret, :access_token, :refresh_token, :expires_in, :expires_at
7
+ attr_accessor :items
8
+
9
+ def expires?
10
+ expires_at < Time.now
11
+ end
12
+
13
+ def authorized?
14
+ !access_token.nil? && !refresh_token.nil? && !expires_in.nil? && !expires_at.nil?
15
+ end
16
+
17
+ # Gets a user's history of shortened URLs.
18
+ #
19
+ def history(options={})
20
+ return unless authorized?
21
+ resp = options.blank? ? get(Googl::Utils::API_HISTORY_URL) : get(Googl::Utils::API_HISTORY_URL, :query => options)
22
+ case resp.code
23
+ when 200
24
+ self.items = resp.parsed_response.to_openstruct
25
+ else
26
+ raise exception("#{resp.code} #{resp.parsed_response}")
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def request_token(code, request_uri="urn:ietf:wg:oauth:2.0:oob")
33
+ params = "code=#{code}&client_id=#{client_id}&client_secret=#{client_secret}&redirect_uri=#{request_uri}&grant_type=authorization_code"
34
+ modify_headers('Content-Type' => 'application/x-www-form-urlencoded')
35
+ resp = post("https://accounts.google.com/o/oauth2/token", :body => params)
36
+ case resp.code
37
+ when 200
38
+ modify_headers("Authorization" => "OAuth #{resp["access_token"]}")
39
+ self.access_token = resp["access_token"]
40
+ self.refresh_token = resp["refresh_token"]
41
+ self.expires_in = resp["expires_in"]
42
+ self.expires_at = Time.now + expires_in if expires_in
43
+ self
44
+ when 401
45
+ raise exception("#{resp.code} #{resp.parsed_response["error"]["message"]}")
46
+ else
47
+ raise exception("#{resp.code} #{resp.parsed_response["error"]}")
48
+ end
49
+ end
50
+
51
+ def make_authorize_url(redirect_uri)
52
+ "https://accounts.google.com/o/oauth2/auth?client_id=#{client_id}&redirect_uri=#{redirect_uri}&scope=#{Googl::Utils::SCOPE_URL}&response_type=code"
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -2,7 +2,7 @@ module Googl
2
2
 
3
3
  class Shorten < Base
4
4
 
5
- API_URL = 'https://www.googleapis.com/urlshortener/v1/url'
5
+ include Googl::Utils
6
6
 
7
7
  attr_accessor :short_url, :long_url
8
8
 
@@ -11,12 +11,12 @@ module Googl
11
11
  def initialize(long_url)
12
12
  modify_headers('Content-Type' => 'application/json')
13
13
  options = {"longUrl" => long_url}.to_json
14
- resp = Googl::Request.post(API_URL, :body => options)
14
+ resp = post(API_URL, :body => options)
15
15
  if resp.code == 200
16
- @short_url = resp['id']
17
- @long_url = resp['longUrl']
16
+ self.short_url = resp['id']
17
+ self.long_url = resp['longUrl']
18
18
  else
19
- raise Exception.new(resp.parsed_response)
19
+ raise exception(resp.parsed_response)
20
20
  end
21
21
  end
22
22
 
@@ -0,0 +1,30 @@
1
+ module Googl
2
+
3
+ module Utils # :nodoc:
4
+
5
+ API_URL = 'https://www.googleapis.com/urlshortener/v1/url'
6
+ API_HISTORY_URL = "https://www.googleapis.com/urlshortener/v1/url/history"
7
+ API_CLIENT_LOGIN_URL = "https://www.google.com/accounts/ClientLogin"
8
+ SCOPE_URL = "https://www.googleapis.com/auth/urlshortener"
9
+
10
+ private
11
+
12
+ def modify_headers(item)
13
+ Googl::Request.headers.merge!(item)
14
+ end
15
+
16
+ def post(url, params={})
17
+ Googl::Request.post(url, params)
18
+ end
19
+
20
+ def get(url, params={})
21
+ Googl::Request.get(url, params)
22
+ end
23
+
24
+ def exception(msg)
25
+ Exception.new(msg)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,12 @@
1
+ HTTP/1.1 200 OK
2
+ Content-Type: application/json
3
+ Date: Thu, 17 Mar 2011 02:45:33 GMT
4
+ Expires: Thu, 17 Mar 2011 02:45:33 GMT
5
+ Cache-Control: private, max-age=0
6
+ X-Content-Type-Options: nosniff
7
+ X-Frame-Options: SAMEORIGIN
8
+ X-XSS-Protection: 1; mode=block
9
+ Server: GSE
10
+ Transfer-Encoding: chunked
11
+
12
+ {"access_token":"1/YCzoGAYT8XUuOifjNh_KqA","expires_in":3600,"refresh_token":"1/x_31GvgzdgHDMkRep5i8YxFlq76w3yjFu9Dp72Op-pI"}
@@ -0,0 +1,12 @@
1
+ HTTP/1.1 400 Bad Request
2
+ Content-Type: application/json
3
+ Date: Thu, 17 Mar 2011 03:05:33 GMT
4
+ Expires: Thu, 17 Mar 2011 03:05:33 GMT
5
+ Cache-Control: private, max-age=0
6
+ X-Content-Type-Options: nosniff
7
+ X-Frame-Options: SAMEORIGIN
8
+ X-XSS-Protection: 1; mode=block
9
+ Server: GSE
10
+ Transfer-Encoding: chunked
11
+
12
+ {"error":"invalid_token"}
@@ -0,0 +1,27 @@
1
+ HTTP/1.1 401 Unauthorized
2
+ WWW-Authenticate: AuthSub realm="https://gaiastaging.corp.google.com/accounts/AuthSubRequest" allowed-scopes="https://www.googleapis.com/auth/urlshortener"
3
+ Content-Type: application/json; charset=UTF-8
4
+ Date: Fri, 18 Mar 2011 23:32:07 GMT
5
+ Expires: Fri, 18 Mar 2011 23:32:07 GMT
6
+ Cache-Control: private, max-age=0
7
+ X-Content-Type-Options: nosniff
8
+ X-Frame-Options: SAMEORIGIN
9
+ X-XSS-Protection: 1; mode=block
10
+ Server: GSE
11
+ Transfer-Encoding: chunked
12
+
13
+ {
14
+ "error": {
15
+ "errors": [
16
+ {
17
+ "domain": "global",
18
+ "reason": "invalid",
19
+ "message": "Invalid Credentials",
20
+ "locationType": "header",
21
+ "location": "Authorization"
22
+ }
23
+ ],
24
+ "code": 401,
25
+ "message": "Invalid Credentials"
26
+ }
27
+ }
@@ -0,0 +1,12 @@
1
+ HTTP/1.1 200 OK
2
+ Content-Type: application/json
3
+ Date: Fri, 18 Mar 2011 02:11:50 GMT
4
+ Expires: Fri, 18 Mar 2011 02:11:50 GMT
5
+ Cache-Control: private, max-age=0
6
+ X-Content-Type-Options: nosniff
7
+ X-Frame-Options: SAMEORIGIN
8
+ X-XSS-Protection: 1; mode=block
9
+ Server: GSE
10
+ Transfer-Encoding: chunked
11
+
12
+ {"access_token":"1/9eNgoHDXi-1u1fDzZ2wLLGATiaQZnWPB51nTvo8n9Sw","expires_in":3600,"refresh_token":"1/gvmLC5XlU0qRPIBR3mt7OBBfEoTKB6i2T-Gu4dBDupw"}
@@ -0,0 +1,12 @@
1
+ HTTP/1.1 400 Bad Request
2
+ Content-Type: application/json
3
+ Date: Fri, 18 Mar 2011 14:12:06 GMT
4
+ Expires: Fri, 18 Mar 2011 14:12:06 GMT
5
+ Cache-Control: private, max-age=0
6
+ X-Content-Type-Options: nosniff
7
+ X-Frame-Options: SAMEORIGIN
8
+ X-XSS-Protection: 1; mode=block
9
+ Server: GSE
10
+ Transfer-Encoding: chunked
11
+
12
+ {"error":"invalid_token"}
@@ -0,0 +1,27 @@
1
+ HTTP/1.1 401 Unauthorized
2
+ WWW-Authenticate: AuthSub realm="https://gaiastaging.corp.google.com/accounts/AuthSubRequest" allowed-scopes="https://www.googleapis.com/auth/urlshortener"
3
+ Content-Type: application/json; charset=UTF-8
4
+ Date: Sat, 19 Mar 2011 00:00:35 GMT
5
+ Expires: Sat, 19 Mar 2011 00:00:35 GMT
6
+ Cache-Control: private, max-age=0
7
+ X-Content-Type-Options: nosniff
8
+ X-Frame-Options: SAMEORIGIN
9
+ X-XSS-Protection: 1; mode=block
10
+ Server: GSE
11
+ Transfer-Encoding: chunked
12
+
13
+ {
14
+ "error": {
15
+ "errors": [
16
+ {
17
+ "domain": "global",
18
+ "reason": "invalid",
19
+ "message": "Invalid Credentials",
20
+ "locationType": "header",
21
+ "location": "Authorization"
22
+ }
23
+ ],
24
+ "code": 401,
25
+ "message": "Invalid Credentials"
26
+ }
27
+ }
@@ -60,6 +60,15 @@ describe Googl::Expand do
60
60
 
61
61
  subject { Googl.expand('http://goo.gl/DWDfi', :projection => :full) }
62
62
 
63
+ describe "#created" do
64
+ let(:element) { subject.created }
65
+
66
+ it "should be the time url was shortened" do
67
+ element.should == Time.iso8601("2011-01-13T03:48:10.309+00:00")
68
+ end
69
+
70
+ end
71
+
63
72
  describe "#all_time" do
64
73
  let(:element) { subject.analytics.all_time }
65
74
 
@@ -67,7 +76,7 @@ describe Googl::Expand do
67
76
  it_should_behave_like 'a period'
68
77
 
69
78
  it "should rename id to label" do
70
- element.countries.first.label.should == "BR"
79
+ element.countries.first.label.should == "BR"
71
80
  end
72
81
  end
73
82
 
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ describe Googl::OAuth2::Native do
4
+
5
+ before :each do
6
+ fake_urls? true
7
+ end
8
+
9
+ subject do
10
+ Googl::OAuth2.native("185706845724.apps.googleusercontent.com", "DrBLCdCQ3gOybHrj7TPz/B0N")
11
+ end
12
+
13
+ describe "#initialize" do
14
+
15
+ it "should assign client_id" do
16
+ subject.client_id.should == "185706845724.apps.googleusercontent.com"
17
+ end
18
+
19
+ it "should assign client_secret" do
20
+ subject.client_secret.should == "DrBLCdCQ3gOybHrj7TPz/B0N"
21
+ end
22
+
23
+ end
24
+
25
+ describe "#authorize_url" do
26
+
27
+ it { subject.should respond_to(:authorize_url) }
28
+
29
+ it "should return url for authorize" do
30
+ subject.authorize_url.should == "https://accounts.google.com/o/oauth2/auth?client_id=185706845724.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/urlshortener&response_type=code"
31
+ end
32
+
33
+ it "should include the client_id" do
34
+ subject.authorize_url.should be_include("client_id=185706845724.apps.googleusercontent.com")
35
+ end
36
+
37
+ it "should include the redirect_uri" do
38
+ subject.authorize_url.should be_include("redirect_uri=urn:ietf:wg:oauth:2.0:oob")
39
+ end
40
+
41
+ it "should include the scope" do
42
+ subject.authorize_url.should be_include("scope=https://www.googleapis.com/auth/urlshortener")
43
+ end
44
+
45
+ it "should include the response_type" do
46
+ subject.authorize_url.should be_include("response_type=code")
47
+ end
48
+
49
+ end
50
+
51
+ describe "#request_access_token" do
52
+
53
+ it { subject.should respond_to(:request_access_token) }
54
+
55
+ context "with valid code" do
56
+
57
+ let(:native) { subject.request_access_token("4/SuSud6RqPojUXsPpeh-wSVCwnmTQ") }
58
+
59
+ it "should return a access_token" do
60
+ native.access_token.should == "1/YCzoGAYT8XUuOifjNh_KqA"
61
+ end
62
+
63
+ it "should return a refresh_token" do
64
+ native.refresh_token.should == "1/x_31GvgzdgHDMkRep5i8YxFlq76w3yjFu9Dp72Op-pI"
65
+ end
66
+
67
+ it "should return a expires_in" do
68
+ native.expires_in.should == 3600
69
+ end
70
+
71
+ end
72
+
73
+ context "with invalid code" do
74
+ it "should raise error" do
75
+ lambda { subject.request_access_token("my_invalid_code") }.should raise_error(/400 invalid_token/)
76
+ end
77
+ it "should raise Invalid Credentials on 401 response" do
78
+ lambda { subject.request_access_token("4/JvkEhCtr7tv1A60ENmubQT-cosRl") }.should raise_error(/401 Invalid Credentials/)
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ describe "#expires_at" do
85
+
86
+ before do
87
+ @now = Time.now
88
+ Time.stub!(:now).and_return(@now)
89
+ end
90
+
91
+ let(:native) { subject.request_access_token("4/SuSud6RqPojUXsPpeh-wSVCwnmTQ") }
92
+
93
+ it "should be a time representation of #expires_in" do
94
+ native.expires_at.should == (@now + 3600)
95
+ end
96
+
97
+ end
98
+
99
+ describe "#expires?" do
100
+
101
+ before :each do
102
+ Time.stub!(:now).and_return(Time.parse("2011-04-23 15:30:00"))
103
+ subject.request_access_token("4/SuSud6RqPojUXsPpeh-wSVCwnmTQ")
104
+ end
105
+
106
+ it "should be true if access token expires" do
107
+ Time.stub!(:now).and_return(Time.parse("2011-04-23 18:30:00"))
108
+ subject.expires?.should be_true
109
+ end
110
+
111
+ it "should be false if access token not expires" do
112
+ subject.expires?.should be_false
113
+ end
114
+
115
+ end
116
+
117
+ describe "#authorized?" do
118
+
119
+ it "should return false if client is not authorized" do
120
+ subject.authorized?.should be_false
121
+ end
122
+
123
+ let(:native) { subject.request_access_token("4/SuSud6RqPojUXsPpeh-wSVCwnmTQ") }
124
+
125
+ it "should return true if client is authorized" do
126
+ native.authorized?.should be_true
127
+ end
128
+
129
+ end
130
+
131
+ context "when gets a user history of shortened" do
132
+
133
+ it { subject.should respond_to(:history) }
134
+
135
+ it "should not return when client not authorized" do
136
+ subject.history.should be_nil
137
+ end
138
+
139
+ context "if authorized" do
140
+
141
+ let(:native) { subject.request_access_token("4/SuSud6RqPojUXsPpeh-wSVCwnmTQ") }
142
+ let(:history) { native.history }
143
+
144
+ it { history.should respond_to(:total_items) }
145
+ it { history.should respond_to(:items_per_page) }
146
+ it { history.should respond_to(:items) }
147
+
148
+ end
149
+
150
+ end
151
+
152
+ end
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe Googl::OAuth2::Server do
4
+
5
+ before :each do
6
+ fake_urls? true
7
+ end
8
+
9
+ subject do
10
+ Googl::OAuth2.server("438834493660.apps.googleusercontent.com", "8i4iJJkFTukWhNpxTU1b2Zhi", "http://gooogl.heroku.com/back")
11
+ end
12
+
13
+ describe "#initialize" do
14
+
15
+ it "should assign client_id" do
16
+ subject.client_id.should == "438834493660.apps.googleusercontent.com"
17
+ end
18
+
19
+ it "should assign client_secret" do
20
+ subject.client_secret.should == "8i4iJJkFTukWhNpxTU1b2Zhi"
21
+ end
22
+
23
+ it "should assign redirect_uri" do
24
+ subject.redirect_uri.should == "http://gooogl.heroku.com/back"
25
+ end
26
+
27
+ end
28
+
29
+ describe "#authorize_url" do
30
+
31
+ it { subject.should respond_to(:authorize_url) }
32
+
33
+ it "should return url for authorize" do
34
+ subject.authorize_url.should == "https://accounts.google.com/o/oauth2/auth?client_id=438834493660.apps.googleusercontent.com&redirect_uri=http://gooogl.heroku.com/back&scope=https://www.googleapis.com/auth/urlshortener&response_type=code"
35
+ end
36
+
37
+ it "should include the client_id" do
38
+ subject.authorize_url.should be_include("client_id=438834493660.apps.googleusercontent.com")
39
+ end
40
+
41
+ it "should include the redirect_uri" do
42
+ subject.authorize_url.should be_include("redirect_uri=http://gooogl.heroku.com/back")
43
+ end
44
+
45
+ it "should include the scope" do
46
+ subject.authorize_url.should be_include("scope=https://www.googleapis.com/auth/urlshortener")
47
+ end
48
+
49
+ it "should include the response_type" do
50
+ subject.authorize_url.should be_include("response_type=code")
51
+ end
52
+
53
+ end
54
+
55
+ describe "#request_access_token" do
56
+
57
+ it { subject.should respond_to(:request_access_token) }
58
+
59
+ context "with valid code" do
60
+
61
+ let(:server) { subject.request_access_token("4/z43CZpNmqd0IO3dR1Y_ouase13CH") }
62
+
63
+ it "should return a access_token" do
64
+ server.access_token.should == "1/9eNgoHDXi-1u1fDzZ2wLLGATiaQZnWPB51nTvo8n9Sw"
65
+ end
66
+
67
+ it "should return a refresh_token" do
68
+ server.refresh_token.should == "1/gvmLC5XlU0qRPIBR3mt7OBBfEoTKB6i2T-Gu4dBDupw"
69
+ end
70
+
71
+ it "should return a expires_in" do
72
+ server.expires_in.should == 3600
73
+ end
74
+
75
+ end
76
+
77
+ context "with invalid code" do
78
+ it "should raise error" do
79
+ lambda { subject.request_access_token("my_invalid_code") }.should raise_error(/400 invalid_token/)
80
+ end
81
+ it "should raise Invalid Credentials on 401 response" do
82
+ lambda { subject.request_access_token("4/JvkEhCtr7tv1A60ENmubQT-cosRl") }.should raise_error(/401 Invalid Credentials/)
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ describe "#expires_at" do
89
+
90
+ before do
91
+ @now = Time.now
92
+ Time.stub!(:now).and_return(@now)
93
+ end
94
+
95
+ let(:server) { subject.request_access_token("4/z43CZpNmqd0IO3dR1Y_ouase13CH") }
96
+
97
+ it "should be a time representation of #expires_in" do
98
+ server.expires_at.should == (@now + 3600)
99
+ end
100
+
101
+ end
102
+
103
+ describe "#expires?" do
104
+
105
+ before :each do
106
+ Time.stub!(:now).and_return(Time.parse("2011-04-23 15:30:00"))
107
+ subject.request_access_token("4/z43CZpNmqd0IO3dR1Y_ouase13CH")
108
+ end
109
+
110
+ it "should be true if access token expires" do
111
+ Time.stub!(:now).and_return(Time.parse("2011-04-23 18:30:00"))
112
+ subject.expires?.should be_true
113
+ end
114
+
115
+ it "should be false if access token not expires" do
116
+ subject.expires?.should be_false
117
+ end
118
+
119
+ end
120
+
121
+ describe "#authorized?" do
122
+
123
+ it "should return false if client is not authorized" do
124
+ subject.authorized?.should be_false
125
+ end
126
+
127
+ let(:server) { subject.request_access_token("4/z43CZpNmqd0IO3dR1Y_ouase13CH") }
128
+
129
+ it "should return true if client is authorized" do
130
+ server.authorized?.should be_true
131
+ end
132
+
133
+ end
134
+
135
+ context "when gets a user history of shortened" do
136
+
137
+ it { subject.should respond_to(:history) }
138
+
139
+ it "should not return when client not authorized" do
140
+ subject.history.should be_nil
141
+ end
142
+
143
+ context "if authorized" do
144
+
145
+ let(:server) { subject.request_access_token("4/z43CZpNmqd0IO3dR1Y_ouase13CH") }
146
+ let(:history) { server.history }
147
+
148
+ it { history.should respond_to(:total_items) }
149
+ it { history.should respond_to(:items_per_page) }
150
+ it { history.should respond_to(:items) }
151
+
152
+ end
153
+
154
+ end
155
+
156
+ end
@@ -75,7 +75,7 @@ def fake_urls?(status)
75
75
  :headers => {'Authorization'=>'GoogleLogin auth=DQAAAK8AAAC9ahL-o7g', 'Content-Type'=>'application/json'}).
76
76
  to_return(load_fixture('shorten_authenticated.json'))
77
77
 
78
- # History
78
+ # History for ClientLogin
79
79
  stub_request(:get, "https://www.googleapis.com/urlshortener/v1/url/history").
80
80
  with(:headers => {'Authorization'=>'GoogleLogin auth=DQAAAK8AAAC9ahL-o7g'}).
81
81
  to_return(load_fixture('history.json'))
@@ -84,6 +84,53 @@ def fake_urls?(status)
84
84
  stub_request(:get, "https://www.googleapis.com/urlshortener/v1/url/history?projection=analytics_clicks").
85
85
  with(:headers => {'Authorization'=>'GoogleLogin auth=DQAAAK8AAAC9ahL-o7g'}).
86
86
  to_return(load_fixture('history_projection_clicks.json'))
87
+
88
+ # OAuth 2.0 for native applications
89
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
90
+ with(:body => "code=4/SuSud6RqPojUXsPpeh-wSVCwnmTQ&client_id=185706845724.apps.googleusercontent.com&client_secret=DrBLCdCQ3gOybHrj7TPz/B0N&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code",
91
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
92
+ to_return(load_fixture('oauth2/native.json'))
93
+
94
+ # OAuth 2.0 for native applications (invalid token)
95
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
96
+ with(:body => "code=my_invalid_code&client_id=185706845724.apps.googleusercontent.com&client_secret=DrBLCdCQ3gOybHrj7TPz/B0N&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code",
97
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
98
+ to_return(load_fixture('oauth2/native_invalid.json'))
99
+
100
+ # OAuth 2.0 for native applications (expired token)
101
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
102
+ with(:body => "code=4/JvkEhCtr7tv1A60ENmubQT-cosRl&client_id=185706845724.apps.googleusercontent.com&client_secret=DrBLCdCQ3gOybHrj7TPz/B0N&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code",
103
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
104
+ to_return(load_fixture('oauth2/native_token_expires.json'))
105
+
106
+ # OAuth 2.0 for native applications (history)
107
+ stub_request(:get, "https://www.googleapis.com/urlshortener/v1/url/history").
108
+ with(:headers => {'Authorization'=>'OAuth 1/YCzoGAYT8XUuOifjNh_KqA', 'Content-Type'=>'application/x-www-form-urlencoded'}).
109
+ to_return(load_fixture('history.json'))
110
+
111
+ # OAuth 2.0 for server-side web applications
112
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
113
+ with(:body => "code=4/z43CZpNmqd0IO3dR1Y_ouase13CH&client_id=438834493660.apps.googleusercontent.com&client_secret=8i4iJJkFTukWhNpxTU1b2Zhi&redirect_uri=http://gooogl.heroku.com/back&grant_type=authorization_code",
114
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
115
+ to_return(load_fixture('oauth2/server.json'))
116
+
117
+ # OAuth 2.0 for server-side web applications (invalid token)
118
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
119
+ with(:body => "code=my_invalid_code&client_id=438834493660.apps.googleusercontent.com&client_secret=8i4iJJkFTukWhNpxTU1b2Zhi&redirect_uri=http://gooogl.heroku.com/back&grant_type=authorization_code",
120
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
121
+ to_return(load_fixture('oauth2/server_invalid.json'))
122
+
123
+ # OAuth 2.0 for server-side web applications (expired token)
124
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").
125
+ with(:body => "code=4/JvkEhCtr7tv1A60ENmubQT-cosRl&client_id=438834493660.apps.googleusercontent.com&client_secret=8i4iJJkFTukWhNpxTU1b2Zhi&redirect_uri=http://gooogl.heroku.com/back&grant_type=authorization_code",
126
+ :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
127
+ to_return(load_fixture('oauth2/server_token_expires.json'))
128
+
129
+ # OAuth 2.0 for server-side web applications (history)
130
+ stub_request(:get, "https://www.googleapis.com/urlshortener/v1/url/history").
131
+ with(:headers => {'Authorization'=>'OAuth 1/9eNgoHDXi-1u1fDzZ2wLLGATiaQZnWPB51nTvo8n9Sw'}).
132
+ to_return(load_fixture('history.json'))
133
+
87
134
  else
88
135
  WebMock.allow_net_connect!
89
136
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googl
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 4
9
- - 3
10
- version: 0.4.3
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jesus Lopes
@@ -15,12 +15,14 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-16 00:00:00 -03:00
18
+ date: 2011-05-01 00:00:00 -03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ name: httparty
22
24
  type: :runtime
23
- requirement: &id001 !ruby/object:Gem::Requirement
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
26
  none: false
25
27
  requirements:
26
28
  - - ">="
@@ -31,12 +33,12 @@ dependencies:
31
33
  - 6
32
34
  - 1
33
35
  version: 0.6.1
34
- name: httparty
35
- version_requirements: *id001
36
- prerelease: false
36
+ requirement: *id001
37
37
  - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ name: json
38
40
  type: :runtime
39
- requirement: &id002 !ruby/object:Gem::Requirement
41
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
42
  none: false
41
43
  requirements:
42
44
  - - ">="
@@ -47,12 +49,12 @@ dependencies:
47
49
  - 4
48
50
  - 6
49
51
  version: 1.4.6
50
- name: json
51
- version_requirements: *id002
52
- prerelease: false
52
+ requirement: *id002
53
53
  - !ruby/object:Gem::Dependency
54
+ prerelease: false
55
+ name: rspec
54
56
  type: :development
55
- requirement: &id003 !ruby/object:Gem::Requirement
57
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
58
  none: false
57
59
  requirements:
58
60
  - - ~>
@@ -63,12 +65,12 @@ dependencies:
63
65
  - 3
64
66
  - 0
65
67
  version: 2.3.0
66
- name: rspec
67
- version_requirements: *id003
68
- prerelease: false
68
+ requirement: *id003
69
69
  - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ name: bundler
70
72
  type: :development
71
- requirement: &id004 !ruby/object:Gem::Requirement
73
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
74
  none: false
73
75
  requirements:
74
76
  - - ~>
@@ -79,12 +81,12 @@ dependencies:
79
81
  - 0
80
82
  - 0
81
83
  version: 1.0.0
82
- name: bundler
83
- version_requirements: *id004
84
- prerelease: false
84
+ requirement: *id004
85
85
  - !ruby/object:Gem::Dependency
86
+ prerelease: false
87
+ name: jeweler
86
88
  type: :development
87
- requirement: &id005 !ruby/object:Gem::Requirement
89
+ version_requirements: &id005 !ruby/object:Gem::Requirement
88
90
  none: false
89
91
  requirements:
90
92
  - - ~>
@@ -95,12 +97,12 @@ dependencies:
95
97
  - 5
96
98
  - 2
97
99
  version: 1.5.2
98
- name: jeweler
99
- version_requirements: *id005
100
- prerelease: false
100
+ requirement: *id005
101
101
  - !ruby/object:Gem::Dependency
102
+ prerelease: false
103
+ name: rcov
102
104
  type: :development
103
- requirement: &id006 !ruby/object:Gem::Requirement
105
+ version_requirements: &id006 !ruby/object:Gem::Requirement
104
106
  none: false
105
107
  requirements:
106
108
  - - ">="
@@ -109,12 +111,12 @@ dependencies:
109
111
  segments:
110
112
  - 0
111
113
  version: "0"
112
- name: rcov
113
- version_requirements: *id006
114
- prerelease: false
114
+ requirement: *id006
115
115
  - !ruby/object:Gem::Dependency
116
+ prerelease: false
117
+ name: webmock
116
118
  type: :development
117
- requirement: &id007 !ruby/object:Gem::Requirement
119
+ version_requirements: &id007 !ruby/object:Gem::Requirement
118
120
  none: false
119
121
  requirements:
120
122
  - - ~>
@@ -125,9 +127,7 @@ dependencies:
125
127
  - 6
126
128
  - 2
127
129
  version: 1.6.2
128
- name: webmock
129
- version_requirements: *id007
130
- prerelease: false
130
+ requirement: *id007
131
131
  description: Small library for Google URL Shortener API
132
132
  email: jlopes@zigotto.com.br
133
133
  executables: []
@@ -148,11 +148,13 @@ files:
148
148
  - lib/googl/base.rb
149
149
  - lib/googl/client_login.rb
150
150
  - lib/googl/expand.rb
151
+ - lib/googl/oauth2/native.rb
152
+ - lib/googl/oauth2/server.rb
153
+ - lib/googl/oauth2/utils.rb
151
154
  - lib/googl/request.rb
152
155
  - lib/googl/ruby_extensions.rb
153
156
  - lib/googl/shorten.rb
154
- - spec/client_spec.rb
155
- - spec/expand_spec.rb
157
+ - lib/googl/utils.rb
156
158
  - spec/fixtures/client_login_invalid.json
157
159
  - spec/fixtures/client_login_valid.json
158
160
  - spec/fixtures/expand.json
@@ -163,12 +165,22 @@ files:
163
165
  - spec/fixtures/expand_removed.json
164
166
  - spec/fixtures/history.json
165
167
  - spec/fixtures/history_projection_clicks.json
168
+ - spec/fixtures/oauth2/native.json
169
+ - spec/fixtures/oauth2/native_invalid.json
170
+ - spec/fixtures/oauth2/native_token_expires.json
171
+ - spec/fixtures/oauth2/server.json
172
+ - spec/fixtures/oauth2/server_invalid.json
173
+ - spec/fixtures/oauth2/server_token_expires.json
166
174
  - spec/fixtures/shorten.json
167
175
  - spec/fixtures/shorten_authenticated.json
168
176
  - spec/fixtures/shorten_invalid_content_type.json
169
- - spec/request_spec.rb
177
+ - spec/googl/client_spec.rb
178
+ - spec/googl/expand_spec.rb
179
+ - spec/googl/oauth2/native_spec.rb
180
+ - spec/googl/oauth2/server_spec.rb
181
+ - spec/googl/request_spec.rb
182
+ - spec/googl/shorten_spec.rb
170
183
  - spec/shared_examples.rb
171
- - spec/shorten_spec.rb
172
184
  - spec/spec_helper.rb
173
185
  has_rdoc: true
174
186
  homepage: http://github.com/zigotto/googl
@@ -200,14 +212,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
212
  requirements: []
201
213
 
202
214
  rubyforge_project:
203
- rubygems_version: 1.5.3
215
+ rubygems_version: 1.4.2
204
216
  signing_key:
205
217
  specification_version: 3
206
218
  summary: Wrapper for Google URL Shortener API
207
219
  test_files:
208
- - spec/client_spec.rb
209
- - spec/expand_spec.rb
210
- - spec/request_spec.rb
220
+ - spec/googl/client_spec.rb
221
+ - spec/googl/expand_spec.rb
222
+ - spec/googl/oauth2/native_spec.rb
223
+ - spec/googl/oauth2/server_spec.rb
224
+ - spec/googl/request_spec.rb
225
+ - spec/googl/shorten_spec.rb
211
226
  - spec/shared_examples.rb
212
- - spec/shorten_spec.rb
213
227
  - spec/spec_helper.rb