googl 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "httparty", "0.6.1"
3
+ gem "httparty", ">= 0.6.1"
4
4
 
5
5
  group :development do
6
6
  gem "rspec", "~> 2.3.0"
data/Gemfile.lock CHANGED
@@ -30,7 +30,7 @@ PLATFORMS
30
30
 
31
31
  DEPENDENCIES
32
32
  bundler (~> 1.0.0)
33
- httparty (= 0.6.1)
33
+ httparty (>= 0.6.1)
34
34
  jeweler (~> 1.5.2)
35
35
  rcov
36
36
  rspec (~> 2.3.0)
data/README.rdoc CHANGED
@@ -44,6 +44,63 @@ Go to http://goo.gl to see URL statistics.
44
44
 
45
45
  TODO
46
46
 
47
+ == Analytics
48
+
49
+ Expands a short URL or gets creation time and analytics
50
+
51
+ For analytics and additional information to return (using the :projection parameter)
52
+
53
+ :full returns the creation timestamp and all available analytics
54
+ :analytics_clicks returns only click counts
55
+ :analytics_top_strings returns only top string counts (e.g. referrers, countries, etc)
56
+
57
+ === Get Analytics
58
+
59
+ url = Googl.expand('http://goo.gl/DWDfi', :projection => :full)
60
+
61
+ url.analytics.all_time.browsers.first.label
62
+ => "Chrome"
63
+ url.analytics.all_time.browsers.first.count
64
+ => "11"
65
+
66
+ A summary of the click analytics for the short and long URL
67
+
68
+ url.analytics
69
+
70
+ Analytics details for a particular window of time; counts in this object are of clicks that occurred within the most recent window of this length.
71
+
72
+ url.analytics.all_time
73
+ url.analytics.month
74
+ url.analytics.week
75
+ url.analytics.day
76
+ url.analytics.two_hours
77
+
78
+ Number of clicks on this short URL. Replace (*) for all_time, month, week, day or two_hours
79
+
80
+ url.analytics.*.short_url_clicks
81
+
82
+ Number of clicks on all goo.gl short URLs pointing to this long URL.
83
+
84
+ url.analytics.*.long_url_clicks
85
+
86
+ Top referring hosts, e.g. "www.google.com"; sorted by (descending) click counts. Only present if this data is available.
87
+
88
+ url.analytics.*.referrers
89
+
90
+ Top countries (expressed as country codes), e.g. "US" or "DE"; sorted by (descending) click counts.
91
+
92
+ url.analytics.*.countries
93
+
94
+ Top browsers, e.g. "Chrome"; sorted by (descending) click counts.
95
+
96
+ url.analytics.*.browsers
97
+
98
+ Top platforms or OSes, e.g. "Windows"; sorted by (descending) click counts.
99
+
100
+ url.analytics.*.platforms
101
+
102
+ For details, see http://code.google.com/intl/pt-BR/apis/urlshortener/v1/reference.html#resource_url
103
+
47
104
  == Installation
48
105
 
49
106
  gem install googl
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/googl.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{googl}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.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-01-13}
12
+ s.date = %q{2011-01-17}
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 = [
@@ -28,14 +28,22 @@ Gem::Specification.new do |s|
28
28
  "lib/googl/client_login.rb",
29
29
  "lib/googl/expand.rb",
30
30
  "lib/googl/request.rb",
31
+ "lib/googl/ruby_extensions.rb",
31
32
  "lib/googl/shorten.rb",
32
33
  "spec/client_spec.rb",
33
34
  "spec/expand_spec.rb",
34
35
  "spec/fixtures/client_login_invalid.json",
35
36
  "spec/fixtures/client_login_valid.json",
36
37
  "spec/fixtures/expand.json",
38
+ "spec/fixtures/expand_404.json",
39
+ "spec/fixtures/expand_projection_clicks.json",
40
+ "spec/fixtures/expand_projection_full.json",
41
+ "spec/fixtures/expand_projection_strings.json",
42
+ "spec/fixtures/expand_removed.json",
37
43
  "spec/fixtures/shorten.json",
38
44
  "spec/fixtures/shorten_authenticated.json",
45
+ "spec/fixtures/shorten_invalid_content_type.json",
46
+ "spec/shared_examples.rb",
39
47
  "spec/shorten_spec.rb",
40
48
  "spec/spec_helper.rb"
41
49
  ]
@@ -47,6 +55,7 @@ Gem::Specification.new do |s|
47
55
  s.test_files = [
48
56
  "spec/client_spec.rb",
49
57
  "spec/expand_spec.rb",
58
+ "spec/shared_examples.rb",
50
59
  "spec/shorten_spec.rb",
51
60
  "spec/spec_helper.rb"
52
61
  ]
@@ -55,7 +64,7 @@ Gem::Specification.new do |s|
55
64
  s.specification_version = 3
56
65
 
57
66
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
- s.add_runtime_dependency(%q<httparty>, ["= 0.6.1"])
67
+ s.add_runtime_dependency(%q<httparty>, [">= 0.6.1"])
59
68
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
60
69
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
61
70
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -63,7 +72,7 @@ Gem::Specification.new do |s|
63
72
  s.add_development_dependency(%q<webmock>, ["~> 1.6.2"])
64
73
  s.add_runtime_dependency(%q<httparty>, [">= 0.6.1"])
65
74
  else
66
- s.add_dependency(%q<httparty>, ["= 0.6.1"])
75
+ s.add_dependency(%q<httparty>, [">= 0.6.1"])
67
76
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
68
77
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
78
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -72,7 +81,7 @@ Gem::Specification.new do |s|
72
81
  s.add_dependency(%q<httparty>, [">= 0.6.1"])
73
82
  end
74
83
  else
75
- s.add_dependency(%q<httparty>, ["= 0.6.1"])
84
+ s.add_dependency(%q<httparty>, [">= 0.6.1"])
76
85
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
77
86
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
78
87
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
data/lib/googl.rb CHANGED
@@ -1,21 +1,99 @@
1
1
  require 'httparty'
2
+ require 'ostruct'
2
3
 
3
4
  require 'googl/base'
4
5
  require 'googl/request'
5
6
  require 'googl/shorten'
6
7
  require 'googl/expand'
7
8
  require 'googl/client_login'
9
+ require 'googl/ruby_extensions'
8
10
 
9
11
  module Googl
10
12
 
11
- def self.shorten(url)
13
+ # Creates a new short URL
14
+ #
15
+ # url = Googl.shorten('http://www.zigotto.com')
16
+ # url.short_url
17
+ # => "http://goo.gl/ump4S"
18
+ #
19
+ def self.shorten(url=nil)
20
+ raise ArgumentError.new("URL to shorten is required") if url.blank?
12
21
  Googl::Shorten.new(url)
13
22
  end
14
23
 
15
- def self.expand(url)
16
- Googl::Expand.new(url)
24
+ # Expands a short URL or gets creation time and analytics
25
+ #
26
+ # url = Googl.expand('http://goo.gl/ump4S')
27
+ # url.long_url
28
+ # => "http://www.zigotto.com/"
29
+ #
30
+ # For analytics and additional information to return (using the :projection parameter)
31
+ #
32
+ # * :full => returns the creation timestamp and all available analytics
33
+ # * :analytics_clicks => returns only click counts
34
+ # * :analytics_top_strings => returns only top string counts (e.g. referrers, countries, etc)
35
+ #
36
+ # url = Googl.expand('http://goo.gl/DWDfi', :projection => :full)
37
+ #
38
+ # url.analytics.all_time.browsers.first.label
39
+ # => "Chrome"
40
+ # url.analytics.all_time.browsers.first.count
41
+ # => "11"
42
+ #
43
+ # A summary of the click analytics for the short and long URL
44
+ #
45
+ # url.analytics
46
+ #
47
+ # Analytics details for a particular window of time; counts in this object are of clicks that occurred within the most recent window of this length.
48
+ #
49
+ # url.analytics.all_time
50
+ # url.analytics.month
51
+ # url.analytics.week
52
+ # url.analytics.day
53
+ # url.analytics.two_hours
54
+ #
55
+ # Number of clicks on this short URL. Replace (*) for all_time, month, week, day or two_hours
56
+ #
57
+ # url.analytics.*.short_url_clicks
58
+ #
59
+ # Number of clicks on all goo.gl short URLs pointing to this long URL.
60
+ #
61
+ # url.analytics.*.long_url_clicks
62
+ #
63
+ # Top referring hosts, e.g. "www.google.com"; sorted by (descending) click counts. Only present if this data is available.
64
+ #
65
+ # url.analytics.*.referrers
66
+ #
67
+ # Top countries (expressed as country codes), e.g. "US" or "DE"; sorted by (descending) click counts.
68
+ #
69
+ # url.analytics.*.countries
70
+ #
71
+ # Top browsers, e.g. "Chrome"; sorted by (descending) click counts.
72
+ #
73
+ # url.analytics.*.browsers
74
+ #
75
+ # Top platforms or OSes, e.g. "Windows"; sorted by (descending) click counts.
76
+ #
77
+ # url.analytics.*.platforms
78
+ #
79
+ # For mor details, see http://code.google.com/intl/pt-BR/apis/urlshortener/v1/reference.html#resource_url
80
+ #
81
+ def self.expand(url=nil, options={})
82
+ raise ArgumentError.new("URL to expand is required") if url.blank?
83
+ options = {:shortUrl => url, :projection => nil}.merge!(options)
84
+ Googl::Expand.new(options)
17
85
  end
18
86
 
87
+ # The Google URL Shortener API ClientLogin authentication.
88
+ #
89
+ # client = Googl.client('user@gmail.com', 'my_valid_password')
90
+ #
91
+ # url = client.shorten('https://github.com/zigotto/googl')
92
+ # url.short_url
93
+ # => http://goo.gl/DWDfi
94
+ #
95
+ # Go to http://goo.gl to see URL statistics.
96
+ #
19
97
  def self.client(email, passwd)
20
98
  Googl::ClientLogin.new(email, passwd)
21
99
  end
data/lib/googl/base.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Googl
2
2
 
3
- class Base
3
+ class Base # :nodoc:
4
4
 
5
5
  private
6
6
 
@@ -8,6 +8,8 @@ module Googl
8
8
 
9
9
  attr_accessor :code
10
10
 
11
+ # The Google URL Shortener API ClientLogin authentication. See Googl.client
12
+ #
11
13
  def initialize(email, passwd)
12
14
  modify_headers('Content-Type' => 'application/x-www-form-urlencoded')
13
15
  resp = Request.post(API_URL, :body => PARAMS.merge!('Email' => email, 'Passwd' => passwd))
@@ -16,10 +18,14 @@ module Googl
16
18
  token = resp.split('=').last.gsub(/\n/, '')
17
19
  modify_headers("Authorization" => "GoogleLogin auth=#{token}")
18
20
  else
19
- resp.response
21
+ raise Exception.new("#{resp.code} #{resp.parsed_response}")
20
22
  end
21
23
  end
22
24
 
25
+ # Creates a new short URL and thus will gather unique click statistics. It shows up on the user’s dashboard at http://goo.gl.
26
+ #
27
+ # See Googl.client
28
+ #
23
29
  def shorten(url)
24
30
  Googl::Shorten.new(url)
25
31
  end
data/lib/googl/expand.rb CHANGED
@@ -2,16 +2,23 @@ module Googl
2
2
 
3
3
  class Expand
4
4
 
5
- API_URL = "https://www.googleapis.com/urlshortener/v1/url?shortUrl="
5
+ API_URL = "https://www.googleapis.com/urlshortener/v1/url"
6
6
 
7
- attr_accessor :long_url
7
+ attr_accessor :long_url, :analytics, :status
8
8
 
9
- def initialize(short_url)
10
- resp = Request.get(API_URL + short_url)
9
+ # Expands a short URL or gets creation time and analytics. See Googl.expand
10
+ #
11
+ def initialize(options={})
12
+
13
+ options.delete_if {|key, value| value.nil?}
14
+
15
+ resp = Request.get(API_URL, :query => options)
11
16
  if resp.code == 200
12
17
  @long_url = resp['longUrl']
18
+ @analytics = resp['analytics'].to_openstruct if resp.has_key?('analytics')
19
+ @status = resp['status']
13
20
  else
14
- resp.response
21
+ raise Exception.new("#{resp.code} #{resp.message}")
15
22
  end
16
23
  end
17
24
 
data/lib/googl/request.rb CHANGED
@@ -1,3 +1,3 @@
1
- class Request
1
+ class Request # :nodoc:
2
2
  include HTTParty
3
3
  end
@@ -0,0 +1,38 @@
1
+ # Rails
2
+ class String # :nodoc:
3
+ def underscore
4
+ self.gsub(/::/, '/').
5
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
6
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
7
+ tr("-", "_").
8
+ downcase
9
+ end
10
+ end
11
+
12
+ # Hash to OpenStruct
13
+ # http://www.rubyquiz.com/quiz81.html
14
+ class Object # :nodoc:
15
+ def to_openstruct
16
+ self
17
+ end
18
+ end
19
+
20
+ class Array # :nodoc:
21
+ def to_openstruct
22
+ map{ |el| el.to_openstruct }
23
+ end
24
+ end
25
+
26
+ class Hash #:nodoc:
27
+ def move(from, to)
28
+ self[to] = delete(from) if has_key?(from)
29
+ self
30
+ end
31
+ def to_openstruct
32
+ map = inject({}) do |mapped, (key, value)|
33
+ mapped[key.underscore] = value.to_openstruct
34
+ mapped.move('id', 'label')
35
+ end
36
+ OpenStruct.new(map)
37
+ end
38
+ end
data/lib/googl/shorten.rb CHANGED
@@ -6,6 +6,8 @@ module Googl
6
6
 
7
7
  attr_accessor :short_url, :long_url
8
8
 
9
+ # Creates a new short URL, see Googl.shorten
10
+ #
9
11
  def initialize(long_url)
10
12
  modify_headers('Content-Type' => 'application/json')
11
13
  options = {"longUrl" => long_url}.inspect
@@ -14,12 +16,22 @@ module Googl
14
16
  @short_url = resp['id']
15
17
  @long_url = resp['longUrl']
16
18
  else
17
- resp.response
19
+ raise Exception.new(resp.parsed_response)
18
20
  end
19
21
  end
20
22
 
23
+ # URL for QR Code
24
+ #
25
+ # url = Googl.shorten('http://goo.gl/ump4S')
26
+ # ur.qr_code
27
+ # => http://goo.gl/ump4S.qr
28
+ #
29
+ # Usage:
30
+ #
31
+ # <img src="http://goo.gl/ump4S.qr" />
32
+ #
21
33
  def qr_code
22
- short_url + ".qr" if !short_url.blank?
34
+ "#{short_url}.qr" if !short_url.blank?
23
35
  end
24
36
 
25
37
  end
data/spec/client_spec.rb CHANGED
@@ -26,10 +26,8 @@ describe Googl::ClientLogin do
26
26
 
27
27
  subject { Googl.client('my_invalid_gmail', 'my_invalid_passwod') }
28
28
 
29
- describe "#code" do
30
- it "should return 403" do
31
- subject.code.should == 403
32
- end
29
+ it "should return BadAuthentication" do
30
+ lambda { Googl.client('my_invalid_gmail', 'my_invalid_passwod') }.should raise_error(Exception, /403 Error=BadAuthentication/)
33
31
  end
34
32
 
35
33
  end
data/spec/expand_spec.rb CHANGED
@@ -5,20 +5,159 @@ describe Googl::Expand do
5
5
  before :each do
6
6
  fake_urls
7
7
  end
8
-
8
+
9
9
  context "when expand any goo.gl short URL" do
10
10
 
11
11
  it { Googl.should respond_to(:expand) }
12
12
 
13
- subject { Googl.expand('http://goo.gl/7lob') }
13
+ context "wirh invalid url" do
14
+
15
+ it "should return error 404" do
16
+ lambda { Googl.expand('http://goo.gl/blajjddkksijj') }.should raise_error(Exception, /404 Not Found/)
17
+ end
14
18
 
15
- describe "#long_url" do
16
- it "should return a long url" do
17
- subject.long_url.should == 'http://jlopes.zigotto.com.br/'
19
+ it "should return error for required url" do
20
+ lambda { Googl.expand }.should raise_error(ArgumentError, /URL to expand is required/)
18
21
  end
22
+
23
+ it "should return status REMOVED" do
24
+ Googl.expand('http://goo.gl/R7f68').status.should == 'REMOVED'
25
+ end
26
+
19
27
  end
20
28
 
21
- end
29
+ context "with valid url" do
30
+
31
+ subject { Googl.expand('http://goo.gl/7lob') }
32
+
33
+ describe "#long_url" do
34
+ it "should return a long url" do
35
+ subject.long_url.should == 'http://jlopes.zigotto.com.br/'
36
+ end
37
+ end
38
+
39
+ describe "#status" do
40
+ it "should return a status of url" do
41
+ subject.status.should == 'OK'
42
+ end
43
+ end
44
+
45
+ context "with projection" do
46
+
47
+ context "full" do
48
+
49
+ subject { Googl.expand('http://goo.gl/DWDfi', :projection => :full) }
50
+
51
+ describe "#all_time" do
52
+ let(:element) { subject.analytics.all_time }
53
+
54
+ it_should_behave_like 'a clicks'
55
+ it_should_behave_like 'a period'
56
+
57
+ it "should rename id to label" do
58
+ element.countries.first.label.should == "BR"
59
+ end
60
+ end
61
+
62
+ describe "#month" do
63
+ let(:element) { subject.analytics.month }
64
+
65
+ it_should_behave_like 'a clicks'
66
+ it_should_behave_like 'a period'
67
+ end
68
+
69
+ describe "#week" do
70
+ let(:element) { subject.analytics.week }
71
+
72
+ it_should_behave_like 'a clicks'
73
+ it_should_behave_like 'a period'
74
+ end
75
+
76
+ describe "#day" do
77
+ let(:element) { subject.analytics.day }
78
+
79
+ it_should_behave_like 'a clicks'
80
+ it_should_behave_like 'a period'
81
+ end
82
+
83
+ describe "#two_hours" do
84
+ let(:element) { subject.analytics.two_hours }
85
+
86
+ it_should_behave_like 'a clicks'
87
+ end
88
+
89
+ end
90
+
91
+ context "analytics_clicks" do
22
92
 
93
+ subject { Googl.expand('http://goo.gl/DWDfi', :projection => :analytics_clicks) }
94
+
95
+ describe "#all_time" do
96
+ let(:element) { subject.analytics.all_time }
97
+
98
+ it_should_behave_like 'a clicks'
99
+ end
100
+
101
+ describe "#month" do
102
+ let(:element) { subject.analytics.month }
103
+
104
+ it_should_behave_like 'a clicks'
105
+ end
106
+
107
+ describe "#week" do
108
+ let(:element) { subject.analytics.week }
109
+
110
+ it_should_behave_like 'a clicks'
111
+ end
112
+
113
+ describe "#day" do
114
+ let(:element) { subject.analytics.day }
115
+
116
+ it_should_behave_like 'a clicks'
117
+ end
118
+
119
+ describe "#two_hours" do
120
+ let(:element) { subject.analytics.two_hours }
121
+
122
+ it_should_behave_like 'a clicks'
123
+ end
124
+
125
+ end
126
+
127
+ context "analytics_top_strings" do
128
+
129
+ subject { Googl.expand('http://goo.gl/DWDfi', :projection => :analytics_top_strings) }
130
+
131
+ describe "#all_time" do
132
+ let(:element) { subject.analytics.all_time }
133
+
134
+ it_should_behave_like 'a period'
135
+ end
136
+
137
+ describe "#month" do
138
+ let(:element) { subject.analytics.month }
139
+
140
+ it_should_behave_like 'a period'
141
+ end
142
+
143
+ describe "#week" do
144
+ let(:element) { subject.analytics.week }
145
+
146
+ it_should_behave_like 'a period'
147
+ end
148
+
149
+ describe "#day" do
150
+ let(:element) { subject.analytics.day }
151
+
152
+ it_should_behave_like 'a period'
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ end
23
162
 
24
163
  end