statcounter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6a07f45e20aad61cd55786e84bcb777ecb6cffb6
4
+ data.tar.gz: 0b47936f0a2454d253e5e896572027c4df974c81
5
+ SHA512:
6
+ metadata.gz: 67f0ac03e7fa837d29d93a1648f75c7a50216a7485a841b4d8a8a9b0b5740ed4ab072476beb068dfe614a047382375ae7bdfdef0ab631fee7116cf2acb86cb6e
7
+ data.tar.gz: f077806e3fd8d8b7005e827f3dd87c112ee838bafc7b817e0434461be6dee54fd4d73c81af9c20d11653b2d808cbd205f5f038ef019b0b15770097657eac0e63
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ LineLength:
2
+ Max: 100
3
+
4
+ Documentation:
5
+ Enabled: false
6
+
7
+ SignalException:
8
+ EnforcedStyle: only_raise
9
+
10
+ ClassAndModuleChildren:
11
+ Enabled: false
12
+
13
+ TrailingCommaInLiteral:
14
+ Enabled: false
15
+
16
+ TrailingCommaInArguments:
17
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ script: 'bundle exec rspec'
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # Statcounter
2
+
3
+ TODO: Add readme
4
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task default: :spec
@@ -0,0 +1,28 @@
1
+ require 'statcounter/version'
2
+ require 'statcounter/client'
3
+ require 'statcounter/configuration'
4
+ require 'statcounter/errors'
5
+ require 'statcounter/params_encoder'
6
+ require 'statcounter/projects'
7
+
8
+ module Statcounter
9
+ API_URL = 'http://api.statcounter.com/'.freeze
10
+
11
+ module_function
12
+
13
+ def configure
14
+ yield(config)
15
+ end
16
+
17
+ def config
18
+ @config ||= Configuration.new
19
+ end
20
+
21
+ def client
22
+ @client ||= Client.new
23
+ end
24
+
25
+ def default_credentials
26
+ @default_credentials ||= { username: config.username, secret: config.secret }
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module Statcounter
5
+ class Client
6
+ STATUS_FAIL = 'fail'.freeze
7
+
8
+ def get(path, params: {}, credentials: nil)
9
+ response = connection(credentials).get(path, params)
10
+ body = JSON.parse(response.body, symbolize_names: true)
11
+
12
+ if body[:@attributes][:status] == STATUS_FAIL
13
+ raise Error, body[:error][0][:description]
14
+ end
15
+
16
+ body
17
+ rescue Faraday::ClientError => e
18
+ raise Error, 'Server could not process your request'
19
+ end
20
+
21
+ private
22
+
23
+ def connection(credentials)
24
+ @connection ||= build_connection(credentials)
25
+ end
26
+
27
+ def build_connection(credentials)
28
+ Faraday.new(url: Statcounter::API_URL) do |c|
29
+ c.adapter Faraday.default_adapter
30
+ c.use FaradayMiddleware::FollowRedirects, limit: 3
31
+ c.response :json, content_type: /\bjson$/
32
+ c.response :raise_error
33
+ c.options[:timeout] = config.timeout
34
+ c.options[:open_timeout] = config.timeout
35
+ c.options[:params_encoder] = Statcounter::ParamsEncoder.new(credentials)
36
+ end
37
+ end
38
+
39
+ def config
40
+ Statcounter.config
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ module Statcounter
2
+ class Configuration
3
+ attr_accessor :username, :secret, :timeout
4
+
5
+ def timeout
6
+ @timeout ||= 60
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Statcounter
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,38 @@
1
+ module Statcounter
2
+ class ParamsEncoder
3
+ def initialize(credentials)
4
+ @credentials = credentials || Statcounter.default_credentials
5
+ end
6
+
7
+ def encode(params)
8
+ return unless params.is_a? Hash
9
+
10
+ query_string = hash_to_query_string(request_params(params))
11
+ sha1 = generate_sha1(query_string)
12
+
13
+ "#{query_string}&sha1=#{sha1}"
14
+ end
15
+
16
+ private
17
+
18
+ def request_params(params)
19
+ params[:vn] ||= 3
20
+ params[:t] ||= Time.now.to_i
21
+ params[:u] ||= @credentials[:username]
22
+ params[:f] ||= :json
23
+ params
24
+ end
25
+
26
+ def hash_to_query_string(hash)
27
+ hash.each_with_object('') do |(key, value), query_string|
28
+ next value.each { |v| query_string << "#{key}=#{v}&" } if value.is_a? Array
29
+
30
+ query_string << "#{key}=#{value}&"
31
+ end.chomp('&')
32
+ end
33
+
34
+ def generate_sha1(query_string)
35
+ Digest::SHA1.hexdigest('?' + query_string + @credentials[:secret])
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ module Statcounter
2
+ class Projects
3
+ def self.all(credentials: nil)
4
+ response = Statcounter.client.get('user_projects', credentials: credentials)
5
+ response[:sc_data]
6
+ end
7
+
8
+ def self.find(project_id, credentials: nil)
9
+ params = { pi: project_id }
10
+ response = Statcounter.client.get('select_project', params: params, credentials: credentials)
11
+ response[:sc_data][0]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Statcounter
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "@attributes": {
3
+ "status": "fail"
4
+ },
5
+ "error": [{
6
+ "code": "CODE",
7
+ "description": "ERROR_DESCRIPTION"
8
+ }]
9
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "@attributes": {
3
+ "status":"ok"
4
+ },
5
+ "sc_data":[{
6
+ "project_name":"PROJECT_NAME",
7
+ "log_size": "LOG_SIZE",
8
+ "timezone": "TIME_ZONE",
9
+ "url":"URL",
10
+ "log_oldest_entry":"LOG_OLDEST_ENTRY",
11
+ "log_latest_entry":"LOG_LATEST_ENTRY",
12
+ "created_at":"CREATED_AT"
13
+ }]
14
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "@attributes":{
3
+ "status":"ok"
4
+ },
5
+ "sc_data":[
6
+ {
7
+ "project_id":"1000000",
8
+ "project_name":"mywebsite1.com",
9
+ "url":"https:\/\/www.mywebsite1.com",
10
+ "project_group_id":"2",
11
+ "project_group_name":"Websites 2",
12
+ "hidden_group":"0"
13
+ },
14
+ {
15
+ "project_id":"1000001",
16
+ "project_name":"mywebsite2.com",
17
+ "url":"https:\/\/www.mywebsite2.com",
18
+ "project_group_id":"2",
19
+ "project_group_name":"Websites 2",
20
+ "hidden_group":"0"
21
+ },
22
+ {
23
+ "project_id":"1000002",
24
+ "project_name":"mywebsite3.com",
25
+ "url":"https:\/\/www.mywebsite3.com\/",
26
+ "project_group_id":"1",
27
+ "project_group_name":"Websites 1",
28
+ "hidden_group":"0"
29
+ },
30
+ {
31
+ "project_id":"1000003",
32
+ "project_name":"mywebsite4.com",
33
+ "url":"https:\/\/www.mywebsite4.com",
34
+ "project_group_id":"1",
35
+ "project_group_name":"Websites 1",
36
+ "hidden_group":"0"
37
+ },
38
+ {
39
+ "project_id":"1000004",
40
+ "project_name":"mywebsite5.com",
41
+ "url":"https:\/\/www.mywebsite5.com",
42
+ "project_group_id":"1",
43
+ "project_group_name":"Websites 1",
44
+ "hidden_group":"0"
45
+ }
46
+ ]
47
+ }
@@ -0,0 +1,36 @@
1
+ RSpec.describe Statcounter::Client do
2
+ describe '#get' do
3
+ subject { described_class.new.get('user_projects', credentials: default_credentials) }
4
+ let(:response_body) { 'spec/assets/user_projects.json' }
5
+ let(:response_status) { 200 }
6
+
7
+ before do
8
+ Timecop.freeze(Time.at(1466614800))
9
+ stub_request(:get, 'http://api.statcounter.com/user_projects?f=json&sha1=06290316a4a4aa9911db04b42294609a8a4694e4&t=1466614800&u=john_brown&vn=3')
10
+ .to_return(body: File.read(response_body), status: response_status)
11
+ end
12
+
13
+ shared_examples 'raises error' do
14
+ it 'raises an exception' do
15
+ expect { subject }.to raise_error(Statcounter::Error)
16
+ end
17
+ end
18
+
19
+ it 'returns body' do
20
+ expect(subject[:sc_data]).to be_instance_of Array
21
+ expect(subject[:sc_data].first).to include(:project_id)
22
+ end
23
+
24
+ context 'when failure response' do
25
+ let(:response_body) { 'spec/assets/failure.json' }
26
+
27
+ it_behaves_like 'raises error'
28
+ end
29
+
30
+ context 'when client error' do
31
+ let(:response_status) { 500 }
32
+
33
+ it_behaves_like 'raises error'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ RSpec.describe Statcounter::ParamsEncoder do
2
+ subject(:params_encoder) { described_class.new(credentials) }
3
+ let(:credentials) { nil }
4
+
5
+ before { allow(Statcounter).to receive(:default_credentials).and_return(default_credentials) }
6
+
7
+ describe '#encode' do
8
+ subject(:query) { params_encoder.encode(params) }
9
+ let(:params) { { test: 1 } }
10
+ let(:parsed_query) { Rack::Utils.parse_nested_query(subject) }
11
+
12
+ before { Timecop.freeze }
13
+
14
+ it 'appends default params' do
15
+ expect(parsed_query).to include(
16
+ 'vn' => '3',
17
+ 't' => Time.now.to_i.to_s,
18
+ 'f' => 'json',
19
+ 'u' => default_credentials[:username],
20
+ 'test' => '1',
21
+ )
22
+
23
+ expect(parsed_query).to include('sha1')
24
+ end
25
+
26
+ context 'when default param is given' do
27
+ let(:params) { { vn: 4 } }
28
+
29
+ it 'uses given param value' do
30
+ expect(parsed_query).to include('vn' => '4')
31
+ end
32
+ end
33
+
34
+ context 'when custom credentials given' do
35
+ let(:credentials) { { username: username, secret: secret } }
36
+ let(:username) { 'tom_brown' }
37
+ let(:secret) { 'toms_little_secret' }
38
+
39
+ it 'uses default credentials' do
40
+ expect(parsed_query).to include('u' => username)
41
+ end
42
+ end
43
+
44
+ context 'when param is array' do
45
+ let(:params) { { test: [1, 2] } }
46
+
47
+ it 'builds flat query' do
48
+ expect(query).to match(/test=1&test=2/)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ RSpec.describe Statcounter::Projects do
2
+ before { Timecop.freeze(Time.at(1466614800)) }
3
+
4
+ describe '.all' do
5
+ subject { described_class.all(credentials: default_credentials) }
6
+
7
+ before do
8
+ stub_request(:get, 'http://api.statcounter.com/user_projects?f=json&sha1=06290316a4a4aa9911db04b42294609a8a4694e4&t=1466614800&u=john_brown&vn=3')
9
+ .to_return(body: File.read('spec/assets/user_projects.json'))
10
+ end
11
+
12
+ it 'returns projects array' do
13
+ expect(subject).to be_instance_of Array
14
+ expect(subject.first).to include(
15
+ :project_id,
16
+ :project_name,
17
+ :url,
18
+ :project_group_id,
19
+ :project_group_name,
20
+ :hidden_group,
21
+ )
22
+ end
23
+ end
24
+
25
+ describe '.find' do
26
+ subject { described_class.find(project_id, credentials: default_credentials) }
27
+ let(:project_id) { 1 }
28
+
29
+ before do
30
+ stub_request(:get, 'http://api.statcounter.com/select_project?f=json&pi=1&sha1=229b79fa8787e1a8bf8d09c3a1cbec96a8219364&t=1466614800&u=john_brown&vn=3')
31
+ .to_return(body: File.read('spec/assets/select_project.json'))
32
+ end
33
+
34
+ it 'returns project details' do
35
+ expect(subject).to be_instance_of Hash
36
+ expect(subject).to include(
37
+ :project_name,
38
+ :log_size,
39
+ :timezone,
40
+ :url,
41
+ :log_oldest_entry,
42
+ :log_latest_entry,
43
+ :created_at,
44
+ )
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'rspec'
4
+ require 'statcounter'
5
+ require 'timecop'
6
+ require 'rack'
7
+ require 'webmock/rspec'
8
+
9
+ WebMock.disable_net_connect!
10
+
11
+ Dir['./spec/support/**/*.rb'].each { |f| require(f) }
12
+
13
+ RSpec.configure do |config|
14
+ config.order = :random
15
+ end
@@ -0,0 +1,9 @@
1
+ module RequestStubbing
2
+ def default_credentials
3
+ { username: 'john_brown', secret: 'johns_little_secret' }
4
+ end
5
+ end
6
+
7
+ RSpec.configure do |config|
8
+ config.include RequestStubbing
9
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'statcounter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'statcounter'
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.version = Statcounter::VERSION
10
+ spec.authors = ['Šarūnas Kūjalis']
11
+ spec.email = ['sarjalis@gmail.com']
12
+ spec.summary = 'Ruby Statcounter API wrapper'
13
+ spec.homepage = 'https://github.com/ksarunas/statcounter'
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.test_files = `git ls-files -- {spec}/*`.split("\n")
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_dependency 'faraday', '~> 0.9'
20
+ spec.add_dependency 'faraday_middleware', '~> 0.10'
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.11'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'timecop', '~> 0.8.1'
26
+ spec.add_development_dependency 'rack', '~> 1.6.4'
27
+ spec.add_development_dependency 'webmock', '~> 1.24'
28
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statcounter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - "Šarūnas Kūjalis"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.6.4
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.4
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.24'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.24'
125
+ description:
126
+ email:
127
+ - sarjalis@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".rubocop.yml"
135
+ - ".travis.yml"
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - lib/statcounter.rb
140
+ - lib/statcounter/client.rb
141
+ - lib/statcounter/configuration.rb
142
+ - lib/statcounter/errors.rb
143
+ - lib/statcounter/params_encoder.rb
144
+ - lib/statcounter/projects.rb
145
+ - lib/statcounter/version.rb
146
+ - spec/assets/failure.json
147
+ - spec/assets/select_project.json
148
+ - spec/assets/user_projects.json
149
+ - spec/client_spec.rb
150
+ - spec/params_encoder_spec.rb
151
+ - spec/projects_spec.rb
152
+ - spec/spec_helper.rb
153
+ - spec/support/request_stubbing.rb
154
+ - statcounter.gemspec
155
+ homepage: https://github.com/ksarunas/statcounter
156
+ licenses: []
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 2.4.5
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Ruby Statcounter API wrapper
178
+ test_files: []