googl 0.2.0 → 0.3.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.
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