ga_events 4.1.0 → 4.2.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 +4 -4
- data/README.md +5 -1
- data/app/assets/javascripts/ga_events.js.coffee +44 -12
- data/lib/ga_events/middleware.rb +10 -7
- data/lib/ga_events/version.rb +1 -1
- data/spec/middlware_event_placement_spec.rb +119 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83fd5b6e15c900730f8f41e03ade3efab0b619a7c4e9e67074db8730ac0701db
|
4
|
+
data.tar.gz: e866ec33593b82a6f826004c4c03486203963be5eae9e534ed6be0cad9a3e626
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0345da75e6b2500c22ef51b72823c3eaf42e71a0e57c9e617119dcacfa4a2aed07ea4120fb25db030d2c3394c322cfce44bdcd44c300ef0deaf275e36b9d5613
|
7
|
+
data.tar.gz: d2c2449b6117600eebd172680950a5e42c98f2669c75bb6eda9651d1e1dda0a8da731881b78d205236bedb17130f046a6df500cefd56641667e1c39bc9af35a3
|
data/README.md
CHANGED
@@ -15,7 +15,11 @@ pushes it to Google Analytics via gtag.js or Google Tag Manager.
|
|
15
15
|
|
16
16
|
* Ruby >= 3.4
|
17
17
|
* Rails 7.2 onwards
|
18
|
+
|
19
|
+
## Optional dependencies
|
20
|
+
|
18
21
|
* jQuery
|
22
|
+
* Turbolinks or Hotwire Turbo
|
19
23
|
|
20
24
|
## Installation
|
21
25
|
|
@@ -27,7 +31,7 @@ gem 'ga_events'
|
|
27
31
|
|
28
32
|
Run the `bundle` command to install it.
|
29
33
|
|
30
|
-
Add to the top of your `application.js`
|
34
|
+
Add to the top of your `application.js`
|
31
35
|
|
32
36
|
```javascript
|
33
37
|
//= require ga_events.js
|
@@ -16,24 +16,33 @@ class GaEvents.Event
|
|
16
16
|
# Decompose an event-string (ruby side) into an event object.
|
17
17
|
@from_json: (string) ->
|
18
18
|
events = JSON.parse(string)
|
19
|
-
|
20
|
-
$.map events, (event) =>
|
19
|
+
events.map((event) ->
|
21
20
|
if event_name = event.__event__
|
22
21
|
delete event.__event__
|
23
22
|
new @(event_name, event)
|
23
|
+
, @)
|
24
24
|
|
25
25
|
@from_dom: ->
|
26
26
|
data_attribute = "data-#{@html_key}"
|
27
|
-
dom_events =
|
28
|
-
|
27
|
+
dom_events = document
|
28
|
+
.querySelector("div[#{data_attribute}]")
|
29
|
+
?.getAttribute(data_attribute)
|
30
|
+
|
31
|
+
@from_json(dom_events) if dom_events?
|
29
32
|
|
30
33
|
# Events should not be sent to an adapter unless the DOM has finished loading.
|
31
34
|
@flush: ->
|
32
35
|
return if @require_user_consent && !@user_consent_given
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
push_events = =>
|
38
|
+
if @list.length > 0 and @may_flush
|
39
|
+
@list.forEach((event) -> event.push_to_adapter())
|
40
|
+
@list = []
|
41
|
+
|
42
|
+
if window.requestIdleCallback
|
43
|
+
requestIdleCallback(push_events, timeout: 250)
|
44
|
+
else
|
45
|
+
setTimeout(push_events, 1)
|
37
46
|
|
38
47
|
# Add all events to a queue to flush them later
|
39
48
|
constructor: (@event_name, @options = {}) ->
|
@@ -49,21 +58,44 @@ class GaEvents.Event
|
|
49
58
|
# https://support.google.com/analytics/answer/13316687?hl=en#zippy=%2Cweb
|
50
59
|
is_valid_event_name: -> /^[a-z]+[a-z0-9_]*$/i.test(@event_name)
|
51
60
|
|
52
|
-
|
61
|
+
|
62
|
+
start = ->
|
53
63
|
@may_flush = true
|
54
64
|
@flush()
|
55
65
|
|
66
|
+
addEventListener = document.addEventListener
|
67
|
+
|
56
68
|
process_xhr = (xhr) =>
|
57
69
|
xhr_events = xhr.getResponseHeader @header_key
|
58
70
|
@from_json decodeURIComponent(xhr_events) if xhr_events?
|
59
71
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
72
|
+
process_fetch = (response) =>
|
73
|
+
events = response.headers.get @header_key
|
74
|
+
@from_json decodeURIComponent(events) if events?
|
75
|
+
|
76
|
+
# jQuery $.ajax
|
77
|
+
if window.jQuery && jQuery.ajax
|
78
|
+
# This event can only be caught on the jQuery event bus.
|
79
|
+
jQuery(document).on("ajaxComplete", (_, xhr) -> process_xhr(xhr))
|
80
|
+
|
81
|
+
|
82
|
+
# classic Turbolinks
|
83
|
+
addEventListener("turbolinks:request-end", (event) ->
|
84
|
+
process_xhr(event.originalEvent.data.xhr)
|
85
|
+
)
|
86
|
+
|
87
|
+
# Hotwire/Turbo
|
88
|
+
addEventListener("turbo:before-fetch-response", (event) ->
|
89
|
+
process_fetch(event.detail.fetchResponse.response)
|
90
|
+
)
|
64
91
|
|
65
92
|
@from_dom()
|
66
93
|
|
94
|
+
if document.readyState == "loading"
|
95
|
+
addEventListener("DOMContentLoaded", start.bind(@), once: true)
|
96
|
+
else
|
97
|
+
start.call(@)
|
98
|
+
|
67
99
|
|
68
100
|
class GaEvents.GTagAdapter
|
69
101
|
constructor: (options) ->
|
data/lib/ga_events/middleware.rb
CHANGED
@@ -9,6 +9,7 @@ require 'rubygems'
|
|
9
9
|
module GaEvents
|
10
10
|
class Middleware
|
11
11
|
SESSION_GA_EVENTS_KEY = 'ga_events.events'
|
12
|
+
RESPONSE_HEADER = 'x-ga-events'
|
12
13
|
|
13
14
|
HEADERS_KLASS = if Gem::Version.new(Rack.release) < Gem::Version.new('3.0')
|
14
15
|
Rack::Utils::HeaderHash
|
@@ -31,9 +32,9 @@ module GaEvents
|
|
31
32
|
|
32
33
|
# Can outgrow, headers might get too big
|
33
34
|
serialized_events = GaEvents::List.to_s
|
34
|
-
if
|
35
|
+
if xhr_or_turbo?(request)
|
35
36
|
# AJAX request
|
36
|
-
headers[
|
37
|
+
headers[RESPONSE_HEADER] = CGI.escapeURIComponent(serialized_events)
|
37
38
|
elsif redirect?(status)
|
38
39
|
# 30x/redirect? Then add event list to rack session to survive the
|
39
40
|
# redirect.
|
@@ -76,18 +77,20 @@ module GaEvents
|
|
76
77
|
|
77
78
|
# Taken from:
|
78
79
|
# https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/jsonp.rb
|
79
|
-
def html?(status,
|
80
|
+
def html?(status, response_headers)
|
80
81
|
!Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
|
81
|
-
|
82
|
-
|
82
|
+
response_headers.key?('content-type') &&
|
83
|
+
response_headers['content-type'].start_with?('text/html')
|
83
84
|
end
|
84
85
|
|
85
86
|
def redirect?(status)
|
86
87
|
(300..399).cover?(status)
|
87
88
|
end
|
88
89
|
|
89
|
-
def
|
90
|
-
request.xhr? ||
|
90
|
+
def xhr_or_turbo?(request)
|
91
|
+
request.xhr? ||
|
92
|
+
request.env['HTTP_TURBOLINKS_REFERRER'] ||
|
93
|
+
request.env['HTTP_X_TURBO_REQUEST_ID']
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
data/lib/ga_events/version.rb
CHANGED
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe GaEvents::Middleware do
|
6
|
+
let(:app) do
|
7
|
+
lambda do |env|
|
8
|
+
status_code = env.delete('app_status_code').to_i
|
9
|
+
|
10
|
+
GaEvents::Event.new('test', 'event' => 'stuff')
|
11
|
+
[status_code, { 'content-type' => 'text/html' }, [<<~HTML]]
|
12
|
+
<!doctype html>
|
13
|
+
<html lang=en>
|
14
|
+
<head>
|
15
|
+
<meta charset=utf-8>
|
16
|
+
<title>ABC</title>
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
<p>I'm the content</p>
|
20
|
+
</body>
|
21
|
+
</html>
|
22
|
+
HTML
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'html requests' do
|
27
|
+
it 'adds events in an injected div' do
|
28
|
+
response = make_request
|
29
|
+
expect(response.body).to include('<div data-ga-events="[{')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does no write to rack ression or response headers' do
|
33
|
+
session = {}
|
34
|
+
response = make_request('rack.session' => session)
|
35
|
+
expect(session[GaEvents::Middleware::SESSION_GA_EVENTS_KEY]).to be_nil
|
36
|
+
expect(response.headers).not_to have_key(
|
37
|
+
GaEvents::Middleware::RESPONSE_HEADER
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'non xhr/fetch redirects' do
|
43
|
+
it 'writes to session' do
|
44
|
+
session = {}
|
45
|
+
make_request('app_status_code' => '302', 'rack.session' => session)
|
46
|
+
expect(session[GaEvents::Middleware::SESSION_GA_EVENTS_KEY]).to be_present
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not inject html or add response headers' do
|
50
|
+
response = make_request('app_status_code' => '302', 'rack.session' => {})
|
51
|
+
expect(response.body).not_to include('<div data-ga-events="[{')
|
52
|
+
expect(response.headers).not_to have_key(
|
53
|
+
GaEvents::Middleware::RESPONSE_HEADER
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'xhr requests' do
|
59
|
+
it 'adds events as response header' do
|
60
|
+
response = make_request('HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')
|
61
|
+
expect(
|
62
|
+
response.headers[GaEvents::Middleware::RESPONSE_HEADER]
|
63
|
+
).to include('__event__')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not inject a div or writes to session' do
|
67
|
+
session = {}
|
68
|
+
response =
|
69
|
+
make_request(
|
70
|
+
'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest',
|
71
|
+
'rack.session' => session
|
72
|
+
)
|
73
|
+
expect(response.body).not_to include('<div data-ga-events')
|
74
|
+
expect(session[GaEvents::Middleware::SESSION_GA_EVENTS_KEY]).to be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'turbolink requests' do
|
79
|
+
it 'adds events as response header' do
|
80
|
+
response = make_request('HTTP_TURBOLINKS_REFERRER' => '...')
|
81
|
+
expect(
|
82
|
+
response.headers[GaEvents::Middleware::RESPONSE_HEADER]
|
83
|
+
).to include('__event__')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not inject a div with events' do
|
87
|
+
response = make_request('HTTP_TURBOLINKS_REFERRER' => '...')
|
88
|
+
expect(response.body).not_to include('<div data-ga-events')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'turbo requests' do
|
93
|
+
it 'adds events as response header' do
|
94
|
+
response = make_request('HTTP_X_TURBO_REQUEST_ID' => '...')
|
95
|
+
expect(
|
96
|
+
response.headers[GaEvents::Middleware::RESPONSE_HEADER]
|
97
|
+
).to include('__event__')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'does not inject a div with events' do
|
101
|
+
response = make_request('HTTP_X_TURBO_REQUEST_ID' => '...')
|
102
|
+
expect(response.body).not_to include('<div data-ga-events')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def make_request(options = nil)
|
109
|
+
middleware = described_class.new(app)
|
110
|
+
options = {
|
111
|
+
'app_status_code' => '200',
|
112
|
+
:lint => true,
|
113
|
+
:fatal => true,
|
114
|
+
**options
|
115
|
+
}
|
116
|
+
r = Rack::MockRequest.new(middleware)
|
117
|
+
r.get('/', options)
|
118
|
+
end
|
119
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ga_events
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nix-wie-weg Team
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- spec/event_spec.rb
|
50
50
|
- spec/list_spec.rb
|
51
51
|
- spec/middleware_spec.rb
|
52
|
+
- spec/middlware_event_placement_spec.rb
|
52
53
|
- spec/spec_helper.rb
|
53
54
|
homepage: https://github.com/Nix-wie-weg/ga_events
|
54
55
|
licenses:
|