plausible_api 0.1.10 → 0.3
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 +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
|