bitlyr 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +146 -0
- data/LICENSE +20 -0
- data/README.md +97 -0
- data/README.rdoc +37 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/bitlyr.gemspec +154 -0
- data/lib/bitlyr/client.rb +153 -0
- data/lib/bitlyr/country.rb +10 -0
- data/lib/bitlyr/day.rb +12 -0
- data/lib/bitlyr/error.rb +13 -0
- data/lib/bitlyr/lib/core_ext/hash.rb +27 -0
- data/lib/bitlyr/lib/core_ext/string.rb +5 -0
- data/lib/bitlyr/missing_url.rb +12 -0
- data/lib/bitlyr/realtime_link.rb +16 -0
- data/lib/bitlyr/referrer.rb +12 -0
- data/lib/bitlyr/response.rb +39 -0
- data/lib/bitlyr/strategy/access_token.rb +26 -0
- data/lib/bitlyr/strategy/api_key.rb +33 -0
- data/lib/bitlyr/strategy/base.rb +26 -0
- data/lib/bitlyr/strategy/oauth.rb +50 -0
- data/lib/bitlyr/url.rb +124 -0
- data/lib/bitlyr/user.rb +100 -0
- data/lib/bitlyr.rb +39 -0
- data/test/fixtures/9uX1TE.json +1 -0
- data/test/fixtures/9uX1TEclicks.json +1 -0
- data/test/fixtures/9uX1TEclicks2.json +1 -0
- data/test/fixtures/9uX1TEinfo.json +1 -0
- data/test/fixtures/9uX1TEinfo2.json +1 -0
- data/test/fixtures/auth_fail.json +1 -0
- data/test/fixtures/auth_success.json +1 -0
- data/test/fixtures/betaworks.json +1 -0
- data/test/fixtures/betaworks2.json +1 -0
- data/test/fixtures/betaworks_jmp.json +1 -0
- data/test/fixtures/betaworks_other_user.json +1 -0
- data/test/fixtures/bitly9uX1TE.json +1 -0
- data/test/fixtures/bitly_pro_domain.json +1 -0
- data/test/fixtures/clicks_by_day.json +1 -0
- data/test/fixtures/clicks_by_day1.json +1 -0
- data/test/fixtures/clicks_by_day2.json +1 -0
- data/test/fixtures/clicks_by_minute1_url.json +1 -0
- data/test/fixtures/clicks_by_minute2_url.json +1 -0
- data/test/fixtures/clicks_by_minute_hash.json +1 -0
- data/test/fixtures/clicks_by_minute_hashes.json +1 -0
- data/test/fixtures/country_hash.json +1 -0
- data/test/fixtures/country_hash2.json +1 -0
- data/test/fixtures/country_url.json +1 -0
- data/test/fixtures/failure.json +1 -0
- data/test/fixtures/invalid_bitly_pro_domain.json +1 -0
- data/test/fixtures/invalid_credentials.json +1 -0
- data/test/fixtures/invalid_domain.json +1 -0
- data/test/fixtures/invalid_user.json +1 -0
- data/test/fixtures/invalid_x_api_key.json +1 -0
- data/test/fixtures/lookup_multiple_url.json +1 -0
- data/test/fixtures/lookup_not_real_url.json +1 -0
- data/test/fixtures/lookup_single_url.json +1 -0
- data/test/fixtures/missing_hash.json +1 -0
- data/test/fixtures/multiple_info.json +1 -0
- data/test/fixtures/multiple_url_click.json +1 -0
- data/test/fixtures/multiple_urls.json +1 -0
- data/test/fixtures/not_bitly_pro_domain.json +1 -0
- data/test/fixtures/not_found_info.json +1 -0
- data/test/fixtures/referrer_hash.json +1 -0
- data/test/fixtures/referrer_hash2.json +1 -0
- data/test/fixtures/referrer_url.json +1 -0
- data/test/fixtures/success.json +1 -0
- data/test/fixtures/url_info.json +1 -0
- data/test/fixtures/user_clicks.json +32 -0
- data/test/fixtures/user_countries.json +60 -0
- data/test/fixtures/user_realtime_links.json +15 -0
- data/test/fixtures/user_referrers.json +1 -0
- data/test/fixtures/valid_user.json +1 -0
- data/test/integration/strategy/test_api_key.rb +20 -0
- data/test/integration/strategy/test_oauth.rb +52 -0
- data/test/integration/test_client.rb +1415 -0
- data/test/integration/test_user.rb +97 -0
- data/test/test_helper.rb +54 -0
- data/test/unit/core_ext/test_hash.rb +69 -0
- data/test/unit/core_ext/test_string.rb +14 -0
- data/test/unit/strategy/test_access_token.rb +14 -0
- data/test/unit/strategy/test_api_key.rb +11 -0
- data/test/unit/strategy/test_base.rb +71 -0
- data/test/unit/strategy/test_oauth.rb +32 -0
- data/test/unit/test_bitly.rb +43 -0
- data/test/unit/test_client.rb +21 -0
- data/test/unit/test_country.rb +20 -0
- data/test/unit/test_day.rb +22 -0
- data/test/unit/test_error.rb +11 -0
- data/test/unit/test_missing.rb +34 -0
- data/test/unit/test_realtime_link.rb +30 -0
- data/test/unit/test_referrer.rb +20 -0
- data/test/unit/test_response.rb +86 -0
- data/test/unit/test_url.rb +155 -0
- data/test/unit/test_user.rb +17 -0
- metadata +233 -0
data/lib/bitlyr/error.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
class Hash
|
2
|
+
def to_query
|
3
|
+
map {|k, v|"#{k}=#{v}"}.sort * '&'
|
4
|
+
end
|
5
|
+
|
6
|
+
def stringify_keys!
|
7
|
+
keys.each do |key|
|
8
|
+
self[key.to_s] = delete(key)
|
9
|
+
end
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def stringify_keys
|
14
|
+
dup.stringify_keys!
|
15
|
+
end
|
16
|
+
|
17
|
+
def symbolize_keys!
|
18
|
+
keys.each do |key|
|
19
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def symbolize_keys
|
25
|
+
dup.symbolize_keys!
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
class MissingUrl
|
3
|
+
attr_accessor :short_url, :user_hash, :long_url, :error
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@error = options['error']
|
7
|
+
@long_url = options['long_url']
|
8
|
+
@short_url = options['short_url']
|
9
|
+
@user_hash = options['hash']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
# Day objects are created by the realtime_links method of a user
|
3
|
+
class RealtimeLink
|
4
|
+
attr_reader :clicks, :user_hash
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@clicks = options['clicks']
|
8
|
+
@user_hash = options['user_hash']
|
9
|
+
end
|
10
|
+
|
11
|
+
# A convenience method to create a Bitlyr::Url from the data
|
12
|
+
def create_url(client)
|
13
|
+
Bitlyr::Url.new(client, 'user_clicks' => clicks, 'user_hash' => user_hash)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
class Referrer
|
3
|
+
attr_reader :clicks, :referrer, :referrer_app, :url
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@url = options['url']
|
7
|
+
@clicks = options['clicks']
|
8
|
+
@referrer = options['referrer']
|
9
|
+
@referrer_app = options['referrer_app']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
class Response
|
3
|
+
REASONS = { "OK" => "OK",
|
4
|
+
"INVALID_URI" => "Invalid URI" }.freeze
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@response = response
|
8
|
+
end
|
9
|
+
|
10
|
+
def success?
|
11
|
+
parsed['status_code'] == 200
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
parsed['status_code']
|
16
|
+
end
|
17
|
+
|
18
|
+
def reason
|
19
|
+
status = parsed['status_txt']
|
20
|
+
REASONS[ status ] || status.split('_').map(&:capitalize) * ' '
|
21
|
+
end
|
22
|
+
|
23
|
+
def body
|
24
|
+
parsed['data']
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def parsed
|
29
|
+
case @response
|
30
|
+
when OAuth2::Response
|
31
|
+
@response.parsed
|
32
|
+
when HTTParty::Response
|
33
|
+
@response.parsed_response
|
34
|
+
else
|
35
|
+
raise "Unsupported Response type: #{@response.class}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
module Strategy
|
3
|
+
class AccessToken < Base
|
4
|
+
extend Forwardable
|
5
|
+
delegate [ :client, :[] ] => :access_token
|
6
|
+
|
7
|
+
def initialize(access_token)
|
8
|
+
@access_token = access_token
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
# Make a request from the access token
|
13
|
+
def run_request(verb, method, options={})
|
14
|
+
access_token.send(verb, method, :params => authorization_params.merge(options), :parse => :json)
|
15
|
+
end
|
16
|
+
|
17
|
+
def access_token
|
18
|
+
@access_token
|
19
|
+
end
|
20
|
+
|
21
|
+
def authorization_params
|
22
|
+
{ :access_token => access_token.token }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
module Strategy
|
3
|
+
class ApiKey < Base
|
4
|
+
include HTTParty
|
5
|
+
base_uri 'http://api.bit.ly/v3/'
|
6
|
+
query_string_normalizer proc { |query|
|
7
|
+
query.map do |key, value|
|
8
|
+
case value
|
9
|
+
when Array
|
10
|
+
value.map { |v| "#{key}=#{CGI.escape( v.to_s )}" }
|
11
|
+
else
|
12
|
+
"#{key}=#{CGI.escape( value.to_s )}"
|
13
|
+
end
|
14
|
+
end.compact.join('&')
|
15
|
+
}
|
16
|
+
|
17
|
+
def initialize(login, api_key)
|
18
|
+
@login = login
|
19
|
+
@api_key = api_key
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# Make a request using the login and apiKey
|
24
|
+
def run_request(verb, method, options={})
|
25
|
+
self.class.send(verb, "/#{method}", :query => authorization_params.merge(options))
|
26
|
+
end
|
27
|
+
|
28
|
+
def authorization_params
|
29
|
+
{ :login => @login, :apiKey => @api_key }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
module Strategy
|
3
|
+
class Base
|
4
|
+
def request(*args)
|
5
|
+
response = Bitlyr::Response.new(run_request(*args))
|
6
|
+
if response.success?
|
7
|
+
response.body
|
8
|
+
else
|
9
|
+
raise BitlyrError.new(response)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Validates a login and api key
|
14
|
+
def validate(x_login, x_api_key)
|
15
|
+
response = request(:get, :validate, :x_login => x_login, :x_apiKey => x_api_key)
|
16
|
+
return response['valid'] == 1
|
17
|
+
end
|
18
|
+
alias :valid? :validate
|
19
|
+
|
20
|
+
private
|
21
|
+
def run_request(*args)
|
22
|
+
raise "Define this in the subclass"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
module Strategy
|
3
|
+
class OAuth < Base
|
4
|
+
extend Forwardable
|
5
|
+
delegate [ :run_request ] => :access_token
|
6
|
+
|
7
|
+
BASE_URL = 'https://api-ssl.bit.ly/v3/'
|
8
|
+
|
9
|
+
def initialize(consumer_token, consumer_secret)
|
10
|
+
@consumer_token = consumer_token
|
11
|
+
@consumer_secret = consumer_secret
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the url to redirect a user to, pass the url you want the user
|
15
|
+
# to be redirected back to.
|
16
|
+
def authorize_url(redirect_url)
|
17
|
+
client.auth_code.authorize_url(:redirect_uri => redirect_url)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get the access token. You must pass the exact same redirect_url passed
|
21
|
+
# to the authorize_url method
|
22
|
+
def get_access_token_from_code(code, redirect_url)
|
23
|
+
access_token = client.auth_code.get_token(code, :redirect_uri => redirect_url, :parse => :query)
|
24
|
+
Bitlyr::Strategy::AccessToken.new(access_token)
|
25
|
+
end
|
26
|
+
|
27
|
+
# If you already have a user token, this method gets the access token
|
28
|
+
def get_access_token_from_token(token, params={})
|
29
|
+
params.stringify_keys!
|
30
|
+
access_token = ::OAuth2::AccessToken.new(client, token, params)
|
31
|
+
Bitlyr::Strategy::AccessToken.new(access_token)
|
32
|
+
end
|
33
|
+
|
34
|
+
# If you already have a user token, this method sets the access token
|
35
|
+
def set_access_token_from_token!(token, params={})
|
36
|
+
@access_token ||= get_access_token_from_token(token, params)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
# Get the OAuth 2 client
|
41
|
+
def client
|
42
|
+
@client ||= ::OAuth2::Client.new(@consumer_token, @consumer_secret, :site => BASE_URL, :token_url => '/oauth/access_token')
|
43
|
+
end
|
44
|
+
|
45
|
+
def access_token
|
46
|
+
@access_token
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/bitlyr/url.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
|
3
|
+
# Url objects should only be created by the client object as it collects the correct information
|
4
|
+
# from the API.
|
5
|
+
class Url
|
6
|
+
attr_reader :short_url, :long_url, :user_hash, :global_hash, :referrers, :countries
|
7
|
+
|
8
|
+
# Initialize with a bitly client and optional hash to fill in the details for the url.
|
9
|
+
def initialize(client, options = {})
|
10
|
+
@client = client
|
11
|
+
@title = options['title'] || '' if options.key?('title')
|
12
|
+
@new_hash = (options['new_hash'] == 1)
|
13
|
+
@long_url = options['long_url']
|
14
|
+
@user_hash = options['hash'] || options['user_hash']
|
15
|
+
@short_url = options['url'] || options['short_url'] || "http://bit.ly/#{@user_hash}"
|
16
|
+
@created_by = options['created_by']
|
17
|
+
@global_hash = options['global_hash']
|
18
|
+
@user_clicks = options['user_clicks']
|
19
|
+
@global_clicks = options['global_clicks']
|
20
|
+
|
21
|
+
@referrers = options['referrers'].map{|referrer| Bitlyr::Referrer.new(referrer) } if options['referrers']
|
22
|
+
@countries = options['countries'].map{|country| Bitlyr::Country.new(country) } if options['countries']
|
23
|
+
|
24
|
+
if options['clicks'] && options['clicks'][0].is_a?(Hash)
|
25
|
+
@clicks_by_day = options['clicks'].map{|day| Bitlyr::Day.new(day)}
|
26
|
+
else
|
27
|
+
@clicks_by_minute = options['clicks']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns true if the user hash was created first for this call
|
32
|
+
def new_hash?
|
33
|
+
@new_hash
|
34
|
+
end
|
35
|
+
|
36
|
+
# If the url already has click statistics, returns the user clicks.
|
37
|
+
# IF there are no click statistics or <tt>:force => true</tt> is passed,
|
38
|
+
# updates the stats and returns the user clicks
|
39
|
+
def user_clicks(options={})
|
40
|
+
update_clicks_data if @user_clicks.nil? || options[:force]
|
41
|
+
@user_clicks
|
42
|
+
end
|
43
|
+
|
44
|
+
# If the url already has click statistics, returns the global clicks.
|
45
|
+
# IF there are no click statistics or <tt>:force => true</tt> is passed,
|
46
|
+
# updates the stats and returns the global clicks
|
47
|
+
def global_clicks(options={})
|
48
|
+
update_clicks_data if @global_clicks.nil? || options[:force]
|
49
|
+
@global_clicks
|
50
|
+
end
|
51
|
+
|
52
|
+
# If the url already has the title, return it.
|
53
|
+
# IF there is no title or <tt>:force => true</tt> is passed,
|
54
|
+
# updates the info and returns the title
|
55
|
+
def title(options={})
|
56
|
+
update_info if @title.nil? || options[:force]
|
57
|
+
@title
|
58
|
+
end
|
59
|
+
|
60
|
+
# If the url already has the creator, return it.
|
61
|
+
# IF there is no creator or <tt>:force => true</tt> is passed,
|
62
|
+
# updates the info and returns the creator
|
63
|
+
def created_by(options={})
|
64
|
+
update_info if @created_by.nil? || options[:force]
|
65
|
+
@created_by
|
66
|
+
end
|
67
|
+
|
68
|
+
# If the url already has referrer data, return it.
|
69
|
+
# IF there is no referrer or <tt>:force => true</tt> is passed,
|
70
|
+
# updates the referrers and returns them
|
71
|
+
def referrers(options={})
|
72
|
+
update_referrers if @referrers.nil? || options[:force]
|
73
|
+
@referrers
|
74
|
+
end
|
75
|
+
|
76
|
+
# If the url already has country data, return it.
|
77
|
+
# IF there is no country or <tt>:force => true</tt> is passed,
|
78
|
+
# updates the countries and returns them
|
79
|
+
def countries(options={})
|
80
|
+
update_countries if @countries.nil? || options[:force]
|
81
|
+
@countries
|
82
|
+
end
|
83
|
+
|
84
|
+
def clicks_by_minute(options={})
|
85
|
+
if @clicks_by_minute.nil? || options[:force]
|
86
|
+
full_url = @client.clicks_by_minute(@user_hash || @short_url)
|
87
|
+
@clicks_by_minute = full_url.clicks_by_minute
|
88
|
+
end
|
89
|
+
@clicks_by_minute
|
90
|
+
end
|
91
|
+
|
92
|
+
def clicks_by_day(options={})
|
93
|
+
if @clicks_by_day.nil? || options[:force]
|
94
|
+
full_url = @client.clicks_by_day(@user_hash || @short_url)
|
95
|
+
@clicks_by_day = full_url.clicks_by_day
|
96
|
+
end
|
97
|
+
@clicks_by_day
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def update_clicks_data
|
103
|
+
full_url = @client.clicks(@user_hash || @short_url)
|
104
|
+
@global_clicks = full_url.global_clicks
|
105
|
+
@user_clicks = full_url.user_clicks
|
106
|
+
end
|
107
|
+
|
108
|
+
def update_info
|
109
|
+
full_url = @client.info(@user_hash || @short_url)
|
110
|
+
@created_by = full_url.created_by
|
111
|
+
@title = full_url.title
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_referrers
|
115
|
+
full_url = @client.referrers(@user_hash || @short_url)
|
116
|
+
@referrers = full_url.referrers
|
117
|
+
end
|
118
|
+
|
119
|
+
def update_countries
|
120
|
+
full_url = @client.countries(@user_hash || @short_url)
|
121
|
+
@countries = full_url.countries
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/bitlyr/user.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Bitlyr
|
2
|
+
|
3
|
+
# A user requires an oauth access token. The flow is as follows:
|
4
|
+
#
|
5
|
+
# o = Bitlyr::Strategy::OAuth.new(consumer_token, consumer_secret)
|
6
|
+
# o.authorize_url(redirect_url)
|
7
|
+
# #=> "https://bit.ly/oauth/authorize?client_id=#{consumer_token}&type=web_server&redirect_uri=http%3A%2F%2Ftest.local%2Fbitlyr%2Fauth"
|
8
|
+
# Redirect your users to this url, when they authorize your application
|
9
|
+
# they will be redirected to the url you provided with a code parameter.
|
10
|
+
# Use that parameter, and the exact same redirect url as follows:
|
11
|
+
#
|
12
|
+
# o.get_access_token_from_code(params[:code], redirect_url)
|
13
|
+
# #=> #<Bitlyr::AccessToken ...>
|
14
|
+
#
|
15
|
+
# Then use that access token to create your user object.
|
16
|
+
#
|
17
|
+
# u=Bitlyr::User.new(o.access_token)
|
18
|
+
class User
|
19
|
+
|
20
|
+
def initialize(access_token)
|
21
|
+
@access_token = access_token
|
22
|
+
end
|
23
|
+
|
24
|
+
# OAuth 2 endpoint that provides a list of top referrers (up to 500 per
|
25
|
+
# day) for a given user’s bit.ly links, and the number of clicks per referrer.
|
26
|
+
#
|
27
|
+
# http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/referrers
|
28
|
+
def referrers(options={})
|
29
|
+
if @referrers.nil? || options.delete(:force)
|
30
|
+
@referrers = get_method(:referrers, Bitlyr::Referrer, options)
|
31
|
+
end
|
32
|
+
@referrers
|
33
|
+
end
|
34
|
+
|
35
|
+
# OAuth 2 endpoint that provides a list of countries from which clicks
|
36
|
+
# on a given user’s bit.ly links are originating, and the number of clicks per country.
|
37
|
+
#
|
38
|
+
# http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/countries
|
39
|
+
def countries(options={})
|
40
|
+
if @countries.nil? || options.delete(:force)
|
41
|
+
@countries = get_method(:countries, Bitlyr::Country, options)
|
42
|
+
end
|
43
|
+
@countries
|
44
|
+
end
|
45
|
+
|
46
|
+
# OAuth 2 endpoint that provides a given user’s 100 most popular links
|
47
|
+
# based on click traffic in the past hour, and the number of clicks per link.
|
48
|
+
#
|
49
|
+
# http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/realtime_links
|
50
|
+
def realtime_links(options={})
|
51
|
+
if @realtime_links.nil? || options.delete(:force)
|
52
|
+
result = get(:realtime_links, options)
|
53
|
+
@realtime_links = result['realtime_links'].map { |rs| Bitlyr::RealtimeLink.new(rs) }
|
54
|
+
end
|
55
|
+
@realtime_links
|
56
|
+
end
|
57
|
+
|
58
|
+
# OAuth 2 endpoint that provides the total clicks per day on a user’s bit.ly links.
|
59
|
+
#
|
60
|
+
# http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/clicks
|
61
|
+
def clicks(options={})
|
62
|
+
get_clicks(options)
|
63
|
+
@clicks
|
64
|
+
end
|
65
|
+
|
66
|
+
# Displays the total clicks returned from the clicks method.
|
67
|
+
def total_clicks(options={})
|
68
|
+
get_clicks(options)
|
69
|
+
@total_clicks
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a Bitly Client using the credentials of the user.
|
73
|
+
def client
|
74
|
+
@client ||= Bitlyr::Client.new(@access_token)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def get_method(method, klass, options)
|
80
|
+
result = get(method, options)
|
81
|
+
result[method.to_s].map do |rs|
|
82
|
+
rs.map do |obj|
|
83
|
+
klass.new(obj)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_clicks(options={})
|
89
|
+
if @clicks.nil? || options.delete(:force)
|
90
|
+
result = get(:clicks, options)
|
91
|
+
@clicks = result['clicks'].map { |rs| Bitlyr::Day.new(rs) }
|
92
|
+
@total_clicks = result['total_clicks']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def get(method, options)
|
97
|
+
@access_token.request(:get, "user/#{method}", options)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/bitlyr.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require 'oauth2'
|
5
|
+
require 'cgi'
|
6
|
+
require 'forwardable'
|
7
|
+
|
8
|
+
module Bitlyr
|
9
|
+
def self.new(params = {})
|
10
|
+
params.symbolize_keys!
|
11
|
+
if params.key?(:client_id) && params.key?(:client_secret)
|
12
|
+
strategy = Bitlyr::Strategy::OAuth.new(params[:client_id], params[:client_secret])
|
13
|
+
strategy.get_access_token_from_token(params[:token]) if params[:token]
|
14
|
+
Bitlyr::Client.new strategy
|
15
|
+
elsif params.key?(:login) && params.key?(:api_key)
|
16
|
+
Bitlyr::Client.new Bitlyr::Strategy::ApiKey.new(params[:login], params[:api_key])
|
17
|
+
else
|
18
|
+
raise "requires a login and apiKey or client id and client secret"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'bitlyr/client'
|
24
|
+
require 'bitlyr/country'
|
25
|
+
require 'bitlyr/day'
|
26
|
+
require 'bitlyr/error'
|
27
|
+
require 'bitlyr/missing_url'
|
28
|
+
require 'bitlyr/realtime_link'
|
29
|
+
require 'bitlyr/referrer'
|
30
|
+
require 'bitlyr/response'
|
31
|
+
require 'bitlyr/url'
|
32
|
+
require 'bitlyr/user'
|
33
|
+
require 'bitlyr/strategy/base'
|
34
|
+
require 'bitlyr/strategy/access_token'
|
35
|
+
require 'bitlyr/strategy/api_key'
|
36
|
+
require 'bitlyr/strategy/oauth'
|
37
|
+
|
38
|
+
require 'bitlyr/lib/core_ext/hash'
|
39
|
+
require 'bitlyr/lib/core_ext/string'
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ { "hash": "9uX1TE", "long_url": "http:\/\/betaworks.com\/", "user_hash": "9uX1TE", "global_hash": "18H1ET" } ] } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks": [{"user_clicks": 0, "global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "global_clicks": 81}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks": [{"user_clicks": 1, "global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "global_clicks": 82}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"info": [{"global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "created_by": "philnash", "title": "A title"}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"info": [{"global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "created_by": "philnash2", "title": "A New Title"}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"data":{"authenticate":{"successful": false}},"status_code": 200,"status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"data":{"authenticate":{"api_key": "R_0da49e0a9118ff35f52f629d2d71bf07","successful": true,"username": "bitlyapidemo"}},"status_code": 200,"status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 1 } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 0 } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/j.mp\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 0 } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/cWJlxM", "hash": "cWJlxM", "global_hash": "18H1ET", "new_hash": 1 } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ { "short_url": "http:\/\/bit.ly\/9uX1TE", "long_url": "http:\/\/betaworks.com\/", "user_hash": "9uX1TE", "global_hash": "18H1ET" } ] } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"domain": "nyti.ms", "bitly_pro_domain": true}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}, {"global_hash": "dpC5ns", "hash": "dvxi6W", "user_hash": "dvxi6W", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 0, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 2, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_minute": [{"short_url": "http://j.mp/9DguyN", "global_hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_minute": [{"short_url": "http://j.mp/9DguyN", "global_hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_minute": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"clicks_by_minute": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {"global_hash": "dpC5ns", "hash": "dvxi6W", "user_hash": "dvxi6W", "clicks": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"global_hash": "djZ9g4", "hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 58}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"global_hash": "djZ9g4", "hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 59}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 200, "data": {"short_url": "http://bit.ly/djZ9g4", "global_hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 58}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 400, "status_txt": "Bad Request", "data": { } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status_code": 500, "data": null, "status_txt": "INVALID_DOMAIN"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "data": [ ], "status_code": 500, "status_txt": "INVALID_LOGIN" }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "data": [ ], "status_code": 500, "status_txt": "INVALID_ARG_DOMAIN" }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "status_code": 200, "status_txt": "OK", "data": { "valid": 0 } }
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "data": [ ], "status_code": 500, "status_txt": "INVALID_X_APIKEY" }
|