Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.7.0] - 2026-04-27

### Changed
- `install_generator`: removed `inject_body_tracking_attributes` — the generator no longer patches `app/views/layouts/application.html.erb` automatically. Add the `data-track-*` attributes to your `<body>` tag manually (instructions printed in the post-install message and documented in the README)

### Added
- POST_INSTALL message now includes the exact `<body>` snippet to copy
- README: new "JavaScript — client-side page visit tracking" section documenting the `action_trace.js` script and the required body attributes, including a conditional variant for unauthenticated pages

## [0.6.0] - 2026-04-14

### Changed
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
action_trace (0.6.0)
action_trace (0.7.0)
ahoy_matey (>= 5)
discard (>= 1)
interactor (> 3)
Expand Down Expand Up @@ -356,7 +356,7 @@ DEPENDENCIES

CHECKSUMS
action_text-trix (2.1.17) sha256=b44691639d77e67169dc054ceacd1edc04d44dc3e4c6a427aa155a2beb4cc951
action_trace (0.6.0)
action_trace (0.7.0)
actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969
actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b
actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,26 @@ end

For soft-delete tracking with discard, add `include Discard::Model` alongside `DataTrackable`. The `data_destroy` event is still recorded via the `before_destroy` callback.

### JavaScript — client-side page visit tracking

The installer copies `app/javascript/action_trace.js` and imports it from `application.js`. The script reads data attributes from the `<body>` tag on every Turbo page load and fires an Ahoy `page_visit` event.

You must add those attributes to your layout manually:

```erb
<body
data-track-controller="<%= controller_name %>"
data-track-action="<%= action_name %>"
data-track-company-id="<%= current_user.company_id %>"
data-track-method="<%= request.method %>">
```

If the `data-track-controller` attribute is absent the script exits early and no event is sent, so pages where the user is not logged in (and `current_user` is nil) are safe as long as you omit the attributes conditionally:

```erb
<body <%= current_user ? %(data-track-controller="#{controller_name}" data-track-action="#{action_name}" data-track-company-id="#{current_user.company_id}" data-track-method="#{request.method}") : "" %>>
```

### Controllers — tracking page visits and sessions

Include `ActivityTrackable` in any controller (or `ApplicationController`) to track page visits:
Expand Down
2 changes: 1 addition & 1 deletion lib/action_trace/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module ActionTrace
VERSION = '0.6.0'
VERSION = '0.7.0'
end
14 changes: 13 additions & 1 deletion lib/generators/action_trace/install/POST_INSTALL
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,19 @@

include ActionTrace::DataTrackable

5. (Optional) Customize views:
5. Add data attributes to your layout's <body> tag so the JavaScript
tracker can read the current controller, action, company and HTTP method:

<body
data-track-controller="<%= controller_name %>"
data-track-action="<%= action_name %>"
data-track-company-id="<%= current_user.company_id %>"
data-track-method="<%= request.method %>">

The generated app/javascript/action_trace.js reads these attributes on
every Turbo page load and sends a page_visit event via Ahoy.

6. (Optional) Customize views:

rails generate action_trace:views
rails generate action_trace:views --controller
Expand Down
32 changes: 31 additions & 1 deletion lib/generators/action_trace/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ def self.next_migration_number(dirname)
desc: 'Skip discard generator (already installed)'

def run_ahoy_install
generate 'ahoy:install' unless options[:skip_ahoy]
unless options[:skip_ahoy]
generate 'ahoy:install'
patch_ahoy_initializer
end
inject_ahoy_filter_by_company
end

Expand All @@ -52,8 +55,35 @@ def create_initializer
template 'initializers/action_trace.rb.tt', 'config/initializers/action_trace.rb'
end

def pin_ahoy_for_importmap
return if options[:skip_ahoy]
return unless File.exist?('config/importmap.rb')

append_to_file 'config/importmap.rb', %(pin "ahoy", to: "ahoy.js"\n)
end

def import_ahoy_in_javascript
return if options[:skip_ahoy]
return unless File.exist?('app/javascript/application.js')

append_to_file 'app/javascript/application.js', %(import "ahoy"\n)
end

def create_javascript_tracking_file
return if options[:skip_ahoy]
return unless File.exist?('app/javascript')

template 'action_trace.js.tt', 'app/javascript/action_trace.js'
append_to_file 'app/javascript/application.js', %(import "./action_trace"\n) if File.exist?('app/javascript/application.js')
end

private

def patch_ahoy_initializer
gsub_file 'config/initializers/ahoy.rb', 'Ahoy.api = false', 'Ahoy.api = true'
append_to_file 'config/initializers/ahoy.rb', "\nAhoy.server_side_visits = false\n"
end

def inject_ahoy_filter_by_company
filter_method = <<~RUBY

Expand Down
14 changes: 14 additions & 0 deletions lib/generators/action_trace/install/templates/action_trace.js.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "ahoy"

document.addEventListener('turbo:load', () => {
const { trackController, trackAction, trackCompanyId, trackMethod } = document.body.dataset
if (!trackController) return

ahoy.track('page_visit', {
path: window.location.pathname,
controller: trackController,
action: trackAction,
company_id: trackCompanyId,
method: trackMethod
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,6 @@ ActionTrace.configure do |config|
# config.log_retention_period = 1.year # default: 1.year
end

class Ahoy::Store < Ahoy::DatabaseStore
def track_visit(data)
super(data)
end

def track_event(data)
super(data)
end
end

Ahoy.api = false
Ahoy.geocode = false
Ahoy.mask_ips = true
Ahoy.cookies = true

PaperTrail.config do |config|
config.enabled = true
config.version_limit = 10
Expand Down
Loading