A comprehensive Ruby gem for tracking user behavior events with multi-tenant support, visit/session management, device detection, geographic analytics, and advanced querying capabilities with enterprise-grade features.
- Flexible Context Tracking: Track events with multi-tenant support, user types, and custom filters
- Event Buffering: Efficient batch processing with configurable buffer size and flush intervals
- Visit/Session Management: Automatic visit tracking with session management, visitor identification, and visit analytics
- Device & Browser Detection: Automatic device, browser, and OS detection from user agents
- Geographic Analytics: Country, city, and region tracking from IP addresses
- Referrer Analytics: UTM parameter tracking, source analysis, and search keyword extraction
- Comprehensive Analytics:
- Event counts and aggregations
- Engagement scoring with customizable weights
- Time-based analytics (hourly, daily, weekly, monthly)
- Feature usage statistics
- Funnel analysis
- Cohort analysis
- Retention tracking
- Geographic Analytics: Country/city breakdowns, device/browser statistics
- Referrer Analytics: Traffic source analysis, UTM campaign tracking, search keyword insights
- Multiple Storage Adapters:
- ActiveRecord adapter for production use
- In-memory adapter for testing
- Redis adapter for high-throughput scenarios
- Elasticsearch adapter for advanced search
- Kafka adapter for event streaming
- Async Processing: Background job support (Sidekiq, DelayedJob, ActiveJob)
- Event Streaming: Real-time event pub/sub system
- Simplified API: Simple tracking API with automatic context resolution
- Rails Integration: Automatic API call tracking via middleware with selective tracking
- Query Interface: Fluent query builder for filtering events with advanced aggregations
- JavaScript Client: Frontend tracking with automatic page views and click tracking
- Data Retention: Automatic cleanup policies for visits and events
- Ruby >= 3.0.0
- Rails >= 6.0 (for Rails integration)
- ActiveSupport >= 6.0
Add this line to your application's Gemfile:
gem 'behavior_analytics', '~> 2.2'And then execute:
$ bundle installOr install it directly:
$ gem install behavior_analyticsThe latest stable version is 2.2.2. See CHANGELOG.md for version history.
rails generate behavior_analytics:installThis will:
- Create migrations for the
behavior_eventsandbehavior_visitstables - Create an initializer at
config/initializers/behavior_analytics.rb - Create models at
app/models/behavior_analytics_event.rbandapp/models/behavior_analytics_visit.rb
Note: If you're upgrading from v2.1.x or earlier, you'll need to run the new migrations for visit tracking features.
rails db:migrateThis will create:
behavior_eventstable for event trackingbehavior_visitstable for visit/session tracking- Indexes for optimal query performance
Edit config/initializers/behavior_analytics.rb:
BehaviorAnalytics.configure do |config|
# Configure storage adapter (required)
config.storage_adapter = BehaviorAnalytics::Storage::ActiveRecordAdapter.new(
model_class: BehaviorAnalyticsEvent
)
# Configure batching
config.batch_size = 100
config.flush_interval = 300 # 5 minutes
# Configure context resolver (optional)
config.context_resolver = ->(request) {
{
tenant_id: current_tenant&.id,
user_id: current_user&.id,
user_type: current_user&.account_type
}
}
# Configure engagement scoring weights
config.scoring_weights = {
activity: 0.4,
unique_users: 0.3,
feature_diversity: 0.2,
time_in_trial: 0.1
}
# Visit/Session Management (v2.2+)
config.track_visits = true # Enable visit tracking (requires migrations)
config.visit_duration = 30.minutes # Visit expires after 30 min of inactivity
config.track_device_info = true # Auto-detect device, browser, OS
config.track_geolocation = true # Auto-detect country/city from IP
config.device_detector = :simple # :simple, :browser, or :user_agent_parser
# Note: For better device detection, add 'browser' or 'user_agent_parser' gem
# For geolocation, add 'geocoder' gem
# Data Retention
config.visit_retention_days = 90 # Keep visits for 90 days
config.event_retention_days = 365 # Keep events for 1 year
# Single-tenant support
config.default_tenant_id = "global" # For non-multi-tenant systems
# Optional: Advanced features
config.use_async = false # Enable async processing
config.debug_mode = Rails.env.development? # Enable debug logging
endclass ApplicationController < ActionController::Base
include BehaviorAnalytics::Integrations::Rails
endInclude the JavaScript client in your layout:
<!-- app/views/layouts/application.html.erb -->
<%= javascript_include_tag 'behavior_analytics' %>Or use the inline script generator:
<script>
<%= BehaviorAnalytics::Javascript::Client.generate_script(
tracker_url: behavior_analytics_track_path,
auto_track: true
).html_safe %>
</script>The gem provides a simplified API for easy tracking without needing to create context objects manually:
# Simple event tracking with automatic context resolution
BehaviorAnalytics.track("button_click", properties: { button: "signup" })
# Page view tracking
BehaviorAnalytics.track_page_view(path: "/dashboard")
# Click tracking
BehaviorAnalytics.track_click(element: "signup_button", properties: { location: "header" })
# Conversion tracking
BehaviorAnalytics.track_conversion(conversion_name: "signup", value: 99.99)
# Or use the helper methods in controllers
class ApplicationController < ActionController::Base
include BehaviorAnalytics::Helpers::TrackingHelper
def create
# ... create logic ...
track_event("project_created", properties: { project_id: @project.id })
end
endVisits are automatically created and tracked when visit tracking is enabled:
# Visits are automatically created on first request
# You can access the current visit in your controllers:
class ApplicationController < ActionController::Base
include BehaviorAnalytics::Integrations::Rails
def current_visit
@current_visit ||= visit_manager&.find_or_create_visit(
visitor_token: visit_auto_creator.get_or_create_visitor_token(request),
tenant_id: current_tenant&.id,
user_id: current_user&.id,
ip: request.ip,
user_agent: request.user_agent,
referrer: request.referer,
landing_page: request.path
)
end
end
# Query visits
tracker = BehaviorAnalytics.create_tracker
visit_manager = BehaviorAnalytics::Visits::Manager.new(
storage_adapter: tracker.storage_adapter
)
# Get user's visits
user_visits = visit_manager.find_visits_by_user(user_id, limit: 100)
# Get visitor's visits (anonymous)
visitor_visits = visit_manager.find_visits_by_visitor(visitor_token, limit: 100)
# Link anonymous visits to user on login
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
visit_manager: visit_manager
)
user_resolver.identify_user(user_id, visitor_token: visitor_token)Device information is automatically detected and stored in visits:
# Device detection happens automatically when track_device_info is enabled
# Visit will include:
# - browser: "Chrome", "Safari", "Firefox", etc.
# - os: "iOS", "Android", "Windows", "Mac OS", etc.
# - device_type: "mobile", "tablet", "desktop"
# You can also manually detect device info:
detector = BehaviorAnalytics::Detection::DeviceDetector.new(strategy: :simple)
device_info = detector.detect(request.user_agent)
# => { browser: "Chrome", os: "Mac OS", device_type: "desktop" }Geographic information is automatically detected from IP addresses:
# Geolocation happens automatically when track_geolocation is enabled
# Visit will include:
# - country: "United States"
# - city: "San Francisco"
# - country_code: "US"
# Query geographic analytics
analytics = tracker.analytics
# Country breakdown
countries = analytics.geographic.country_breakdown(context)
# => [{ country: "United States", count: 150 }, { country: "Canada", count: 50 }]
# City breakdown
cities = analytics.geographic.city_breakdown(context)
# => [{ city: "San Francisco", count: 75 }, { city: "New York", count: 50 }]
# Device breakdown
devices = analytics.geographic.device_breakdown(context)
# => [{ device: "desktop", count: 100 }, { device: "mobile", count: 50 }]
# Browser breakdown
browsers = analytics.geographic.browser_breakdown(context)
# => [{ browser: "Chrome", count: 120 }, { browser: "Safari", count: 30 }]Track traffic sources, UTM parameters, and search keywords:
analytics = tracker.analytics
# Traffic source breakdown
sources = analytics.referrer.source_breakdown(context)
# => [{ source: "google", count: 100 }, { source: "direct", count: 50 }]
# UTM source breakdown
utm_sources = analytics.referrer.utm_source_breakdown(context)
# => [{ utm_source: "newsletter", count: 75 }, { utm_source: "social", count: 25 }]
# UTM campaign breakdown
campaigns = analytics.referrer.utm_campaign_breakdown(context)
# => [{ utm_campaign: "summer_sale", count: 50 }, { utm_campaign: "winter_promo", count: 30 }]
# Search keyword breakdown
keywords = analytics.referrer.search_keyword_breakdown(context)
# => [{ keyword: "ruby gem", count: 20 }, { keyword: "analytics", count: 15 }]
# Referring domain breakdown
domains = analytics.referrer.referring_domain_breakdown(context)
# => [{ domain: "google.com", count: 100 }, { domain: "twitter.com", count: 25 }]The gem is flexible and supports different business scenarios:
Track events with tenant isolation for SaaS applications:
context = BehaviorAnalytics::Context.new(
tenant_id: "org_123",
user_id: "user_456",
user_type: "premium"
)Track events for regular web applications without tenant concept:
# Option A: Set default tenant (recommended)
BehaviorAnalytics.configure do |config|
config.default_tenant_id = "global"
end
context = BehaviorAnalytics::Context.new(
user_id: current_user.id,
user_type: "admin"
)
# Option B: Track without tenant_id (uses session_id or user_id as identifier)
context = BehaviorAnalytics::Context.new(
user_id: current_user.id
)Track API calls without user context (for monitoring, analytics, etc.):
# Track API calls directly without user context
tracker.track_api_call(
context: BehaviorAnalytics::Context.new, # Empty context - uses session_id from request
method: "POST",
path: "/api/endpoint",
status_code: 200,
duration_ms: 150
)
# Or with minimal context
context = BehaviorAnalytics::Context.new(
filters: { environment: "production", service: "api" }
)Track events for anonymous users or public pages:
context = BehaviorAnalytics::Context.new(
filters: { page: "homepage", referrer: request.referer }
)
tracker.track(
context: context,
event_name: "page_view",
metadata: { path: request.path }
)# Create a tracker
tracker = BehaviorAnalytics.create_tracker
# Multi-tenant example
context = BehaviorAnalytics::Context.new(
tenant_id: "org_123",
user_id: "user_456",
user_type: "trial"
)
# Single-tenant example (with default tenant)
context = BehaviorAnalytics::Context.new(
user_id: "user_456",
user_type: "trial"
)
# API-only example (no user context)
context = BehaviorAnalytics::Context.new
# Track a custom event
tracker.track(
context: context,
event_name: "project_created",
metadata: { project_id: 789 }
)
# Track an API call
tracker.track_api_call(
context: context,
method: "POST",
path: "/api/projects",
status_code: 201,
duration_ms: 150
)
# Track feature usage
tracker.track_feature_usage(
context: context,
feature: "advanced_search",
metadata: { query: "..." }
)
# Flush buffered events
tracker.flushanalytics = tracker.analytics
# Basic counts
event_count = analytics.event_count(context, since: 7.days.ago)
unique_users = analytics.unique_users(context)
active_days = analytics.active_days(context)
# Engagement scoring
score = analytics.engagement_score(context)
# => 75.5
# Time-based analytics
timeline = analytics.activity_timeline(context, period: :daily)
# => { 2024-01-01 => 10, 2024-01-02 => 15, ... }
daily = analytics.daily_activity(context, date_range: 7.days.ago..Time.current)
# Feature usage
feature_stats = analytics.feature_usage_stats(context)
# => { "projects" => 25, "search" => 10, ... }
top_features = analytics.top_features(context, limit: 10)
# Funnel analysis
funnel = analytics.funnels.create(
name: "signup_funnel",
steps: ["page_view", "signup_form", "signup_submit", "email_verified"]
)
conversion_rate = funnel.conversion_rate(context)
# Cohort analysis
cohort = analytics.cohorts.create(
name: "signup_cohort",
event_name: "signup",
period: :monthly
)
cohort_data = cohort.analyze(context)
# Retention analysis
retention = analytics.retention.calculate(
context: context,
event_name: "login",
period: :weekly
)Automatically clean up old visits and events:
# Configure retention policy
retention_policy = BehaviorAnalytics::Cleanup::RetentionPolicy.new(
visit_retention_days: 90,
event_retention_days: 365
)
# Create scheduler
scheduler = BehaviorAnalytics::Cleanup::Scheduler.new(
storage_adapter: tracker.storage_adapter,
retention_policy: retention_policy
)
# Cleanup old data
results = scheduler.cleanup_all
# => { visits: 1500, events: 50000 }
# Or cleanup separately
deleted_visits = scheduler.cleanup_visits
deleted_events = scheduler.cleanup_events
# Schedule cleanup job (e.g., in a cron job or scheduled task)
# config/schedule.rb (whenever gem)
every 1.day, at: '2:00 am' do
runner "BehaviorAnalytics::Cleanup::Scheduler.new(
storage_adapter: BehaviorAnalytics.configuration.storage_adapter,
retention_policy: BehaviorAnalytics::Cleanup::RetentionPolicy.new(
visit_retention_days: BehaviorAnalytics.configuration.visit_retention_days,
event_retention_days: BehaviorAnalytics.configuration.event_retention_days
)
).cleanup_all"
endquery = tracker.query
# Build complex queries
events = query
.for_tenant("org_123")
.for_user_type("trial")
.with_event_type(:feature_usage)
.since(7.days.ago)
.limit(100)
.execute
# Count events
count = query
.for_tenant("org_123")
.with_event_name("project_created")
.count
# Advanced filtering
events = query
.with_metadata(key: "feature", value: "advanced_search")
.with_path("/api/search")
.with_method("POST")
.with_status_code(200)
.group_by(:user_id)
.aggregate(field: :duration_ms, function: :avg)
.execute
# Visit-based queries
visit_events = query
.for_visit(visit_token)
.with_event_type(:custom)
.executeThe JavaScript client provides automatic frontend tracking. The client file is included in the gem at vendor/assets/javascripts/behavior_analytics.js:
// Automatic tracking (enabled by default)
// - Page views on load
// - Clicks on elements with data-track attribute
// - Form submissions with data-track attribute
// Manual tracking
BehaviorAnalytics.track("button_click", { button: "signup" });
BehaviorAnalytics.trackPageView();
BehaviorAnalytics.trackClick(element, { location: "header" });
// HTML attributes for automatic tracking
<button data-track="signup_click" data-track-properties='{"plan": "premium"}'>
Sign Up
</button>
<form data-track="newsletter_signup">
<!-- form fields -->
</form>Track anonymous visitors and merge with user accounts:
# Identify user on login (merges anonymous visits)
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
visit_manager: visit_manager
)
# On user login
user_resolver.identify_user(
user_id: current_user.id,
visitor_token: cookies[:behavior_visitor_token]
)
# Get all visits for a user (including anonymous visits before login)
user_visits = user_resolver.get_user_visits(user_id)
# Get visits for anonymous visitor
visitor_visits = user_resolver.get_visitor_visits(visitor_token)class MyCustomAdapter < BehaviorAnalytics::Storage::Adapter
def save_events(events)
# Your implementation
end
def events_for_context(context, options = {})
# Your implementation
end
# ... implement other required methods
end
tracker = BehaviorAnalytics.create_tracker(
storage_adapter: MyCustomAdapter.new
)storage_adapter: Storage adapter instance (required)batch_size: Number of events to buffer before flushing (default: 100)flush_interval: Seconds between automatic flushes (default: 300)context_resolver: Lambda/proc to resolve context from requestsscoring_weights: Hash of weights for engagement scoringdefault_tenant_id: Default tenant ID for single-tenant systems (default: "default")
track_visits: Enable visit tracking (default: false)visit_duration: Visit expiration time after inactivity (default: 30.minutes)track_device_info: Enable automatic device/browser detection (default: false)track_geolocation: Enable automatic geographic detection from IP (default: false)device_detector: Device detection strategy -:simple,:browser, or:user_agent_parser(default::simple)
visit_retention_days: Days to keep visits before cleanup (default: 90)event_retention_days: Days to keep events before cleanup (default: 365)
use_async: Enable async event processing (default: false)async_processor: Async processor instance (Sidekiq, DelayedJob, ActiveJob)event_stream: Event streaming instance for pub/subhooks_manager: Event hooks manager for lifecycle callbackssampling_strategy: Event sampling strategyrate_limiter: Rate limiting configurationschema_validator: Event schema validatormetrics: Metrics collection instancetracer: Distributed tracing instancedebug_mode: Enable debug logging (default: development mode)logger: Custom logger instance
tracking_whitelist: Array of path patterns to whitelist (nil = track all)tracking_blacklist: Array of path patterns to blacklist (default: [])skip_bots: Skip tracking for bot user agents (default: true)controller_action_filters: Hash of controllers/actions to filterslow_query_threshold: Log slow queries above threshold (ms)track_middleware_requests: Track requests via middleware (default: false)
:api_call- HTTP API requests (automatically tracked via Rails integration):feature_usage- Feature usage events:custom- Custom business events
Visits track user sessions and include:
visit_token: Unique identifier for the visitvisitor_token: Anonymous visitor identifier (persists across visits)tenant_id: Multi-tenant identifieruser_id: User identifier (set when user logs in)ip: IP addressuser_agent: Browser user agent stringreferrer: HTTP referrerlanding_page: First page visited in sessionbrowser: Detected browser (Chrome, Safari, Firefox, etc.)os: Detected operating system (iOS, Android, Windows, etc.)device_type: Device type (mobile, tablet, desktop)country: Country from IP geolocationcity: City from IP geolocationutm_source,utm_medium,utm_campaign,utm_term,utm_content: UTM parametersreferring_domain: Extracted referring domainsearch_keyword: Extracted search keyword from search enginesstarted_at: Visit start timeended_at: Visit end time (null for active visits)
The Context class encapsulates tracking context and is flexible to support different business cases:
tenant_id(optional) - Multi-tenant identifier. Only required for multi-tenant systemsuser_id(optional) - User identifier. Useful for user-based analyticsuser_type(optional) - User type (e.g., "trial", "premium", "admin")filters(optional) - Hash of custom filter criteria for additional context
A context is valid if it has at least one identifier:
tenant_id(for multi-tenant systems)user_id(for user-based tracking)filterswith identifying information (for anonymous/public tracking)session_id(automatically added for API calls)
This allows the gem to support:
- ✅ Multi-tenant SaaS applications
- ✅ Single-tenant web applications
- ✅ API monitoring without user context
- ✅ Anonymous/public page tracking
Multi-Tenant SaaS:
context = BehaviorAnalytics::Context.new(
tenant_id: "org_123", # Required
user_id: "user_456",
user_type: "premium"
)Single-Tenant Web App:
# Set default tenant (optional but recommended)
BehaviorAnalytics.configure do |config|
config.default_tenant_id = "global"
end
# Track with just user_id
context = BehaviorAnalytics::Context.new(
user_id: current_user.id,
user_type: current_user.role
)API-Only Tracking:
# Track API calls without user context
context = BehaviorAnalytics::Context.new # Empty context - session_id will be used
tracker.track_api_call(
context: context,
method: "POST",
path: "/api/endpoint",
status_code: 200
)Anonymous/Public Tracking:
context = BehaviorAnalytics::Context.new(
filters: {
page_type: "public",
referrer: request.referer
}
)
tracker.track(context: context, event_name: "page_view")Process events asynchronously using background jobs:
# Configure async processor
BehaviorAnalytics.configure do |config|
config.use_async = true
config.async_processor = BehaviorAnalytics::Processors::BackgroundJobProcessor.new(
job_class: BehaviorAnalytics::Jobs::ActiveEventJob
)
end
# Events will be processed in background
tracker.track(context: context, event_name: "event")Execute callbacks on event lifecycle:
BehaviorAnalytics.configuration.hooks_manager.register_before_track do |event, context|
# Modify event before tracking
event[:metadata][:custom_field] = "value"
end
BehaviorAnalytics.configuration.hooks_manager.register_after_track do |event, context|
# Execute after tracking
NotificationService.notify(event)
endSample events to reduce storage:
sampling_strategy = BehaviorAnalytics::Sampling::Strategy.new(
sample_rate: 0.1 # Sample 10% of events
)
BehaviorAnalytics.configuration.sampling_strategy = sampling_strategyLimit events per context:
rate_limiter = BehaviorAnalytics::Throttling::Limiter.new(
max_events_per_minute: 100
)
BehaviorAnalytics.configuration.rate_limiter = rate_limiterValidate events against JSON schemas:
schema = BehaviorAnalytics::Schema::Definition.new(
event_name: "signup",
schema: {
type: "object",
properties: {
email: { type: "string" },
plan: { type: "string", enum: ["basic", "premium"] }
},
required: ["email"]
}
)
validator = BehaviorAnalytics::Schema::Validator.new
validator.register_schema(schema)
BehaviorAnalytics.configuration.schema_validator = validatorv2 is backward compatible with v1. Existing code will continue to work:
# v1 code still works
tracker.track(context: context, event_name: "event")
# New v2 features are opt-in
BehaviorAnalytics.configure do |config|
config.track_visits = true # Enable new features
end- Run migrations:
rails db:migrate- Enable in configuration:
BehaviorAnalytics.configure do |config|
config.track_visits = true
config.track_device_info = true
config.track_geolocation = true
end- Visits will be automatically created on requests
If you have existing events and want to link them to visits:
# Create visits for existing events (one-time migration)
BehaviorAnalyticsEvent.find_each do |event|
visit = visit_manager.find_or_create_visit(
visitor_token: generate_visitor_token_for_event(event),
tenant_id: event.tenant_id,
user_id: event.user_id,
ip: event.ip,
user_agent: event.user_agent
)
event.update(visit_id: visit.visit_token, visitor_id: visit.visitor_token)
endThe migrations create indexes for optimal query performance. For high-volume applications, consider:
- Additional composite indexes based on your query patterns
- Partitioning large tables by date
- Archiving old data to separate tables
Configure appropriate batch sizes based on your volume:
# High volume
config.batch_size = 500
config.flush_interval = 60 # 1 minute
# Low volume
config.batch_size = 50
config.flush_interval = 600 # 10 minutesFor high-throughput scenarios, use async processing:
config.use_async = true
config.async_processor = BehaviorAnalytics::Processors::BackgroundJobProcessor.new(
job_class: BehaviorAnalytics::Jobs::SidekiqEventJob
)- Check that
track_visitsis enabled:
BehaviorAnalytics.configuration.track_visits # => true- Verify migrations are run:
rails db:migrate:status- Check Rails integration is included:
class ApplicationController < ActionController::Base
include BehaviorAnalytics::Integrations::Rails
end- Ensure
track_device_infois enabled - Check user agent is being passed:
request.user_agent # Should not be nil- For better detection, use a gem:
# Gemfile
gem 'browser' # or 'user_agent_parser'
# config
config.device_detector = :browser- Ensure
track_geolocationis enabled - Install geocoding gem:
# Gemfile
gem 'geocoder'
# Will automatically use Geocoder for IP lookup- For production, consider MaxMind GeoIP2 database
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests.
To install this gem onto your local machine, run bundle exec rake install.
bundle exec rspecgem build behavior_analytics.gemspecgem push behavior_analytics-x.x.x.gemHere's a complete example showing all features:
# config/initializers/behavior_analytics.rb
BehaviorAnalytics.configure do |config|
config.storage_adapter = BehaviorAnalytics::Storage::ActiveRecordAdapter.new(
model_class: BehaviorAnalyticsEvent
)
# Visit tracking
config.track_visits = true
config.visit_duration = 30.minutes
config.track_device_info = true
config.track_geolocation = true
# Data retention
config.visit_retention_days = 90
config.event_retention_days = 365
# Single-tenant
config.default_tenant_id = "my_app"
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include BehaviorAnalytics::Integrations::Rails
include BehaviorAnalytics::Helpers::TrackingHelper
private
def identify_user_after_login
if user_signed_in?
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
visit_manager: visit_manager
)
user_resolver.identify_user(
current_user.id,
visitor_token: cookies[:behavior_visitor_token]
)
end
end
end
# app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def create
@project = Project.create(project_params)
# Track with simplified API
track_event("project_created", properties: {
project_id: @project.id,
project_name: @project.name
})
redirect_to @project
end
def show
@project = Project.find(params[:id])
# Track page view
track_page_view(path: request.path)
end
end
# app/jobs/cleanup_job.rb
class CleanupJob < ApplicationJob
def perform
retention_policy = BehaviorAnalytics::Cleanup::RetentionPolicy.new(
visit_retention_days: BehaviorAnalytics.configuration.visit_retention_days,
event_retention_days: BehaviorAnalytics.configuration.event_retention_days
)
scheduler = BehaviorAnalytics::Cleanup::Scheduler.new(
storage_adapter: BehaviorAnalytics.configuration.storage_adapter,
retention_policy: retention_policy
)
scheduler.cleanup_all
end
end
# Analytics dashboard
class AnalyticsController < ApplicationController
def index
tracker = BehaviorAnalytics.create_tracker
context = BehaviorAnalytics::Context.new(
tenant_id: current_tenant.id
)
@analytics = {
event_count: tracker.analytics.event_count(context, since: 7.days.ago),
unique_users: tracker.analytics.unique_users(context),
engagement_score: tracker.analytics.engagement_score(context),
daily_activity: tracker.analytics.daily_activity(context),
top_features: tracker.analytics.top_features(context, limit: 10),
countries: tracker.analytics.geographic.country_breakdown(context),
sources: tracker.analytics.referrer.source_breakdown(context)
}
end
endBehaviorAnalytics.configure { |config| ... }- Configure the gemBehaviorAnalytics.create_tracker(options = {})- Create a tracker instanceBehaviorAnalytics.track(event_name, properties: {}, **options)- Simplified trackingBehaviorAnalytics.track_page_view(path:, properties: {}, **options)- Track page viewBehaviorAnalytics.track_click(element:, properties: {}, **options)- Track clickBehaviorAnalytics.track_conversion(conversion_name:, value: nil, properties: {}, **options)- Track conversionBehaviorAnalytics.tracker- Get default tracker instance
track(context:, event_name:, event_type: :custom, metadata: {}, **options)- Track eventtrack_api_call(context:, method:, path:, status_code:, duration_ms: nil, **options)- Track API calltrack_feature_usage(context:, feature:, metadata: {}, **options)- Track feature usageflush- Flush buffered eventsanalytics- Get analytics enginequery- Get query buildersubscribe_to_stream(filter: nil, &block)- Subscribe to event stream
event_count(context, options = {})- Count eventsunique_users(context, options = {})- Count unique usersactive_days(context, options = {})- Count active daysengagement_score(context, options = {})- Calculate engagement scoreactivity_timeline(context, period: :daily, options = {})- Get activity timelinedaily_activity(context, options = {})- Get daily activityfeature_usage_stats(context, options = {})- Get feature usage statisticstop_features(context, limit: 10, options = {})- Get top featuresfunnels- Access funnel analysiscohorts- Access cohort analysisretention- Access retention analysisgeographic- Access geographic analyticsreferrer- Access referrer analytics
find_or_create_visit(visitor_token:, tenant_id: nil, user_id: nil, **options)- Find or create visitfind_active_visit(visitor_token, user_id = nil)- Find active visitsave_visit(visit)- Save visitend_visit(visit_token)- End visitlink_user_to_visits(visitor_token, user_id)- Link visits to userfind_visits_by_user(user_id, limit: 100)- Get user's visitsfind_visits_by_visitor(visitor_token, limit: 100)- Get visitor's visits
identify_user(user_id, visitor_token: nil, request: nil)- Identify user and merge visitsmerge_visits(visitor_token, user_id)- Merge anonymous visits with userget_user_visits(user_id, limit: 100)- Get user's visitsget_visitor_visits(visitor_token, limit: 100)- Get visitor's visits
- Enable visit tracking for web applications to get session analytics
- Disable for API-only applications to reduce overhead
- Use
visit_durationto match your session timeout
- Use
:simplefor basic detection (no dependencies) - Use
:browseror:user_agent_parserfor more accurate detection (requires gems) - Consider caching device info to reduce processing
- Use geocoding service for production (Geocoder gem recommended)
- Consider privacy implications of IP tracking
- Cache geolocation results to reduce API calls
- Set appropriate retention periods based on your needs
- Schedule cleanup jobs to run regularly
- Archive old data before deletion if needed for compliance
- Use async processing for high-volume applications
- Configure appropriate batch sizes
- Monitor query performance and add indexes as needed
- Consider using Redis or Elasticsearch adapters for scale
- Be transparent about data collection
- Implement data deletion on user request
- Consider anonymizing IP addresses
- Comply with GDPR, CCPA, and other regulations
Bug reports and pull requests are welcome on GitHub at https://github.com/nerdawey/behavior_analytics.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Run the test suite:
bundle exec rspec - Submit a pull request
The gem is available as open source under the terms of the MIT License.