diff --git a/Gemfile b/Gemfile index a92461c6..42da1d19 100644 --- a/Gemfile +++ b/Gemfile @@ -99,3 +99,5 @@ gem "simple_form", "~> 5.0.0" gem "sitemap_generator", require: false gem "sucker_punch" gem "faraday" + +gem "ahoy_matey", "~> 5.1" diff --git a/Gemfile.lock b/Gemfile.lock index 2d40083d..1815cd0f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,6 +72,10 @@ GEM tzinfo (~> 2.0) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) + ahoy_matey (5.1.0) + activesupport (>= 6.1) + device_detector (>= 1) + safely_block (>= 0.4) annotate (3.2.0) activerecord (>= 3.2, < 8.0) rake (>= 10.4, < 14.0) @@ -131,6 +135,7 @@ GEM paint (>= 0.9, < 3.0) decent_exposure (3.0.4) activesupport (>= 4.0) + device_detector (1.1.2) devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -382,6 +387,7 @@ GEM ruby_engine (2.0.0) ruby_version (1.0.3) rubyzip (2.3.2) + safely_block (0.4.0) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -453,6 +459,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + ahoy_matey (~> 5.1) annotate bootsnap brakeman diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7c014a20..d9d09ef9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ class ApplicationController < ActionController::Base + include Analytics protect_from_forgery with: :exception before_action do diff --git a/app/controllers/concerns/analytics.rb b/app/controllers/concerns/analytics.rb new file mode 100644 index 00000000..26d2504c --- /dev/null +++ b/app/controllers/concerns/analytics.rb @@ -0,0 +1,13 @@ +module Analytics + extend ActiveSupport::Concern + + included do + after_action :track_action + end + + private + + def track_action + ahoy.track "#{controller_path}##{action_name}", request.path_parameters + end +end diff --git a/app/models/ahoy/event.rb b/app/models/ahoy/event.rb new file mode 100644 index 00000000..4b6953d5 --- /dev/null +++ b/app/models/ahoy/event.rb @@ -0,0 +1,26 @@ +# == Schema Information +# +# Table name: ahoy_events +# +# id :bigint not null, primary key +# visit_id :bigint +# user_id :bigint +# name :string +# properties :jsonb +# time :datetime +# +# Indexes +# +# index_ahoy_events_on_name_and_time (name,time) +# index_ahoy_events_on_properties (properties) USING gin +# index_ahoy_events_on_user_id (user_id) +# index_ahoy_events_on_visit_id (visit_id) +# +class Ahoy::Event < ApplicationRecord + include Ahoy::QueryMethods + + self.table_name = "ahoy_events" + + belongs_to :visit + belongs_to :user, optional: true +end diff --git a/app/models/ahoy/visit.rb b/app/models/ahoy/visit.rb new file mode 100644 index 00000000..910ad187 --- /dev/null +++ b/app/models/ahoy/visit.rb @@ -0,0 +1,43 @@ +# == Schema Information +# +# Table name: ahoy_visits +# +# id :bigint not null, primary key +# visit_token :string +# visitor_token :string +# user_id :bigint +# ip :string +# user_agent :text +# referrer :text +# referring_domain :string +# landing_page :text +# browser :string +# os :string +# device_type :string +# country :string +# region :string +# city :string +# latitude :float +# longitude :float +# utm_source :string +# utm_medium :string +# utm_term :string +# utm_content :string +# utm_campaign :string +# app_version :string +# os_version :string +# platform :string +# started_at :datetime +# +# Indexes +# +# index_ahoy_visits_on_user_id (user_id) +# index_ahoy_visits_on_visit_token (visit_token) UNIQUE +# index_ahoy_visits_on_visitor_token_and_started_at (visitor_token,started_at) +# +class Ahoy::Visit < ApplicationRecord + self.table_name = "ahoy_visits" + + has_many :events, class_name: "Ahoy::Event" + belongs_to :user, optional: true +end diff --git a/config/initializers/ahoy.rb b/config/initializers/ahoy.rb new file mode 100644 index 00000000..8be3948d --- /dev/null +++ b/config/initializers/ahoy.rb @@ -0,0 +1,17 @@ +class Ahoy::Store < Ahoy::DatabaseStore + def authenticate(data) + # disables automatic linking of visits and users for GDPR compliance + end +end + +# remove cookie tracking and mask ips for GDPR compliance +Ahoy.mask_ips = true +Ahoy.cookies = :none + +# set to true for JavaScript tracking +Ahoy.api = false + +# set to true for geocoding (and add the geocoder gem to your Gemfile) +# we recommend configuring local geocoding as well +# see https://github.com/ankane/ahoy#geocoding +Ahoy.geocode = false diff --git a/db/migrate/20240403065507_create_ahoy_visits_and_events.rb b/db/migrate/20240403065507_create_ahoy_visits_and_events.rb new file mode 100644 index 00000000..f2b36648 --- /dev/null +++ b/db/migrate/20240403065507_create_ahoy_visits_and_events.rb @@ -0,0 +1,62 @@ +class CreateAhoyVisitsAndEvents < ActiveRecord::Migration[7.0] + def change + create_table :ahoy_visits do |t| + t.string :visit_token + t.string :visitor_token + + # the rest are recommended but optional + # simply remove any you don't want + + # user + t.references :user + + # standard + t.string :ip + t.text :user_agent + t.text :referrer + t.string :referring_domain + t.text :landing_page + + # technology + t.string :browser + t.string :os + t.string :device_type + + # location + t.string :country + t.string :region + t.string :city + t.float :latitude + t.float :longitude + + # utm parameters + t.string :utm_source + t.string :utm_medium + t.string :utm_term + t.string :utm_content + t.string :utm_campaign + + # native apps + t.string :app_version + t.string :os_version + t.string :platform + + t.datetime :started_at + end + + add_index :ahoy_visits, :visit_token, unique: true + add_index :ahoy_visits, [ :visitor_token, :started_at ] + + create_table :ahoy_events do |t| + t.references :visit + t.references :user + + t.string :name + t.jsonb :properties + t.datetime :time + end + + add_index :ahoy_events, [ :name, :time ] + add_index :ahoy_events, :properties, using: :gin, opclass: :jsonb_path_ops + end +end diff --git a/db/schema.rb b/db/schema.rb index 7b5e91e3..3e5542c7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_04_02_114104) do +ActiveRecord::Schema[7.0].define(version: 2024_04_07_192602) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -54,6 +54,49 @@ t.index ["item", "table", "month", "year"], name: "index_rails_admin_histories" end + create_table "ahoy_events", force: :cascade do |t| + t.bigint "visit_id" + t.bigint "user_id" + t.string "name" + t.jsonb "properties" + t.datetime "time" + t.index ["name", "time"], name: "index_ahoy_events_on_name_and_time" + t.index ["properties"], name: "index_ahoy_events_on_properties", opclass: :jsonb_path_ops, using: :gin + t.index ["user_id"], name: "index_ahoy_events_on_user_id" + t.index ["visit_id"], name: "index_ahoy_events_on_visit_id" + end + + create_table "ahoy_visits", force: :cascade do |t| + t.string "visit_token" + t.string "visitor_token" + t.bigint "user_id" + t.string "ip" + t.text "user_agent" + t.text "referrer" + t.string "referring_domain" + t.text "landing_page" + t.string "browser" + t.string "os" + t.string "device_type" + t.string "country" + t.string "region" + t.string "city" + t.float "latitude" + t.float "longitude" + t.string "utm_source" + t.string "utm_medium" + t.string "utm_term" + t.string "utm_content" + t.string "utm_campaign" + t.string "app_version" + t.string "os_version" + t.string "platform" + t.datetime "started_at" + t.index ["user_id"], name: "index_ahoy_visits_on_user_id" + t.index ["visit_token"], name: "index_ahoy_visits_on_visit_token", unique: true + t.index ["visitor_token", "started_at"], name: "index_ahoy_visits_on_visitor_token_and_started_at" + end + create_table "sponsors", force: :cascade do |t| t.string "name" t.string "website" diff --git a/test/system/home_page_test.rb b/test/system/home_page_test.rb index 61eb320e..e4909a9a 100644 --- a/test/system/home_page_test.rb +++ b/test/system/home_page_test.rb @@ -10,11 +10,13 @@ class HomePageSystemTest < ApplicationSystemTestCase end end - test "Update the session count" do + test "display the home page and track visits" do visit root_path assert_text "Le plus grand meetup Ruby d'Europe", normalize_ws: true assert_selector ".section-sponsors img", count: 4 assert_selector "[alt='#{@active_sponsor.name}']", count: 1 assert_selector "[alt='#{@permanent_sponsor.name}']", count: 1 + assert_equal 1, Ahoy::Visit.count + assert_equal 1, Ahoy::Event.count end end