plausible_api 0.1.10 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +26 -1
- data/examples/basic.rb +5 -0
- data/examples/event.rb +23 -0
- data/lib/plausible_api/client.rb +32 -25
- data/lib/plausible_api/configuration.rb +26 -0
- data/lib/plausible_api/event/base.rb +31 -0
- data/lib/plausible_api/event/post.rb +101 -0
- data/lib/plausible_api/stats/aggregate.rb +2 -2
- data/lib/plausible_api/stats/base.rb +20 -0
- data/lib/plausible_api/stats/breakdown.rb +2 -2
- data/lib/plausible_api/stats/timeseries.rb +2 -2
- data/lib/plausible_api/version.rb +1 -1
- data/lib/plausible_api.rb +15 -2
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3164447cd3eac3c7a7cbf0c7e7cccd36eaf2bd195966283fc0da4ad023b7b21e
|
4
|
+
data.tar.gz: 34acbb3e21f80bd3537d4273b6555dea0da7e4fdb5cf3dcbf8bede5d477e8ce0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03273d140652347b6b282d94ff0c6294746fc7bd410dcaa1feab54a627d80ed64fa8eb1c9c56c0a3a0e3e2a95cb9cda755501987f46a3b795cb9dc2862aa1e01
|
7
|
+
data.tar.gz: b6e84673b3763918a28488c717e3be089107571ecb1b357b4d7e2b1ded589b68eade9d65f615e70a6655b4571a73c7bf767a0af3363b6ca90631cd0810ea91e8
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ gem 'plausible_api'
|
|
9
9
|
```
|
10
10
|
Then you need to initialize a Client with your `site_id` (the domain) and your `token`.
|
11
11
|
```rb
|
12
|
-
c = PlausibleApi::Client.new('dailytics.com', '123123')
|
12
|
+
c = PlausibleApi::Client.new('dailytics.com', '123123')
|
13
13
|
|
14
14
|
# Test if the site and token are valid
|
15
15
|
c.valid?
|
@@ -73,6 +73,31 @@ c.realtime_visitors
|
|
73
73
|
=> 13
|
74
74
|
```
|
75
75
|
|
76
|
+
### Events
|
77
|
+
|
78
|
+
You can send an event like this:
|
79
|
+
|
80
|
+
```rb
|
81
|
+
# Example using Rack::Request in Rails for user_agent and ip.
|
82
|
+
c.event({
|
83
|
+
name: "signup",
|
84
|
+
url: 'https://dailytics.com/users/new',
|
85
|
+
user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.3",
|
86
|
+
ip: "127.0.0.1"
|
87
|
+
})
|
88
|
+
```
|
89
|
+
|
90
|
+
|
91
|
+
### Self-hosted Plausible instances
|
92
|
+
|
93
|
+
If you are using a self-hosted Plausible instance, you can set the `base_url` before initializing the client. On a Ruby on Rails app, you can add this to an initializer like `config/initializers/plausible.rb`
|
94
|
+
|
95
|
+
```rb
|
96
|
+
# Do not include a trailing slash
|
97
|
+
PlausibleApi.configure do |config|
|
98
|
+
config.base_url = "https://your-plausible-instance.com"
|
99
|
+
end
|
100
|
+
```
|
76
101
|
|
77
102
|
## Development
|
78
103
|
|
data/examples/basic.rb
ADDED
data/examples/event.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'plausible_api'
|
3
|
+
|
4
|
+
client = PlausibleApi::Client.new(ENV["SITE_ID"], ENV["TOKEN"])
|
5
|
+
|
6
|
+
# default all the things, pointless but works
|
7
|
+
p client.event
|
8
|
+
|
9
|
+
# change the name
|
10
|
+
p client.event(
|
11
|
+
name: "test",
|
12
|
+
)
|
13
|
+
|
14
|
+
# send the whole kitchen sink
|
15
|
+
p client.event(
|
16
|
+
ip: "127.0.0.1",
|
17
|
+
user_agent: "test",
|
18
|
+
name: "test",
|
19
|
+
url: "app://localhost/test",
|
20
|
+
referrer: "https://example.com",
|
21
|
+
revenue: {currency: "USD", amount: 1.00},
|
22
|
+
props: {foo: "bar"},
|
23
|
+
)
|
data/lib/plausible_api/client.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "plausible_api/stats/base"
|
4
|
+
require "plausible_api/stats/realtime/visitors"
|
5
|
+
require "plausible_api/stats/aggregate"
|
6
|
+
require "plausible_api/stats/timeseries"
|
7
|
+
require "plausible_api/stats/breakdown"
|
8
|
+
require "plausible_api/event/base"
|
9
|
+
require "plausible_api/event/post"
|
8
10
|
|
9
|
-
require
|
11
|
+
require "json"
|
10
12
|
require "net/http"
|
11
13
|
require "uri"
|
12
14
|
require "cgi"
|
13
15
|
|
14
16
|
module PlausibleApi
|
15
17
|
class Client
|
16
|
-
|
17
|
-
BASE_URL = 'https://plausible.io'
|
18
|
-
|
19
18
|
def initialize(site_id, token)
|
20
19
|
@site_id = site_id.to_s
|
21
|
-
@token
|
20
|
+
@token = token.to_s
|
22
21
|
end
|
23
22
|
|
24
23
|
def aggregate(options = {})
|
@@ -38,34 +37,42 @@ module PlausibleApi
|
|
38
37
|
end
|
39
38
|
|
40
39
|
def valid?
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
realtime_visitors
|
41
|
+
true
|
42
|
+
rescue
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def event(options = {})
|
47
|
+
call PlausibleApi::Event::Post.new(options.merge(domain: @site_id))
|
47
48
|
end
|
48
49
|
|
49
50
|
private
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
|
52
|
+
SUCCESS_CODES = %w[200 202].freeze
|
53
|
+
|
54
|
+
def call(api)
|
55
|
+
raise Error, api.errors unless api.valid?
|
56
|
+
raise ConfigurationError, PlausibleApi.configuration.errors unless PlausibleApi.configuration.valid?
|
57
|
+
|
58
|
+
url = "#{PlausibleApi.configuration.base_url}#{api.request_url.gsub("$SITE_ID", @site_id)}"
|
54
59
|
uri = URI.parse(url)
|
55
60
|
|
56
|
-
req =
|
57
|
-
req.
|
61
|
+
req = api.request_class.new(uri.request_uri)
|
62
|
+
req.initialize_http_header(api.request_headers)
|
63
|
+
req.add_field("authorization", "Bearer #{@token}") if api.request_auth?
|
64
|
+
req.body = api.request_body if api.request_body?
|
58
65
|
|
59
66
|
http = Net::HTTP.new(uri.host, uri.port)
|
60
|
-
http.use_ssl = true
|
67
|
+
http.use_ssl = true
|
61
68
|
|
62
69
|
response = http.request(req)
|
63
70
|
|
64
|
-
if response.code
|
71
|
+
if SUCCESS_CODES.include?(response.code)
|
65
72
|
api.parse_response response.body
|
66
73
|
else
|
67
74
|
raise StandardError.new response.body
|
68
75
|
end
|
69
76
|
end
|
70
77
|
end
|
71
|
-
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PlausibleApi
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :base_url
|
4
|
+
|
5
|
+
# Setting up default values
|
6
|
+
def initialize
|
7
|
+
@base_url = "https://plausible.io"
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
errors.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def errors
|
15
|
+
errors = []
|
16
|
+
if base_url.nil? || base_url.empty?
|
17
|
+
errors.push(base_url: "base_url is required")
|
18
|
+
elsif !(URI.parse base_url).is_a? URI::HTTP
|
19
|
+
errors.push(base_url: "base_url is not a valid URL")
|
20
|
+
elsif base_url.end_with?("/")
|
21
|
+
errors.push(base_url: "base_url should not end with a trailing slash")
|
22
|
+
end
|
23
|
+
errors
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module PlausibleApi
|
2
|
+
module Event
|
3
|
+
class Base
|
4
|
+
DEFAULT_USER_AGENT = "plausible_api_ruby/#{PlausibleApi::VERSION}"
|
5
|
+
|
6
|
+
def request_class
|
7
|
+
Net::HTTP::Post
|
8
|
+
end
|
9
|
+
|
10
|
+
def request_url_base
|
11
|
+
"/api/event"
|
12
|
+
end
|
13
|
+
|
14
|
+
def request_url
|
15
|
+
request_url_base
|
16
|
+
end
|
17
|
+
|
18
|
+
def request_auth?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_body?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid?
|
27
|
+
errors.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlausibleApi
|
4
|
+
module Event
|
5
|
+
class Post < Base
|
6
|
+
DEFAULT_USER_AGENT = "plausible_api_ruby/#{PlausibleApi::VERSION}"
|
7
|
+
VALID_REVENUE_KEYS = %i[amount currency].freeze
|
8
|
+
OPTIONS_IN_HEADERS = %i[ip user_agent].freeze
|
9
|
+
|
10
|
+
attr_reader :domain
|
11
|
+
attr_reader :ip, :user_agent, :url
|
12
|
+
attr_reader :name, :props, :referrer, :revenue
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@options = options.transform_keys(&:to_sym)
|
16
|
+
|
17
|
+
@domain = @options[:domain]
|
18
|
+
@ip = @options[:ip]
|
19
|
+
@user_agent = presence(@options[:user_agent]) || DEFAULT_USER_AGENT
|
20
|
+
@name = presence(@options[:name]) || "pageview"
|
21
|
+
@url = presence(@options[:url]) || "app://localhost/#{@name}"
|
22
|
+
@referrer = @options[:referrer]
|
23
|
+
@revenue = @options[:revenue]
|
24
|
+
@props = @options[:props]
|
25
|
+
end
|
26
|
+
|
27
|
+
def request_body
|
28
|
+
data = {
|
29
|
+
url: @url,
|
30
|
+
name: @name,
|
31
|
+
domain: @domain
|
32
|
+
}
|
33
|
+
|
34
|
+
data[:props] = @props if present?(@props)
|
35
|
+
data[:revenue] = @revenue if present?(@revenue)
|
36
|
+
data[:referrer] = @referrer if present?(@referrer)
|
37
|
+
|
38
|
+
JSON.generate(data)
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_headers
|
42
|
+
headers = {
|
43
|
+
"content-type" => "application/json",
|
44
|
+
"user-agent" => @user_agent
|
45
|
+
}
|
46
|
+
headers["x-forwarded-for"] = @ip if present?(@ip)
|
47
|
+
headers
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_response(body)
|
51
|
+
body == "ok"
|
52
|
+
end
|
53
|
+
|
54
|
+
def errors
|
55
|
+
errors = []
|
56
|
+
errors.push(url: "url is required") if blank?(@url)
|
57
|
+
errors.push(name: "name is required") if blank?(@name)
|
58
|
+
errors.push(domain: "domain is required") if blank?(@domain)
|
59
|
+
errors.push(user_agent: "user_agent is required") if blank?(@user_agent)
|
60
|
+
|
61
|
+
if present?(@revenue)
|
62
|
+
if @revenue.is_a?(Hash)
|
63
|
+
unless valid_revenue_keys?(@revenue)
|
64
|
+
errors.push(
|
65
|
+
revenue: "revenue must have keys #{VALID_REVENUE_KEYS.join(", ")} " \
|
66
|
+
"but was #{@revenue.inspect}"
|
67
|
+
)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
errors.push(revenue: "revenue must be a Hash")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if present?(@props) && !@props.is_a?(Hash)
|
75
|
+
errors.push(props: "props must be a Hash")
|
76
|
+
end
|
77
|
+
|
78
|
+
errors
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def valid_revenue_keys?(revenue)
|
84
|
+
revenue.keys.sort.map(&:to_sym) == VALID_REVENUE_KEYS.sort
|
85
|
+
end
|
86
|
+
|
87
|
+
def present?(value)
|
88
|
+
!value.nil? && !value.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
def blank?(value)
|
92
|
+
!present?(value)
|
93
|
+
end
|
94
|
+
|
95
|
+
def presence(value)
|
96
|
+
return nil if blank?(value)
|
97
|
+
value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -5,11 +5,11 @@ module PlausibleApi
|
|
5
5
|
class Aggregate < Base
|
6
6
|
|
7
7
|
def initialize(options = {})
|
8
|
-
super({ period: '30d',
|
8
|
+
super({ period: '30d',
|
9
9
|
metrics: 'visitors,visits,pageviews,views_per_visit,bounce_rate,visit_duration,events' }
|
10
10
|
.merge(options))
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def request_url_base
|
14
14
|
"/api/v1/stats/aggregate?site_id=$SITE_ID"
|
15
15
|
end
|
@@ -15,11 +15,31 @@ module PlausibleApi
|
|
15
15
|
raise NotImplementedError
|
16
16
|
end
|
17
17
|
|
18
|
+
def request_class
|
19
|
+
Net::HTTP::Get
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_body?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_body
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
18
30
|
def request_url
|
19
31
|
params = @options.select{ |_,v| !v.to_s.empty? }
|
20
32
|
[request_url_base, URI.encode_www_form(params)].reject{|e| e.empty?}.join('&')
|
21
33
|
end
|
22
34
|
|
35
|
+
def request_headers
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
39
|
+
def request_auth?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
23
43
|
def parse_response(body)
|
24
44
|
raise NotImplementedError
|
25
45
|
end
|
@@ -7,7 +7,7 @@ module PlausibleApi
|
|
7
7
|
def initialize(options = {})
|
8
8
|
super({ period: '30d', property: 'event:page' }.merge(options))
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def request_url_base
|
12
12
|
"/api/v1/stats/breakdown?site_id=$SITE_ID"
|
13
13
|
end
|
@@ -17,4 +17,4 @@ module PlausibleApi
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
@@ -7,7 +7,7 @@ module PlausibleApi
|
|
7
7
|
def initialize(options = {})
|
8
8
|
super({ period: '30d' }.merge(options))
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def request_url_base
|
12
12
|
"/api/v1/stats/timeseries?site_id=$SITE_ID"
|
13
13
|
end
|
@@ -17,4 +17,4 @@ module PlausibleApi
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
data/lib/plausible_api.rb
CHANGED
@@ -1,7 +1,20 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "plausible_api/version"
|
2
|
+
require "plausible_api/client"
|
3
|
+
require "plausible_api/configuration"
|
3
4
|
|
4
5
|
module PlausibleApi
|
5
6
|
class Error < StandardError; end
|
7
|
+
|
8
|
+
class ConfigurationError < StandardError; end
|
9
|
+
|
6
10
|
# Your code goes here...
|
11
|
+
class << self
|
12
|
+
def configuration
|
13
|
+
@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure
|
17
|
+
yield(configuration)
|
18
|
+
end
|
19
|
+
end
|
7
20
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plausible_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gustavo Garcia
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A very humble wrapper for the new API by Plausible
|
14
14
|
email:
|
@@ -26,8 +26,13 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- bin/console
|
28
28
|
- bin/setup
|
29
|
+
- examples/basic.rb
|
30
|
+
- examples/event.rb
|
29
31
|
- lib/plausible_api.rb
|
30
32
|
- lib/plausible_api/client.rb
|
33
|
+
- lib/plausible_api/configuration.rb
|
34
|
+
- lib/plausible_api/event/base.rb
|
35
|
+
- lib/plausible_api/event/post.rb
|
31
36
|
- lib/plausible_api/stats/aggregate.rb
|
32
37
|
- lib/plausible_api/stats/base.rb
|
33
38
|
- lib/plausible_api/stats/breakdown.rb
|