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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b93d2ffe7308a325730dc5c73faf0765807c9a3c83aea96d911199044e73cf89
4
- data.tar.gz: 044d35f5bba0adf9613e5886065cef5eadd38fded5957879b13ff16beb0dfb89
3
+ metadata.gz: 83fd5b6e15c900730f8f41e03ade3efab0b619a7c4e9e67074db8730ac0701db
4
+ data.tar.gz: e866ec33593b82a6f826004c4c03486203963be5eae9e534ed6be0cad9a3e626
5
5
  SHA512:
6
- metadata.gz: e3ae1007735e30a3217f69fbbb026883eebb1096bb390ed831d855e9324b8444d5460b68842df05e9e5e0ffa5072d0f4ea4b7b09a95d63de8093f74f0c8f2212
7
- data.tar.gz: 68ea2765ce64928d0b64bfebed5a9fdee2feffd4be04d22a31e3f6d40cd63584c60a576e6dce89809723f82f7c1b36263fa56f2a3af8e646c489e6755479c15f
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` (but after requiring jQuery):
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 = $("div[#{data_attribute}]").attr data_attribute
28
- @from_json dom_events if dom_events?
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
- if @list.length > 0 and @may_flush
35
- $.map @list, (event) -> event.push_to_adapter()
36
- @list = []
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
- jQuery =>
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
- $(document).ajaxComplete((_, xhr) -> process_xhr(xhr))
61
- $(document).on "turbolinks:request-end", (event) ->
62
- xhr = event.originalEvent.data.xhr
63
- process_xhr(xhr)
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) ->
@@ -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 xhr_or_turbolinks?(request)
35
+ if xhr_or_turbo?(request)
35
36
  # AJAX request
36
- headers['x-ga-events'] = CGI.escapeURIComponent(serialized_events)
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, headers)
80
+ def html?(status, response_headers)
80
81
  !Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
81
- headers.key?('content-type') &&
82
- headers['content-type'].start_with?('text/html')
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 xhr_or_turbolinks?(request)
90
- request.xhr? || request.env['HTTP_TURBOLINKS_REFERRER']
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GaEvents
4
- VERSION = '4.1.0'
4
+ VERSION = '4.2.0'
5
5
  end
@@ -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.1.0
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: