statcounter 0.1.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.
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: []