consently 0.2.1 → 1.0.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: 3ae532d0c3dd92af70f6bd6bfae150b2d61889fbb7ecae3ea1edae521e33605a
4
- data.tar.gz: f0bccd48aba2d92a00e1a1359580b5f051206de246aa205bb3a2a3323df516c9
3
+ metadata.gz: dab77a672e8b422500eceb9e90f0481fc4f7abb180e22ca7c12fadf98c4ef9ef
4
+ data.tar.gz: 802a3a6d8c5915acce9d6e843bb5fd8b10a28a7eafb4dd68d0af8bc8617ba43b
5
5
  SHA512:
6
- metadata.gz: 8c2917ed994ffc2db028512616ef551d18471e2e68df786e150337fae51b14c8de902aabd209cf3d65a81b14fcd7aed13135e5b2a8a3f886a217a5e6b4abd348
7
- data.tar.gz: bc74d85205720239b176d6caeab993e1bd08d9c49258f4fc6778dd17d763b808a4705155f32b8f7eec620102b1f4215469d3b523fe51b859c74a6961fafa763e
6
+ metadata.gz: 3097f21d9037f63e8d27a697e73c2fae5e54b246e1de83e6722a40b2915005400c45ede835a7d8b77aaf1c3da55b877ba6318e23617aad7b3d00de9d0b001e59
7
+ data.tar.gz: 4247ab6d468b280bc7b784c52401d1f4ee53d98e627f168206159bea2daebc08ff3c359124afd71849b7cf535de37f9b50b49795e020099c988db74dfd12fc67
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0 (2026-09-04)
4
+
5
+ - Events speak the language of the page's tags: `gtag('event', ...)` under
6
+ gtag.js, a `dataLayer` push under Google Tag Manager, decided per request
7
+ from the tags in scope or forced with `event_transport`. Before, every event
8
+ was a dataLayer push - which gtag.js ignores, so a shop without a container
9
+ lost its whole ecommerce funnel.
10
+ - An event rendered without consent is held back as an inert script and
11
+ released with the tags when its category is granted, instead of being
12
+ dropped. The page a visitor accepts on now counts its `view_item`.
13
+ - `consently_stream_event` sends an event from a Turbo Stream response - the
14
+ add-to-cart that never renders a page - through a stream action that ships
15
+ with the banner controller; it is sent at once with consent and queued
16
+ until then otherwise.
17
+ - `Consently.track(event, payload, { category })` and `Consently.granted()`
18
+ in JavaScript, on the same queue.
19
+ - `consently_event` is the new name of `consently_data_layer_push`; the old
20
+ one still works.
21
+
3
22
  ## 0.2.1 (2026-08-13)
4
23
 
5
24
  - The banner's custom properties moved from `.consently` to `:root`, so the
data/README.md CHANGED
@@ -201,8 +201,9 @@ status panel - ask for a reload instead:
201
201
  c.reload_after_choice = true
202
202
  ```
203
203
 
204
- Either way a `consently:change` event fires on `document`, carrying the
205
- granted categories, so you can react to it yourself:
204
+ Either way the events waiting on that consent go out too (see below), and a
205
+ `consently:change` event fires on `document`, carrying the granted categories,
206
+ so you can react to it yourself:
206
207
 
207
208
  ```js
208
209
  document.addEventListener("consently:change", ({ detail }) => {
@@ -237,7 +238,7 @@ button carries `aria-expanded` and `aria-controls`, reopening the panel moves
237
238
  focus into it, and the animation gives way to `prefers-reduced-motion`. Nobody
238
239
  is trapped in a focus cycle they did not ask for.
239
240
 
240
- ## Ecommerce events
241
+ ## Events
241
242
 
242
243
  GA4 wants a particular shape, and your models are not it. Hand the helper
243
244
  whatever you have:
@@ -249,17 +250,50 @@ whatever you have:
249
250
 
250
251
  Items may be hashes already in GA4 shape, or any object answering to
251
252
  `sku`/`id`, `name`, `price`, `quantity`, `category`, `brand`, `variant` - a
252
- line item or a product usually does. The previous `ecommerce` object is
253
- cleared first, as Google asks, so two events on one page cannot bleed into
254
- each other.
253
+ line item or a product usually does. Anything else:
255
254
 
256
- Anything else:
255
+ ```erb
256
+ <%= consently_event "newsletter_signup", source: "footer" %>
257
+ ```
258
+
259
+ **Sent the way your tags listen.** A page running gtag.js gets
260
+ `gtag('event', ...)` calls; a page running Google Tag Manager gets `dataLayer`
261
+ pushes, the previous `ecommerce` object cleared first as Google asks. The gem
262
+ decides per request from the tags in scope - a container means the dataLayer,
263
+ any other Google tag means gtag - because a push meant for a container is
264
+ something gtag.js silently ignores, and a whole checkout funnel can go missing
265
+ that way. Force it if you must:
266
+
267
+ ```ruby
268
+ c.event_transport = :data_layer # or :gtag; :auto is the default
269
+ ```
270
+
271
+ **Held back, not dropped.** Without consent an event is rendered the way a tag
272
+ is - an inert `<script type="text/plain">` - and released together with the
273
+ tags the moment its category is granted. The product page a visitor accepts
274
+ on still counts its `view_item`.
275
+
276
+ **From a Turbo Stream.** An add-to-cart that answers with a stream renders no
277
+ page to put a script on. The stream carries the event instead, and the
278
+ JavaScript side sends it at once if the category is granted, or keeps it until
279
+ it is:
257
280
 
258
281
  ```erb
259
- <%= consently_data_layer_push "newsletter_signup", source: "footer" %>
282
+ <%# line_items/create.turbo_stream.erb %>
283
+ <%= turbo_stream.replace "cart", partial: "cart" %>
284
+ <%= consently_stream_event "add_to_cart", items: [@line_item],
285
+ currency: "EUR", value: @line_item.price %>
260
286
  ```
261
287
 
262
- Both render nothing at all when analytics consent is missing.
288
+ Nothing to register: the action arrives with the banner controller.
289
+
290
+ **From your own JavaScript.** The same queue, the same rule:
291
+
292
+ ```js
293
+ Consently.track("newsletter_signup", { source: "footer" })
294
+ Consently.track("lead", { value: 1 }, { category: "marketing", ecommerce: false })
295
+ Consently.granted("analytics") // true or false, right now
296
+ ```
263
297
 
264
298
  ## Embedded videos and maps
265
299
 
@@ -376,13 +410,14 @@ Helpers:
376
410
 
377
411
  | | |
378
412
  | --- | --- |
379
- | `consently_tags` | `<head>`: the stylesheet, consent mode defaults, and every tag (blocked or live) |
413
+ | `consently_tags` | `<head>`: the stylesheet, consent mode defaults, every tag (blocked or live), and what the JavaScript side needs to know |
380
414
  | `consently_noscript_tags` | after `<body>`: GTM and Meta fallbacks, for granted categories only |
381
415
  | `consently_banner` | the banner, the panel, and the JavaScript that releases blocked tags |
382
416
  | `consently_policy` | the generated cookie policy: categories, vendors, cookies, durations |
383
417
  | `consently_preferences_link` | "Cookie settings" link; anything with `data-consently-open` reopens the panel |
384
- | `consently_ecommerce(event, items:, **params)` | a GA4 ecommerce event, items mapped from your own objects |
385
- | `consently_data_layer_push(event, **payload)` | any other dataLayer event, rendered only with analytics consent |
418
+ | `consently_ecommerce(event, items:, **params)` | a GA4 ecommerce event, items mapped from your own objects; held back until consent |
419
+ | `consently_event(event, **payload)` | any other event, gtag or dataLayer as the page's tags expect; held back until consent |
420
+ | `consently_stream_event(event, items:, **payload)` | the same from a Turbo Stream response, sent or queued in the browser |
386
421
  | `consently_embed(kind, id, category:, ratio:)` | a video or map that waits for consent |
387
422
  | `consently_consent` | the current `Consently::Consent`; `granted?(:analytics)` in your own views |
388
423
 
@@ -397,6 +432,7 @@ Configuration:
397
432
  | `c.enabled` | `true`, `false`, or a callable taking the request |
398
433
  | `c.reload_after_choice` | reload once a choice is made; off by default |
399
434
  | `c.google_consent_mode` | `:basic` (default), `:advanced`, or `false` |
435
+ | `c.event_transport` | `:auto` (default: gtag under gtag.js, the dataLayer under a container), `:gtag`, `:data_layer` |
400
436
  | `c.log_consents`, `c.consent_subject` | store proof of each decision, optionally naming who |
401
437
  | `c.stylesheet` | link the banner's CSS; off if you style it yourself |
402
438
  | `c.cookie_name`, `c.cookie_max_age`, `c.cookie_path`, `c.cookie_domain` | where the choice is kept |
@@ -404,8 +440,9 @@ Configuration:
404
440
  | `c.respect_do_not_track`, `c.respect_global_privacy_control` | treat an opt-out signal as a rejection |
405
441
  | `c.consent_required` | who has to be asked at all; false means no banner and everything granted |
406
442
 
407
- JavaScript: a `consently:change` event fires on `document` with the granted
408
- categories.
443
+ JavaScript: `Consently.track(event, payload, { category })` sends or queues
444
+ an event, `Consently.granted(category)` says where things stand, and a
445
+ `consently:change` event fires on `document` with the granted categories.
409
446
 
410
447
  ## Licence
411
448
 
@@ -9,7 +9,7 @@ module Consently
9
9
  def consently_tags
10
10
  return "".html_safe unless consently_enabled?
11
11
 
12
- parts = []
12
+ parts = [ consently_state_tags ]
13
13
  parts << consently_stylesheet_tag if Consently.config.stylesheet
14
14
  parts << consently_consent_mode_tag if Consently.config.google_consent_mode
15
15
  Consently.tags_for(request).each do |provider|
@@ -75,17 +75,21 @@ module Consently
75
75
  end
76
76
  end
77
77
 
78
- # Push an event onto the dataLayer from a view, respecting consent: with
79
- # no analytics consent the event is simply not emitted.
78
+ # An analytics event from a view, sent the way this page's tags expect -
79
+ # gtag('event', ...) under gtag.js, a dataLayer push under Google Tag
80
+ # Manager (see Consently.event_transport_for) - and only with consent.
81
+ # Without it the event is held back the way a tag is: an inert script
82
+ # the banner releases the moment the category is granted, so the page a
83
+ # visitor accepts on still counts.
80
84
  #
81
- # <%= consently_data_layer_push("newsletter_signup", source: "footer") %>
82
- def consently_data_layer_push(event, category: :analytics, **payload)
83
- return "".html_safe unless consently_enabled? && consently_consent.granted?(category)
84
-
85
- payload = payload.merge(event: event)
86
- consently_inline_script "window.dataLayer = window.dataLayer || []; window.dataLayer.push(#{payload.to_json});"
85
+ # <%= consently_event("newsletter_signup", source: "footer") %>
86
+ def consently_event(event, category: :analytics, **payload)
87
+ consently_event_tag(Event.new(event, payload), category)
87
88
  end
88
89
 
90
+ # The name this helper had before it learnt to speak gtag.
91
+ alias_method :consently_data_layer_push, :consently_event
92
+
89
93
  # A GA4 ecommerce event in the shape Google expects, from whatever your
90
94
  # models happen to look like:
91
95
  #
@@ -96,19 +100,32 @@ module Consently
96
100
  # few obvious names (id/sku, name, price, quantity, category, brand,
97
101
  # variant) - a LineItem or a Product usually does.
98
102
  #
99
- # The previous ecommerce object is cleared first, as Google asks, so two
100
- # events on one page cannot bleed into each other.
103
+ # Under Google Tag Manager the previous ecommerce object is cleared
104
+ # first, as Google asks, so two events on one page cannot bleed into
105
+ # each other. Held back without consent, like consently_event.
101
106
  def consently_ecommerce(event, items: [], category: :analytics, **params)
102
- return "".html_safe unless consently_enabled? && consently_consent.granted?(category)
107
+ consently_event_tag(Event.new(event, params.merge(items: consently_ecommerce_items(items)), ecommerce: true), category)
108
+ end
103
109
 
104
- ecommerce = params.merge(items: Array(items).map { |item| consently_ecommerce_item(item) })
105
- payload = { event: event, ecommerce: ecommerce.compact }
110
+ # The same event from a Turbo Stream response - an add-to-cart that
111
+ # never renders a page. Items make it an ecommerce event. The stream
112
+ # carries the event, not a script: the JavaScript side sends it at once
113
+ # if the category is granted and keeps it until then otherwise, so a
114
+ # cart filled before the click is counted after it.
115
+ #
116
+ # <%= consently_stream_event("add_to_cart", items: [ @line_item ], currency: "EUR", value: 12.5) %>
117
+ def consently_stream_event(event, items: nil, category: :analytics, **payload)
118
+ return "".html_safe unless consently_enabled?
106
119
 
107
- consently_inline_script <<~JS.strip
108
- window.dataLayer = window.dataLayer || [];
109
- window.dataLayer.push({ ecommerce: null });
110
- window.dataLayer.push(#{payload.to_json});
111
- JS
120
+ payload = payload.merge(items: consently_ecommerce_items(items)) unless items.nil?
121
+ event = Event.new(event, payload, ecommerce: !items.nil?)
122
+
123
+ content_tag("turbo-stream", "",
124
+ action: "consently_event",
125
+ event: event.name,
126
+ category: category,
127
+ ecommerce: event.ecommerce?,
128
+ payload: event.payload.to_json)
112
129
  end
113
130
 
114
131
  # A complete cookie policy for the tags this request would load: every
@@ -167,6 +184,40 @@ module Consently
167
184
  Consently.enabled?(request)
168
185
  end
169
186
 
187
+ # Two <meta> tags the JavaScript side reads: which categories this page
188
+ # was rendered with, and how events are to be sent. Provisional head
189
+ # elements, so Turbo Drive swaps them on every visit.
190
+ def consently_state_tags
191
+ granted = Consently.config.categories.select { |category| consently_consent.granted?(category) }
192
+
193
+ safe_join([
194
+ tag.meta(name: "consently-granted", content: granted.join(" ")),
195
+ tag.meta(name: "consently-transport", content: consently_event_transport)
196
+ ], "\n")
197
+ end
198
+
199
+ def consently_event_transport
200
+ @consently_event_transport ||= Consently.event_transport_for(request)
201
+ end
202
+
203
+ # Live when the category is granted; otherwise inert, and released by the
204
+ # banner together with the tags the moment it is.
205
+ def consently_event_tag(event, category)
206
+ return "".html_safe unless consently_enabled?
207
+
208
+ javascript = event.to_js(consently_event_transport)
209
+ return consently_inline_script(javascript) if consently_consent.granted?(category)
210
+
211
+ attributes = { type: "text/plain", data: { "consently-category" => category } }
212
+ attributes[:nonce] = content_security_policy_nonce if content_security_policy_nonce.present?
213
+
214
+ content_tag(:script, javascript.html_safe, attributes)
215
+ end
216
+
217
+ def consently_ecommerce_items(items)
218
+ Array(items).map { |item| consently_ecommerce_item(item) }
219
+ end
220
+
170
221
  # Whether this tag may run now. Normally that means consent; under
171
222
  # advanced consent mode Google's own tags also load before it, having
172
223
  # been told to store nothing until it arrives.
@@ -1,4 +1,7 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
+ // Loaded for its side effects: the event queue, the Turbo Stream action and
3
+ // window.Consently.track come along with the banner.
4
+ import "consently/events"
2
5
 
3
6
  // The banner, the preferences panel, and the part that actually matters:
4
7
  // turning the blocked <script type="text/plain"> tags into live ones the
@@ -0,0 +1,78 @@
1
+ // Analytics events that wait for consent.
2
+ //
3
+ // Three roads lead here. An event rendered into a page without consent is an
4
+ // inert <script> the banner releases together with the tags - nothing to do
5
+ // on this side. A <turbo-stream action="consently_event"> comes from a
6
+ // response that renders no page, and window.Consently.track() from your own
7
+ // code; those two are sent at once when their category is granted and kept
8
+ // until it is otherwise, so an add-to-cart that happened before the click is
9
+ // counted after it.
10
+ //
11
+ // The page says what is granted and how to send in two <meta> tags written by
12
+ // consently_tags. The banner's consently:change event overrides the first the
13
+ // moment a choice is made, and the next Turbo visit brings a fresh page whose
14
+ // tags already know.
15
+
16
+ const NECESSARY = "necessary"
17
+ const waiting = []
18
+ let decided = null
19
+
20
+ export function track(name, payload = {}, { category = "analytics", ecommerce = false } = {}) {
21
+ const event = { name, payload, category, ecommerce }
22
+
23
+ if (granted(category)) send(event)
24
+ else waiting.push(event)
25
+ }
26
+
27
+ export function granted(category) {
28
+ if (category === NECESSARY) return true
29
+
30
+ return (decided ?? meta("consently-granted").split(" ")).includes(category)
31
+ }
32
+
33
+ function meta(name) {
34
+ return document.querySelector(`meta[name="${name}"]`)?.content ?? ""
35
+ }
36
+
37
+ function send({ name, payload, ecommerce }) {
38
+ window.dataLayer = window.dataLayer || []
39
+
40
+ if (meta("consently-transport") === "gtag") {
41
+ // gtag.js reads the queue, so this is safe before the tag has loaded.
42
+ window.gtag = window.gtag || function () { window.dataLayer.push(arguments) }
43
+ window.gtag("event", name, payload)
44
+ } else if (ecommerce) {
45
+ window.dataLayer.push({ ecommerce: null })
46
+ window.dataLayer.push({ event: name, ecommerce: payload })
47
+ } else {
48
+ window.dataLayer.push({ event: name, ...payload })
49
+ }
50
+ }
51
+
52
+ function release(categories) {
53
+ decided = [...categories, NECESSARY]
54
+
55
+ waiting.splice(0).forEach((event) => (granted(event.category) ? send(event) : waiting.push(event)))
56
+ }
57
+
58
+ document.addEventListener("consently:change", (event) => release(event.detail.categories))
59
+
60
+ // A new page carries the server's reading of the cookie, which also knows
61
+ // about policy versions and expiry; from here on it is the one to trust.
62
+ document.addEventListener("turbo:load", () => { decided = null })
63
+
64
+ // The Turbo Stream action. A listener rather than an entry in
65
+ // Turbo.StreamActions, so the gem imports nothing from Turbo and the order
66
+ // the modules load in does not matter.
67
+ document.addEventListener("turbo:before-stream-render", (event) => {
68
+ const stream = event.target
69
+ if (stream.getAttribute("action") !== "consently_event") return
70
+
71
+ event.preventDefault()
72
+ track(stream.getAttribute("event"), JSON.parse(stream.getAttribute("payload") || "{}"), {
73
+ category: stream.getAttribute("category") || "analytics",
74
+ ecommerce: stream.getAttribute("ecommerce") === "true"
75
+ })
76
+ })
77
+
78
+ window.Consently = Object.assign(window.Consently || {}, { track, granted })
data/config/importmap.rb CHANGED
@@ -2,3 +2,4 @@
2
2
  # resolves without anyone editing config/importmap.rb.
3
3
  pin "consently/banner_controller", to: "consently/banner_controller.js", preload: true
4
4
  pin "consently/embed_controller", to: "consently/embed_controller.js", preload: true
5
+ pin "consently/events", to: "consently/events.js", preload: true
@@ -50,6 +50,21 @@ module Consently
50
50
  google_consent_mode == :advanced
51
51
  end
52
52
 
53
+ # How events are sent: :gtag for a page running gtag.js, :data_layer for
54
+ # one running Google Tag Manager. The default, :auto, decides per request
55
+ # from the tags in its scope (see Consently.event_transport_for) and is
56
+ # right unless you push to a container the gem does not know about.
57
+ attr_reader :event_transport
58
+
59
+ def event_transport=(transport)
60
+ transport = transport&.to_sym
61
+ unless [ :auto, *Event::TRANSPORTS ].include?(transport)
62
+ raise ArgumentError, "event_transport must be :auto, :gtag or :data_layer"
63
+ end
64
+
65
+ @event_transport = transport
66
+ end
67
+
53
68
  # Store a row per decision, as proof of consent. Needs the engine mounted
54
69
  # and the migration from `rails g consently:consent_log`.
55
70
  attr_accessor :log_consents
@@ -109,6 +124,7 @@ module Consently
109
124
  @consent_version = 1
110
125
  @enabled = true
111
126
  @google_consent_mode = :basic
127
+ @event_transport = :auto
112
128
  @stylesheet = true
113
129
  @log_consents = false
114
130
  @respect_do_not_track = false
@@ -14,7 +14,7 @@ module Consently
14
14
  if app.config.respond_to?(:assets)
15
15
  app.config.assets.paths << root.join("app/javascript")
16
16
  if app.config.assets.respond_to?(:precompile)
17
- app.config.assets.precompile += %w[consently.css consently/banner_controller.js consently/embed_controller.js]
17
+ app.config.assets.precompile += %w[consently.css consently/banner_controller.js consently/embed_controller.js consently/events.js]
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,54 @@
1
+ require "active_support/core_ext/object/json"
2
+
3
+ module Consently
4
+ # One analytics event on its way to the page: a name, a payload, and whether
5
+ # it is a GA4 ecommerce event. It is written as JavaScript for whichever
6
+ # transport the page runs on - gtag('event', ...) for gtag.js, a dataLayer
7
+ # push for Google Tag Manager - so the helpers never have to know which.
8
+ class Event
9
+ TRANSPORTS = %i[gtag data_layer].freeze
10
+
11
+ attr_reader :name, :payload
12
+
13
+ def initialize(name, payload = {}, ecommerce: false)
14
+ @name = name.to_s
15
+ @payload = payload.to_h.compact
16
+ @ecommerce = ecommerce
17
+ end
18
+
19
+ def ecommerce?
20
+ @ecommerce
21
+ end
22
+
23
+ def to_js(transport)
24
+ case transport.to_sym
25
+ when :gtag then gtag_js
26
+ when :data_layer then data_layer_js
27
+ else raise ArgumentError, "Consently: unknown event transport #{transport.inspect}, expected one of #{TRANSPORTS.join(", ")}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # gtag.js reads the dataLayer as a queue, so an event pushed before the
34
+ # tag has loaded is not lost. That is what lets a released event fire the
35
+ # moment consent arrives, while gtag.js is still on its way.
36
+ def gtag_js
37
+ "window.dataLayer = window.dataLayer || []; " \
38
+ "window.gtag = window.gtag || function(){dataLayer.push(arguments);}; " \
39
+ "gtag('event', #{name.to_json}, #{payload.to_json});"
40
+ end
41
+
42
+ def data_layer_js
43
+ if ecommerce?
44
+ # Cleared first, as Google asks, so two events on one page cannot
45
+ # bleed into each other.
46
+ "window.dataLayer = window.dataLayer || []; " \
47
+ "window.dataLayer.push({ ecommerce: null }); " \
48
+ "window.dataLayer.push(#{{ event: name, ecommerce: payload }.to_json});"
49
+ else
50
+ "window.dataLayer = window.dataLayer || []; window.dataLayer.push(#{payload.merge(event: name).to_json});"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Consently
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/consently.rb CHANGED
@@ -7,6 +7,7 @@ require "consently/version"
7
7
  require "consently/script"
8
8
  require "consently/cookie"
9
9
  require "consently/consent"
10
+ require "consently/event"
10
11
  require "consently/providers/base"
11
12
  require "consently/providers"
12
13
  require "consently/configuration"
@@ -60,6 +61,20 @@ module Consently
60
61
  resolve(config.enabled, request)
61
62
  end
62
63
 
64
+ # How events reach Google for this request. Google Tag Manager reads
65
+ # dataLayer pushes; gtag.js only acts on gtag('event', ...) calls, and a
66
+ # push meant for a container it silently ignores. :auto looks at the tags
67
+ # in the request's scope: a container present means the dataLayer,
68
+ # otherwise any Google tag means gtag.
69
+ def event_transport_for(request = nil)
70
+ return config.event_transport unless config.event_transport == :auto
71
+
72
+ providers = tags_for(request)
73
+ return :data_layer if providers.any? { |provider| provider.is_a?(Providers::GoogleTagManager) }
74
+
75
+ providers.any?(&:google?) ? :gtag : :data_layer
76
+ end
77
+
63
78
  # Whether this visitor has to be asked. When they do not, every category
64
79
  # counts as granted and no banner is rendered - see config.consent_required.
65
80
  def consent_required?(request = nil)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consently
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michał Krzysteczko
@@ -58,6 +58,7 @@ files:
58
58
  - app/helpers/consently/tags_helper.rb
59
59
  - app/javascript/consently/banner_controller.js
60
60
  - app/javascript/consently/embed_controller.js
61
+ - app/javascript/consently/events.js
61
62
  - app/models/consently/application_record.rb
62
63
  - app/models/consently/consent_record.rb
63
64
  - app/views/consently/_banner.html.erb
@@ -80,6 +81,7 @@ files:
80
81
  - lib/consently/consent.rb
81
82
  - lib/consently/cookie.rb
82
83
  - lib/consently/engine.rb
84
+ - lib/consently/event.rb
83
85
  - lib/consently/providers.rb
84
86
  - lib/consently/providers/base.rb
85
87
  - lib/consently/providers/clarity.rb
@@ -121,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
123
  - !ruby/object:Gem::Version
122
124
  version: '0'
123
125
  requirements: []
124
- rubygems_version: 4.0.3
126
+ rubygems_version: 4.0.18
125
127
  specification_version: 4
126
128
  summary: GDPR cookie consent for Rails that actually blocks your analytics tags until
127
129
  the visitor agrees