behavior_analytics 2.1.1 → 2.2.1
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 +4 -4
- data/README.md +861 -8
- data/behavior_analytics.gemspec +5 -4
- data/lib/behavior_analytics/analytics/engine.rb +10 -0
- data/lib/behavior_analytics/event.rb +6 -1
- data/lib/behavior_analytics/integrations/rails.rb +45 -28
- data/lib/behavior_analytics/storage/active_record_adapter.rb +88 -0
- data/lib/behavior_analytics/tracker.rb +4 -1
- data/lib/behavior_analytics/version.rb +1 -1
- data/lib/behavior_analytics.rb +69 -1
- metadata +7 -5
data/README.md
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
# Behavior Analytics
|
|
2
2
|
|
|
3
|
-
A Ruby gem for tracking user behavior events with multi-tenant support,
|
|
3
|
+
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.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
### Core Features
|
|
7
8
|
- **Flexible Context Tracking**: Track events with multi-tenant support, user types, and custom filters
|
|
8
9
|
- **Event Buffering**: Efficient batch processing with configurable buffer size and flush intervals
|
|
10
|
+
- **Visit/Session Management**: Automatic visit tracking with session management, visitor identification, and visit analytics
|
|
11
|
+
- **Device & Browser Detection**: Automatic device, browser, and OS detection from user agents
|
|
12
|
+
- **Geographic Analytics**: Country, city, and region tracking from IP addresses
|
|
13
|
+
- **Referrer Analytics**: UTM parameter tracking, source analysis, and search keyword extraction
|
|
14
|
+
|
|
15
|
+
### Analytics & Insights
|
|
9
16
|
- **Comprehensive Analytics**:
|
|
10
17
|
- Event counts and aggregations
|
|
11
18
|
- Engagement scoring with customizable weights
|
|
12
19
|
- Time-based analytics (hourly, daily, weekly, monthly)
|
|
13
20
|
- Feature usage statistics
|
|
14
|
-
-
|
|
21
|
+
- Funnel analysis
|
|
22
|
+
- Cohort analysis
|
|
23
|
+
- Retention tracking
|
|
24
|
+
- **Geographic Analytics**: Country/city breakdowns, device/browser statistics
|
|
25
|
+
- **Referrer Analytics**: Traffic source analysis, UTM campaign tracking, search keyword insights
|
|
26
|
+
|
|
27
|
+
### Storage & Performance
|
|
28
|
+
- **Multiple Storage Adapters**:
|
|
15
29
|
- ActiveRecord adapter for production use
|
|
16
30
|
- In-memory adapter for testing
|
|
17
|
-
-
|
|
18
|
-
-
|
|
31
|
+
- Redis adapter for high-throughput scenarios
|
|
32
|
+
- Elasticsearch adapter for advanced search
|
|
33
|
+
- Kafka adapter for event streaming
|
|
34
|
+
- **Async Processing**: Background job support (Sidekiq, DelayedJob, ActiveJob)
|
|
35
|
+
- **Event Streaming**: Real-time event pub/sub system
|
|
36
|
+
|
|
37
|
+
### Developer Experience
|
|
38
|
+
- **Simplified API**: Simple tracking API with automatic context resolution
|
|
39
|
+
- **Rails Integration**: Automatic API call tracking via middleware with selective tracking
|
|
40
|
+
- **Query Interface**: Fluent query builder for filtering events with advanced aggregations
|
|
41
|
+
- **JavaScript Client**: Frontend tracking with automatic page views and click tracking
|
|
42
|
+
- **Data Retention**: Automatic cleanup policies for visits and events
|
|
19
43
|
|
|
20
44
|
## Installation
|
|
21
45
|
|
|
@@ -46,16 +70,21 @@ rails generate behavior_analytics:install
|
|
|
46
70
|
```
|
|
47
71
|
|
|
48
72
|
This will:
|
|
49
|
-
- Create
|
|
73
|
+
- Create migrations for the `behavior_events` and `behavior_visits` tables
|
|
50
74
|
- Create an initializer at `config/initializers/behavior_analytics.rb`
|
|
51
|
-
- Create
|
|
75
|
+
- Create models at `app/models/behavior_analytics_event.rb` and `app/models/behavior_analytics_visit.rb`
|
|
52
76
|
|
|
53
|
-
### 2. Run the
|
|
77
|
+
### 2. Run the migrations
|
|
54
78
|
|
|
55
79
|
```bash
|
|
56
80
|
rails db:migrate
|
|
57
81
|
```
|
|
58
82
|
|
|
83
|
+
This will create:
|
|
84
|
+
- `behavior_events` table for event tracking
|
|
85
|
+
- `behavior_visits` table for visit/session tracking
|
|
86
|
+
- Indexes for optimal query performance
|
|
87
|
+
|
|
59
88
|
### 3. Configure the initializer
|
|
60
89
|
|
|
61
90
|
Edit `config/initializers/behavior_analytics.rb`:
|
|
@@ -87,6 +116,24 @@ BehaviorAnalytics.configure do |config|
|
|
|
87
116
|
feature_diversity: 0.2,
|
|
88
117
|
time_in_trial: 0.1
|
|
89
118
|
}
|
|
119
|
+
|
|
120
|
+
# Visit/Session Management
|
|
121
|
+
config.track_visits = true # Enable visit tracking
|
|
122
|
+
config.visit_duration = 30.minutes # Visit expires after 30 min of inactivity
|
|
123
|
+
config.track_device_info = true # Auto-detect device, browser, OS
|
|
124
|
+
config.track_geolocation = true # Auto-detect country/city from IP
|
|
125
|
+
config.device_detector = :simple # :simple, :browser, or :user_agent_parser
|
|
126
|
+
|
|
127
|
+
# Data Retention
|
|
128
|
+
config.visit_retention_days = 90 # Keep visits for 90 days
|
|
129
|
+
config.event_retention_days = 365 # Keep events for 1 year
|
|
130
|
+
|
|
131
|
+
# Single-tenant support
|
|
132
|
+
config.default_tenant_id = "global" # For non-multi-tenant systems
|
|
133
|
+
|
|
134
|
+
# Optional: Advanced features
|
|
135
|
+
config.use_async = false # Enable async processing
|
|
136
|
+
config.debug_mode = Rails.env.development? # Enable debug logging
|
|
90
137
|
end
|
|
91
138
|
```
|
|
92
139
|
|
|
@@ -98,8 +145,175 @@ class ApplicationController < ActionController::Base
|
|
|
98
145
|
end
|
|
99
146
|
```
|
|
100
147
|
|
|
148
|
+
### 5. (Optional) Add JavaScript Client
|
|
149
|
+
|
|
150
|
+
Include the JavaScript client in your layout:
|
|
151
|
+
|
|
152
|
+
```erb
|
|
153
|
+
<!-- app/views/layouts/application.html.erb -->
|
|
154
|
+
<%= javascript_include_tag 'behavior_analytics' %>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Or use the inline script generator:
|
|
158
|
+
|
|
159
|
+
```erb
|
|
160
|
+
<script>
|
|
161
|
+
<%= BehaviorAnalytics::Javascript::Client.generate_script(
|
|
162
|
+
tracker_url: behavior_analytics_track_path,
|
|
163
|
+
auto_track: true
|
|
164
|
+
).html_safe %>
|
|
165
|
+
</script>
|
|
166
|
+
```
|
|
167
|
+
|
|
101
168
|
## Usage
|
|
102
169
|
|
|
170
|
+
### Simplified API
|
|
171
|
+
|
|
172
|
+
The gem provides a simplified API for easy tracking:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# Simple event tracking with automatic context resolution
|
|
176
|
+
BehaviorAnalytics.track("button_click", properties: { button: "signup" })
|
|
177
|
+
|
|
178
|
+
# Page view tracking
|
|
179
|
+
BehaviorAnalytics.track_page_view(path: "/dashboard")
|
|
180
|
+
|
|
181
|
+
# Click tracking
|
|
182
|
+
BehaviorAnalytics.track_click(element: "signup_button", properties: { location: "header" })
|
|
183
|
+
|
|
184
|
+
# Conversion tracking
|
|
185
|
+
BehaviorAnalytics.track_conversion(conversion_name: "signup", value: 99.99)
|
|
186
|
+
|
|
187
|
+
# Or use the helper methods in controllers
|
|
188
|
+
class ApplicationController < ActionController::Base
|
|
189
|
+
include BehaviorAnalytics::Helpers::TrackingHelper
|
|
190
|
+
|
|
191
|
+
def create
|
|
192
|
+
# ... create logic ...
|
|
193
|
+
track_event("project_created", properties: { project_id: @project.id })
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Visit/Session Management
|
|
199
|
+
|
|
200
|
+
Visits are automatically created and tracked when visit tracking is enabled:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# Visits are automatically created on first request
|
|
204
|
+
# You can access the current visit in your controllers:
|
|
205
|
+
|
|
206
|
+
class ApplicationController < ActionController::Base
|
|
207
|
+
include BehaviorAnalytics::Integrations::Rails
|
|
208
|
+
|
|
209
|
+
def current_visit
|
|
210
|
+
@current_visit ||= visit_manager&.find_or_create_visit(
|
|
211
|
+
visitor_token: visit_auto_creator.get_or_create_visitor_token(request),
|
|
212
|
+
tenant_id: current_tenant&.id,
|
|
213
|
+
user_id: current_user&.id,
|
|
214
|
+
ip: request.ip,
|
|
215
|
+
user_agent: request.user_agent,
|
|
216
|
+
referrer: request.referer,
|
|
217
|
+
landing_page: request.path
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Query visits
|
|
223
|
+
tracker = BehaviorAnalytics.create_tracker
|
|
224
|
+
visit_manager = BehaviorAnalytics::Visits::Manager.new(
|
|
225
|
+
storage_adapter: tracker.storage_adapter
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Get user's visits
|
|
229
|
+
user_visits = visit_manager.find_visits_by_user(user_id, limit: 100)
|
|
230
|
+
|
|
231
|
+
# Get visitor's visits (anonymous)
|
|
232
|
+
visitor_visits = visit_manager.find_visits_by_visitor(visitor_token, limit: 100)
|
|
233
|
+
|
|
234
|
+
# Link anonymous visits to user on login
|
|
235
|
+
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
|
|
236
|
+
visit_manager: visit_manager
|
|
237
|
+
)
|
|
238
|
+
user_resolver.identify_user(user_id, visitor_token: visitor_token)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Device & Browser Detection
|
|
242
|
+
|
|
243
|
+
Device information is automatically detected and stored in visits:
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# Device detection happens automatically when track_device_info is enabled
|
|
247
|
+
# Visit will include:
|
|
248
|
+
# - browser: "Chrome", "Safari", "Firefox", etc.
|
|
249
|
+
# - os: "iOS", "Android", "Windows", "Mac OS", etc.
|
|
250
|
+
# - device_type: "mobile", "tablet", "desktop"
|
|
251
|
+
|
|
252
|
+
# You can also manually detect device info:
|
|
253
|
+
detector = BehaviorAnalytics::Detection::DeviceDetector.new(strategy: :simple)
|
|
254
|
+
device_info = detector.detect(request.user_agent)
|
|
255
|
+
# => { browser: "Chrome", os: "Mac OS", device_type: "desktop" }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Geographic Analytics
|
|
259
|
+
|
|
260
|
+
Geographic information is automatically detected from IP addresses:
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
# Geolocation happens automatically when track_geolocation is enabled
|
|
264
|
+
# Visit will include:
|
|
265
|
+
# - country: "United States"
|
|
266
|
+
# - city: "San Francisco"
|
|
267
|
+
# - country_code: "US"
|
|
268
|
+
|
|
269
|
+
# Query geographic analytics
|
|
270
|
+
analytics = tracker.analytics
|
|
271
|
+
|
|
272
|
+
# Country breakdown
|
|
273
|
+
countries = analytics.geographic.country_breakdown(context)
|
|
274
|
+
# => [{ country: "United States", count: 150 }, { country: "Canada", count: 50 }]
|
|
275
|
+
|
|
276
|
+
# City breakdown
|
|
277
|
+
cities = analytics.geographic.city_breakdown(context)
|
|
278
|
+
# => [{ city: "San Francisco", count: 75 }, { city: "New York", count: 50 }]
|
|
279
|
+
|
|
280
|
+
# Device breakdown
|
|
281
|
+
devices = analytics.geographic.device_breakdown(context)
|
|
282
|
+
# => [{ device: "desktop", count: 100 }, { device: "mobile", count: 50 }]
|
|
283
|
+
|
|
284
|
+
# Browser breakdown
|
|
285
|
+
browsers = analytics.geographic.browser_breakdown(context)
|
|
286
|
+
# => [{ browser: "Chrome", count: 120 }, { browser: "Safari", count: 30 }]
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Referrer Analytics
|
|
290
|
+
|
|
291
|
+
Track traffic sources, UTM parameters, and search keywords:
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
analytics = tracker.analytics
|
|
295
|
+
|
|
296
|
+
# Traffic source breakdown
|
|
297
|
+
sources = analytics.referrer.source_breakdown(context)
|
|
298
|
+
# => [{ source: "google", count: 100 }, { source: "direct", count: 50 }]
|
|
299
|
+
|
|
300
|
+
# UTM source breakdown
|
|
301
|
+
utm_sources = analytics.referrer.utm_source_breakdown(context)
|
|
302
|
+
# => [{ utm_source: "newsletter", count: 75 }, { utm_source: "social", count: 25 }]
|
|
303
|
+
|
|
304
|
+
# UTM campaign breakdown
|
|
305
|
+
campaigns = analytics.referrer.utm_campaign_breakdown(context)
|
|
306
|
+
# => [{ utm_campaign: "summer_sale", count: 50 }, { utm_campaign: "winter_promo", count: 30 }]
|
|
307
|
+
|
|
308
|
+
# Search keyword breakdown
|
|
309
|
+
keywords = analytics.referrer.search_keyword_breakdown(context)
|
|
310
|
+
# => [{ keyword: "ruby gem", count: 20 }, { keyword: "analytics", count: 15 }]
|
|
311
|
+
|
|
312
|
+
# Referring domain breakdown
|
|
313
|
+
domains = analytics.referrer.referring_domain_breakdown(context)
|
|
314
|
+
# => [{ domain: "google.com", count: 100 }, { domain: "twitter.com", count: 25 }]
|
|
315
|
+
```
|
|
316
|
+
|
|
103
317
|
### Supported Business Cases
|
|
104
318
|
|
|
105
319
|
The gem is flexible and supports different business scenarios:
|
|
@@ -243,6 +457,66 @@ feature_stats = analytics.feature_usage_stats(context)
|
|
|
243
457
|
# => { "projects" => 25, "search" => 10, ... }
|
|
244
458
|
|
|
245
459
|
top_features = analytics.top_features(context, limit: 10)
|
|
460
|
+
|
|
461
|
+
# Funnel analysis
|
|
462
|
+
funnel = analytics.funnels.create(
|
|
463
|
+
name: "signup_funnel",
|
|
464
|
+
steps: ["page_view", "signup_form", "signup_submit", "email_verified"]
|
|
465
|
+
)
|
|
466
|
+
conversion_rate = funnel.conversion_rate(context)
|
|
467
|
+
|
|
468
|
+
# Cohort analysis
|
|
469
|
+
cohort = analytics.cohorts.create(
|
|
470
|
+
name: "signup_cohort",
|
|
471
|
+
event_name: "signup",
|
|
472
|
+
period: :monthly
|
|
473
|
+
)
|
|
474
|
+
cohort_data = cohort.analyze(context)
|
|
475
|
+
|
|
476
|
+
# Retention analysis
|
|
477
|
+
retention = analytics.retention.calculate(
|
|
478
|
+
context: context,
|
|
479
|
+
event_name: "login",
|
|
480
|
+
period: :weekly
|
|
481
|
+
)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Data Retention & Cleanup
|
|
485
|
+
|
|
486
|
+
Automatically clean up old visits and events:
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
# Configure retention policy
|
|
490
|
+
retention_policy = BehaviorAnalytics::Cleanup::RetentionPolicy.new(
|
|
491
|
+
visit_retention_days: 90,
|
|
492
|
+
event_retention_days: 365
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Create scheduler
|
|
496
|
+
scheduler = BehaviorAnalytics::Cleanup::Scheduler.new(
|
|
497
|
+
storage_adapter: tracker.storage_adapter,
|
|
498
|
+
retention_policy: retention_policy
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
# Cleanup old data
|
|
502
|
+
results = scheduler.cleanup_all
|
|
503
|
+
# => { visits: 1500, events: 50000 }
|
|
504
|
+
|
|
505
|
+
# Or cleanup separately
|
|
506
|
+
deleted_visits = scheduler.cleanup_visits
|
|
507
|
+
deleted_events = scheduler.cleanup_events
|
|
508
|
+
|
|
509
|
+
# Schedule cleanup job (e.g., in a cron job or scheduled task)
|
|
510
|
+
# config/schedule.rb (whenever gem)
|
|
511
|
+
every 1.day, at: '2:00 am' do
|
|
512
|
+
runner "BehaviorAnalytics::Cleanup::Scheduler.new(
|
|
513
|
+
storage_adapter: BehaviorAnalytics.configuration.storage_adapter,
|
|
514
|
+
retention_policy: BehaviorAnalytics::Cleanup::RetentionPolicy.new(
|
|
515
|
+
visit_retention_days: BehaviorAnalytics.configuration.visit_retention_days,
|
|
516
|
+
event_retention_days: BehaviorAnalytics.configuration.event_retention_days
|
|
517
|
+
)
|
|
518
|
+
).cleanup_all"
|
|
519
|
+
end
|
|
246
520
|
```
|
|
247
521
|
|
|
248
522
|
### Query Interface
|
|
@@ -264,6 +538,70 @@ count = query
|
|
|
264
538
|
.for_tenant("org_123")
|
|
265
539
|
.with_event_name("project_created")
|
|
266
540
|
.count
|
|
541
|
+
|
|
542
|
+
# Advanced filtering
|
|
543
|
+
events = query
|
|
544
|
+
.with_metadata(key: "feature", value: "advanced_search")
|
|
545
|
+
.with_path("/api/search")
|
|
546
|
+
.with_method("POST")
|
|
547
|
+
.with_status_code(200)
|
|
548
|
+
.group_by(:user_id)
|
|
549
|
+
.aggregate(field: :duration_ms, function: :avg)
|
|
550
|
+
.execute
|
|
551
|
+
|
|
552
|
+
# Visit-based queries
|
|
553
|
+
visit_events = query
|
|
554
|
+
.for_visit(visit_token)
|
|
555
|
+
.with_event_type(:custom)
|
|
556
|
+
.execute
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### JavaScript Client
|
|
560
|
+
|
|
561
|
+
The JavaScript client provides automatic frontend tracking:
|
|
562
|
+
|
|
563
|
+
```javascript
|
|
564
|
+
// Automatic tracking (enabled by default)
|
|
565
|
+
// - Page views on load
|
|
566
|
+
// - Clicks on elements with data-track attribute
|
|
567
|
+
// - Form submissions with data-track attribute
|
|
568
|
+
|
|
569
|
+
// Manual tracking
|
|
570
|
+
BehaviorAnalytics.track("button_click", { button: "signup" });
|
|
571
|
+
BehaviorAnalytics.trackPageView();
|
|
572
|
+
BehaviorAnalytics.trackClick(element, { location: "header" });
|
|
573
|
+
|
|
574
|
+
// HTML attributes for automatic tracking
|
|
575
|
+
<button data-track="signup_click" data-track-properties='{"plan": "premium"}'>
|
|
576
|
+
Sign Up
|
|
577
|
+
</button>
|
|
578
|
+
|
|
579
|
+
<form data-track="newsletter_signup">
|
|
580
|
+
<!-- form fields -->
|
|
581
|
+
</form>
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### User Identification & Visitor Tracking
|
|
585
|
+
|
|
586
|
+
Track anonymous visitors and merge with user accounts:
|
|
587
|
+
|
|
588
|
+
```ruby
|
|
589
|
+
# Identify user on login (merges anonymous visits)
|
|
590
|
+
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
|
|
591
|
+
visit_manager: visit_manager
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# On user login
|
|
595
|
+
user_resolver.identify_user(
|
|
596
|
+
user_id: current_user.id,
|
|
597
|
+
visitor_token: cookies[:behavior_visitor_token]
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
# Get all visits for a user (including anonymous visits before login)
|
|
601
|
+
user_visits = user_resolver.get_user_visits(user_id)
|
|
602
|
+
|
|
603
|
+
# Get visits for anonymous visitor
|
|
604
|
+
visitor_visits = user_resolver.get_visitor_visits(visitor_token)
|
|
267
605
|
```
|
|
268
606
|
|
|
269
607
|
### Custom Storage Adapter
|
|
@@ -288,6 +626,8 @@ tracker = BehaviorAnalytics.create_tracker(
|
|
|
288
626
|
|
|
289
627
|
## Configuration Options
|
|
290
628
|
|
|
629
|
+
### Core Configuration
|
|
630
|
+
|
|
291
631
|
- `storage_adapter`: Storage adapter instance (required)
|
|
292
632
|
- `batch_size`: Number of events to buffer before flushing (default: 100)
|
|
293
633
|
- `flush_interval`: Seconds between automatic flushes (default: 300)
|
|
@@ -295,12 +635,71 @@ tracker = BehaviorAnalytics.create_tracker(
|
|
|
295
635
|
- `scoring_weights`: Hash of weights for engagement scoring
|
|
296
636
|
- `default_tenant_id`: Default tenant ID for single-tenant systems (default: "default")
|
|
297
637
|
|
|
638
|
+
### Visit/Session Management
|
|
639
|
+
|
|
640
|
+
- `track_visits`: Enable visit tracking (default: false)
|
|
641
|
+
- `visit_duration`: Visit expiration time after inactivity (default: 30.minutes)
|
|
642
|
+
- `track_device_info`: Enable automatic device/browser detection (default: false)
|
|
643
|
+
- `track_geolocation`: Enable automatic geographic detection from IP (default: false)
|
|
644
|
+
- `device_detector`: Device detection strategy - `:simple`, `:browser`, or `:user_agent_parser` (default: `:simple`)
|
|
645
|
+
|
|
646
|
+
### Data Retention
|
|
647
|
+
|
|
648
|
+
- `visit_retention_days`: Days to keep visits before cleanup (default: 90)
|
|
649
|
+
- `event_retention_days`: Days to keep events before cleanup (default: 365)
|
|
650
|
+
|
|
651
|
+
### Advanced Features
|
|
652
|
+
|
|
653
|
+
- `use_async`: Enable async event processing (default: false)
|
|
654
|
+
- `async_processor`: Async processor instance (Sidekiq, DelayedJob, ActiveJob)
|
|
655
|
+
- `event_stream`: Event streaming instance for pub/sub
|
|
656
|
+
- `hooks_manager`: Event hooks manager for lifecycle callbacks
|
|
657
|
+
- `sampling_strategy`: Event sampling strategy
|
|
658
|
+
- `rate_limiter`: Rate limiting configuration
|
|
659
|
+
- `schema_validator`: Event schema validator
|
|
660
|
+
- `metrics`: Metrics collection instance
|
|
661
|
+
- `tracer`: Distributed tracing instance
|
|
662
|
+
- `debug_mode`: Enable debug logging (default: development mode)
|
|
663
|
+
- `logger`: Custom logger instance
|
|
664
|
+
|
|
665
|
+
### Rails Integration
|
|
666
|
+
|
|
667
|
+
- `tracking_whitelist`: Array of path patterns to whitelist (nil = track all)
|
|
668
|
+
- `tracking_blacklist`: Array of path patterns to blacklist (default: [])
|
|
669
|
+
- `skip_bots`: Skip tracking for bot user agents (default: true)
|
|
670
|
+
- `controller_action_filters`: Hash of controllers/actions to filter
|
|
671
|
+
- `slow_query_threshold`: Log slow queries above threshold (ms)
|
|
672
|
+
- `track_middleware_requests`: Track requests via middleware (default: false)
|
|
673
|
+
|
|
298
674
|
## Event Types
|
|
299
675
|
|
|
300
|
-
- `:api_call` - HTTP API requests
|
|
676
|
+
- `:api_call` - HTTP API requests (automatically tracked via Rails integration)
|
|
301
677
|
- `:feature_usage` - Feature usage events
|
|
302
678
|
- `:custom` - Custom business events
|
|
303
679
|
|
|
680
|
+
## Visit Model
|
|
681
|
+
|
|
682
|
+
Visits track user sessions and include:
|
|
683
|
+
|
|
684
|
+
- `visit_token`: Unique identifier for the visit
|
|
685
|
+
- `visitor_token`: Anonymous visitor identifier (persists across visits)
|
|
686
|
+
- `tenant_id`: Multi-tenant identifier
|
|
687
|
+
- `user_id`: User identifier (set when user logs in)
|
|
688
|
+
- `ip`: IP address
|
|
689
|
+
- `user_agent`: Browser user agent string
|
|
690
|
+
- `referrer`: HTTP referrer
|
|
691
|
+
- `landing_page`: First page visited in session
|
|
692
|
+
- `browser`: Detected browser (Chrome, Safari, Firefox, etc.)
|
|
693
|
+
- `os`: Detected operating system (iOS, Android, Windows, etc.)
|
|
694
|
+
- `device_type`: Device type (mobile, tablet, desktop)
|
|
695
|
+
- `country`: Country from IP geolocation
|
|
696
|
+
- `city`: City from IP geolocation
|
|
697
|
+
- `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`: UTM parameters
|
|
698
|
+
- `referring_domain`: Extracted referring domain
|
|
699
|
+
- `search_keyword`: Extracted search keyword from search engines
|
|
700
|
+
- `started_at`: Visit start time
|
|
701
|
+
- `ended_at`: Visit end time (null for active visits)
|
|
702
|
+
|
|
304
703
|
## Context
|
|
305
704
|
|
|
306
705
|
The `Context` class encapsulates tracking context and is flexible to support different business cases:
|
|
@@ -372,16 +771,470 @@ context = BehaviorAnalytics::Context.new(
|
|
|
372
771
|
tracker.track(context: context, event_name: "page_view")
|
|
373
772
|
```
|
|
374
773
|
|
|
774
|
+
## Advanced Usage
|
|
775
|
+
|
|
776
|
+
### Async Processing
|
|
777
|
+
|
|
778
|
+
Process events asynchronously using background jobs:
|
|
779
|
+
|
|
780
|
+
```ruby
|
|
781
|
+
# Configure async processor
|
|
782
|
+
BehaviorAnalytics.configure do |config|
|
|
783
|
+
config.use_async = true
|
|
784
|
+
config.async_processor = BehaviorAnalytics::Processors::BackgroundJobProcessor.new(
|
|
785
|
+
job_class: BehaviorAnalytics::Jobs::ActiveEventJob
|
|
786
|
+
)
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
# Events will be processed in background
|
|
790
|
+
tracker.track(context: context, event_name: "event")
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Event Hooks
|
|
794
|
+
|
|
795
|
+
Execute callbacks on event lifecycle:
|
|
796
|
+
|
|
797
|
+
```ruby
|
|
798
|
+
BehaviorAnalytics.configuration.hooks_manager.register_before_track do |event, context|
|
|
799
|
+
# Modify event before tracking
|
|
800
|
+
event[:metadata][:custom_field] = "value"
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
BehaviorAnalytics.configuration.hooks_manager.register_after_track do |event, context|
|
|
804
|
+
# Execute after tracking
|
|
805
|
+
NotificationService.notify(event)
|
|
806
|
+
end
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Event Sampling
|
|
810
|
+
|
|
811
|
+
Sample events to reduce storage:
|
|
812
|
+
|
|
813
|
+
```ruby
|
|
814
|
+
sampling_strategy = BehaviorAnalytics::Sampling::Strategy.new(
|
|
815
|
+
sample_rate: 0.1 # Sample 10% of events
|
|
816
|
+
)
|
|
817
|
+
BehaviorAnalytics.configuration.sampling_strategy = sampling_strategy
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Rate Limiting
|
|
821
|
+
|
|
822
|
+
Limit events per context:
|
|
823
|
+
|
|
824
|
+
```ruby
|
|
825
|
+
rate_limiter = BehaviorAnalytics::Throttling::Limiter.new(
|
|
826
|
+
max_events_per_minute: 100
|
|
827
|
+
)
|
|
828
|
+
BehaviorAnalytics.configuration.rate_limiter = rate_limiter
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### Event Schema Validation
|
|
832
|
+
|
|
833
|
+
Validate events against JSON schemas:
|
|
834
|
+
|
|
835
|
+
```ruby
|
|
836
|
+
schema = BehaviorAnalytics::Schema::Definition.new(
|
|
837
|
+
event_name: "signup",
|
|
838
|
+
schema: {
|
|
839
|
+
type: "object",
|
|
840
|
+
properties: {
|
|
841
|
+
email: { type: "string" },
|
|
842
|
+
plan: { type: "string", enum: ["basic", "premium"] }
|
|
843
|
+
},
|
|
844
|
+
required: ["email"]
|
|
845
|
+
}
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
validator = BehaviorAnalytics::Schema::Validator.new
|
|
849
|
+
validator.register_schema(schema)
|
|
850
|
+
BehaviorAnalytics.configuration.schema_validator = validator
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
## Migration Guide
|
|
854
|
+
|
|
855
|
+
### From v1 to v2
|
|
856
|
+
|
|
857
|
+
v2 is backward compatible with v1. Existing code will continue to work:
|
|
858
|
+
|
|
859
|
+
```ruby
|
|
860
|
+
# v1 code still works
|
|
861
|
+
tracker.track(context: context, event_name: "event")
|
|
862
|
+
|
|
863
|
+
# New v2 features are opt-in
|
|
864
|
+
BehaviorAnalytics.configure do |config|
|
|
865
|
+
config.track_visits = true # Enable new features
|
|
866
|
+
end
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### Enabling Visit Tracking
|
|
870
|
+
|
|
871
|
+
1. Run migrations:
|
|
872
|
+
```bash
|
|
873
|
+
rails db:migrate
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
2. Enable in configuration:
|
|
877
|
+
```ruby
|
|
878
|
+
BehaviorAnalytics.configure do |config|
|
|
879
|
+
config.track_visits = true
|
|
880
|
+
config.track_device_info = true
|
|
881
|
+
config.track_geolocation = true
|
|
882
|
+
end
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
3. Visits will be automatically created on requests
|
|
886
|
+
|
|
887
|
+
### Migrating Existing Events
|
|
888
|
+
|
|
889
|
+
If you have existing events and want to link them to visits:
|
|
890
|
+
|
|
891
|
+
```ruby
|
|
892
|
+
# Create visits for existing events (one-time migration)
|
|
893
|
+
BehaviorAnalyticsEvent.find_each do |event|
|
|
894
|
+
visit = visit_manager.find_or_create_visit(
|
|
895
|
+
visitor_token: generate_visitor_token_for_event(event),
|
|
896
|
+
tenant_id: event.tenant_id,
|
|
897
|
+
user_id: event.user_id,
|
|
898
|
+
ip: event.ip,
|
|
899
|
+
user_agent: event.user_agent
|
|
900
|
+
)
|
|
901
|
+
event.update(visit_id: visit.visit_token, visitor_id: visit.visitor_token)
|
|
902
|
+
end
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
## Performance Considerations
|
|
906
|
+
|
|
907
|
+
### Database Indexes
|
|
908
|
+
|
|
909
|
+
The migrations create indexes for optimal query performance. For high-volume applications, consider:
|
|
910
|
+
|
|
911
|
+
- Additional composite indexes based on your query patterns
|
|
912
|
+
- Partitioning large tables by date
|
|
913
|
+
- Archiving old data to separate tables
|
|
914
|
+
|
|
915
|
+
### Batch Processing
|
|
916
|
+
|
|
917
|
+
Configure appropriate batch sizes based on your volume:
|
|
918
|
+
|
|
919
|
+
```ruby
|
|
920
|
+
# High volume
|
|
921
|
+
config.batch_size = 500
|
|
922
|
+
config.flush_interval = 60 # 1 minute
|
|
923
|
+
|
|
924
|
+
# Low volume
|
|
925
|
+
config.batch_size = 50
|
|
926
|
+
config.flush_interval = 600 # 10 minutes
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### Async Processing
|
|
930
|
+
|
|
931
|
+
For high-throughput scenarios, use async processing:
|
|
932
|
+
|
|
933
|
+
```ruby
|
|
934
|
+
config.use_async = true
|
|
935
|
+
config.async_processor = BehaviorAnalytics::Processors::BackgroundJobProcessor.new(
|
|
936
|
+
job_class: BehaviorAnalytics::Jobs::SidekiqEventJob
|
|
937
|
+
)
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
## Troubleshooting
|
|
941
|
+
|
|
942
|
+
### Visits Not Being Created
|
|
943
|
+
|
|
944
|
+
1. Check that `track_visits` is enabled:
|
|
945
|
+
```ruby
|
|
946
|
+
BehaviorAnalytics.configuration.track_visits # => true
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
2. Verify migrations are run:
|
|
950
|
+
```bash
|
|
951
|
+
rails db:migrate:status
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
3. Check Rails integration is included:
|
|
955
|
+
```ruby
|
|
956
|
+
class ApplicationController < ActionController::Base
|
|
957
|
+
include BehaviorAnalytics::Integrations::Rails
|
|
958
|
+
end
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Device Detection Not Working
|
|
962
|
+
|
|
963
|
+
1. Ensure `track_device_info` is enabled
|
|
964
|
+
2. Check user agent is being passed:
|
|
965
|
+
```ruby
|
|
966
|
+
request.user_agent # Should not be nil
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
3. For better detection, use a gem:
|
|
970
|
+
```ruby
|
|
971
|
+
# Gemfile
|
|
972
|
+
gem 'browser' # or 'user_agent_parser'
|
|
973
|
+
|
|
974
|
+
# config
|
|
975
|
+
config.device_detector = :browser
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
### Geolocation Not Working
|
|
979
|
+
|
|
980
|
+
1. Ensure `track_geolocation` is enabled
|
|
981
|
+
2. Install geocoding gem:
|
|
982
|
+
```ruby
|
|
983
|
+
# Gemfile
|
|
984
|
+
gem 'geocoder'
|
|
985
|
+
|
|
986
|
+
# Will automatically use Geocoder for IP lookup
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
3. For production, consider MaxMind GeoIP2 database
|
|
990
|
+
|
|
375
991
|
## Development
|
|
376
992
|
|
|
377
993
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
378
994
|
|
|
379
995
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
380
996
|
|
|
997
|
+
### Running Tests
|
|
998
|
+
|
|
999
|
+
```bash
|
|
1000
|
+
bundle exec rspec
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
### Building the Gem
|
|
1004
|
+
|
|
1005
|
+
```bash
|
|
1006
|
+
gem build behavior_analytics.gemspec
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### Publishing
|
|
1010
|
+
|
|
1011
|
+
```bash
|
|
1012
|
+
gem push behavior_analytics-x.x.x.gem
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
## Complete Example
|
|
1016
|
+
|
|
1017
|
+
Here's a complete example showing all features:
|
|
1018
|
+
|
|
1019
|
+
```ruby
|
|
1020
|
+
# config/initializers/behavior_analytics.rb
|
|
1021
|
+
BehaviorAnalytics.configure do |config|
|
|
1022
|
+
config.storage_adapter = BehaviorAnalytics::Storage::ActiveRecordAdapter.new(
|
|
1023
|
+
model_class: BehaviorAnalyticsEvent
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
# Visit tracking
|
|
1027
|
+
config.track_visits = true
|
|
1028
|
+
config.visit_duration = 30.minutes
|
|
1029
|
+
config.track_device_info = true
|
|
1030
|
+
config.track_geolocation = true
|
|
1031
|
+
|
|
1032
|
+
# Data retention
|
|
1033
|
+
config.visit_retention_days = 90
|
|
1034
|
+
config.event_retention_days = 365
|
|
1035
|
+
|
|
1036
|
+
# Single-tenant
|
|
1037
|
+
config.default_tenant_id = "my_app"
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# app/controllers/application_controller.rb
|
|
1041
|
+
class ApplicationController < ActionController::Base
|
|
1042
|
+
include BehaviorAnalytics::Integrations::Rails
|
|
1043
|
+
include BehaviorAnalytics::Helpers::TrackingHelper
|
|
1044
|
+
|
|
1045
|
+
private
|
|
1046
|
+
|
|
1047
|
+
def identify_user_after_login
|
|
1048
|
+
if user_signed_in?
|
|
1049
|
+
user_resolver = BehaviorAnalytics::Identification::UserResolver.new(
|
|
1050
|
+
visit_manager: visit_manager
|
|
1051
|
+
)
|
|
1052
|
+
user_resolver.identify_user(
|
|
1053
|
+
current_user.id,
|
|
1054
|
+
visitor_token: cookies[:behavior_visitor_token]
|
|
1055
|
+
)
|
|
1056
|
+
end
|
|
1057
|
+
end
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
# app/controllers/projects_controller.rb
|
|
1061
|
+
class ProjectsController < ApplicationController
|
|
1062
|
+
def create
|
|
1063
|
+
@project = Project.create(project_params)
|
|
1064
|
+
|
|
1065
|
+
# Track with simplified API
|
|
1066
|
+
track_event("project_created", properties: {
|
|
1067
|
+
project_id: @project.id,
|
|
1068
|
+
project_name: @project.name
|
|
1069
|
+
})
|
|
1070
|
+
|
|
1071
|
+
redirect_to @project
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def show
|
|
1075
|
+
@project = Project.find(params[:id])
|
|
1076
|
+
|
|
1077
|
+
# Track page view
|
|
1078
|
+
track_page_view(path: request.path)
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
# app/jobs/cleanup_job.rb
|
|
1083
|
+
class CleanupJob < ApplicationJob
|
|
1084
|
+
def perform
|
|
1085
|
+
retention_policy = BehaviorAnalytics::Cleanup::RetentionPolicy.new(
|
|
1086
|
+
visit_retention_days: BehaviorAnalytics.configuration.visit_retention_days,
|
|
1087
|
+
event_retention_days: BehaviorAnalytics.configuration.event_retention_days
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
scheduler = BehaviorAnalytics::Cleanup::Scheduler.new(
|
|
1091
|
+
storage_adapter: BehaviorAnalytics.configuration.storage_adapter,
|
|
1092
|
+
retention_policy: retention_policy
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
scheduler.cleanup_all
|
|
1096
|
+
end
|
|
1097
|
+
end
|
|
1098
|
+
|
|
1099
|
+
# Analytics dashboard
|
|
1100
|
+
class AnalyticsController < ApplicationController
|
|
1101
|
+
def index
|
|
1102
|
+
tracker = BehaviorAnalytics.create_tracker
|
|
1103
|
+
context = BehaviorAnalytics::Context.new(
|
|
1104
|
+
tenant_id: current_tenant.id
|
|
1105
|
+
)
|
|
1106
|
+
|
|
1107
|
+
@analytics = {
|
|
1108
|
+
event_count: tracker.analytics.event_count(context, since: 7.days.ago),
|
|
1109
|
+
unique_users: tracker.analytics.unique_users(context),
|
|
1110
|
+
engagement_score: tracker.analytics.engagement_score(context),
|
|
1111
|
+
daily_activity: tracker.analytics.daily_activity(context),
|
|
1112
|
+
top_features: tracker.analytics.top_features(context, limit: 10),
|
|
1113
|
+
countries: tracker.analytics.geographic.country_breakdown(context),
|
|
1114
|
+
sources: tracker.analytics.referrer.source_breakdown(context)
|
|
1115
|
+
}
|
|
1116
|
+
end
|
|
1117
|
+
end
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
## API Reference
|
|
1121
|
+
|
|
1122
|
+
### BehaviorAnalytics Module
|
|
1123
|
+
|
|
1124
|
+
#### Class Methods
|
|
1125
|
+
|
|
1126
|
+
- `BehaviorAnalytics.configure { |config| ... }` - Configure the gem
|
|
1127
|
+
- `BehaviorAnalytics.create_tracker(options = {})` - Create a tracker instance
|
|
1128
|
+
- `BehaviorAnalytics.track(event_name, properties: {}, **options)` - Simplified tracking
|
|
1129
|
+
- `BehaviorAnalytics.track_page_view(path:, properties: {}, **options)` - Track page view
|
|
1130
|
+
- `BehaviorAnalytics.track_click(element:, properties: {}, **options)` - Track click
|
|
1131
|
+
- `BehaviorAnalytics.track_conversion(conversion_name:, value: nil, properties: {}, **options)` - Track conversion
|
|
1132
|
+
- `BehaviorAnalytics.tracker` - Get default tracker instance
|
|
1133
|
+
|
|
1134
|
+
### Tracker Class
|
|
1135
|
+
|
|
1136
|
+
#### Methods
|
|
1137
|
+
|
|
1138
|
+
- `track(context:, event_name:, event_type: :custom, metadata: {}, **options)` - Track event
|
|
1139
|
+
- `track_api_call(context:, method:, path:, status_code:, duration_ms: nil, **options)` - Track API call
|
|
1140
|
+
- `track_feature_usage(context:, feature:, metadata: {}, **options)` - Track feature usage
|
|
1141
|
+
- `flush` - Flush buffered events
|
|
1142
|
+
- `analytics` - Get analytics engine
|
|
1143
|
+
- `query` - Get query builder
|
|
1144
|
+
- `subscribe_to_stream(filter: nil, &block)` - Subscribe to event stream
|
|
1145
|
+
|
|
1146
|
+
### Analytics Engine
|
|
1147
|
+
|
|
1148
|
+
#### Methods
|
|
1149
|
+
|
|
1150
|
+
- `event_count(context, options = {})` - Count events
|
|
1151
|
+
- `unique_users(context, options = {})` - Count unique users
|
|
1152
|
+
- `active_days(context, options = {})` - Count active days
|
|
1153
|
+
- `engagement_score(context, options = {})` - Calculate engagement score
|
|
1154
|
+
- `activity_timeline(context, period: :daily, options = {})` - Get activity timeline
|
|
1155
|
+
- `daily_activity(context, options = {})` - Get daily activity
|
|
1156
|
+
- `feature_usage_stats(context, options = {})` - Get feature usage statistics
|
|
1157
|
+
- `top_features(context, limit: 10, options = {})` - Get top features
|
|
1158
|
+
- `funnels` - Access funnel analysis
|
|
1159
|
+
- `cohorts` - Access cohort analysis
|
|
1160
|
+
- `retention` - Access retention analysis
|
|
1161
|
+
- `geographic` - Access geographic analytics
|
|
1162
|
+
- `referrer` - Access referrer analytics
|
|
1163
|
+
|
|
1164
|
+
### Visit Manager
|
|
1165
|
+
|
|
1166
|
+
#### Methods
|
|
1167
|
+
|
|
1168
|
+
- `find_or_create_visit(visitor_token:, tenant_id: nil, user_id: nil, **options)` - Find or create visit
|
|
1169
|
+
- `find_active_visit(visitor_token, user_id = nil)` - Find active visit
|
|
1170
|
+
- `save_visit(visit)` - Save visit
|
|
1171
|
+
- `end_visit(visit_token)` - End visit
|
|
1172
|
+
- `link_user_to_visits(visitor_token, user_id)` - Link visits to user
|
|
1173
|
+
- `find_visits_by_user(user_id, limit: 100)` - Get user's visits
|
|
1174
|
+
- `find_visits_by_visitor(visitor_token, limit: 100)` - Get visitor's visits
|
|
1175
|
+
|
|
1176
|
+
### User Resolver
|
|
1177
|
+
|
|
1178
|
+
#### Methods
|
|
1179
|
+
|
|
1180
|
+
- `identify_user(user_id, visitor_token: nil, request: nil)` - Identify user and merge visits
|
|
1181
|
+
- `merge_visits(visitor_token, user_id)` - Merge anonymous visits with user
|
|
1182
|
+
- `get_user_visits(user_id, limit: 100)` - Get user's visits
|
|
1183
|
+
- `get_visitor_visits(visitor_token, limit: 100)` - Get visitor's visits
|
|
1184
|
+
|
|
1185
|
+
## Best Practices
|
|
1186
|
+
|
|
1187
|
+
### 1. Visit Tracking
|
|
1188
|
+
|
|
1189
|
+
- Enable visit tracking for web applications to get session analytics
|
|
1190
|
+
- Disable for API-only applications to reduce overhead
|
|
1191
|
+
- Use `visit_duration` to match your session timeout
|
|
1192
|
+
|
|
1193
|
+
### 2. Device Detection
|
|
1194
|
+
|
|
1195
|
+
- Use `:simple` for basic detection (no dependencies)
|
|
1196
|
+
- Use `:browser` or `:user_agent_parser` for more accurate detection (requires gems)
|
|
1197
|
+
- Consider caching device info to reduce processing
|
|
1198
|
+
|
|
1199
|
+
### 3. Geolocation
|
|
1200
|
+
|
|
1201
|
+
- Use geocoding service for production (Geocoder gem recommended)
|
|
1202
|
+
- Consider privacy implications of IP tracking
|
|
1203
|
+
- Cache geolocation results to reduce API calls
|
|
1204
|
+
|
|
1205
|
+
### 4. Data Retention
|
|
1206
|
+
|
|
1207
|
+
- Set appropriate retention periods based on your needs
|
|
1208
|
+
- Schedule cleanup jobs to run regularly
|
|
1209
|
+
- Archive old data before deletion if needed for compliance
|
|
1210
|
+
|
|
1211
|
+
### 5. Performance
|
|
1212
|
+
|
|
1213
|
+
- Use async processing for high-volume applications
|
|
1214
|
+
- Configure appropriate batch sizes
|
|
1215
|
+
- Monitor query performance and add indexes as needed
|
|
1216
|
+
- Consider using Redis or Elasticsearch adapters for scale
|
|
1217
|
+
|
|
1218
|
+
### 6. Privacy & Compliance
|
|
1219
|
+
|
|
1220
|
+
- Be transparent about data collection
|
|
1221
|
+
- Implement data deletion on user request
|
|
1222
|
+
- Consider anonymizing IP addresses
|
|
1223
|
+
- Comply with GDPR, CCPA, and other regulations
|
|
1224
|
+
|
|
381
1225
|
## Contributing
|
|
382
1226
|
|
|
383
1227
|
Bug reports and pull requests are welcome on GitHub at https://github.com/nerdawey/behavior_analytics.
|
|
384
1228
|
|
|
1229
|
+
### Development Setup
|
|
1230
|
+
|
|
1231
|
+
1. Fork the repository
|
|
1232
|
+
2. Create a feature branch
|
|
1233
|
+
3. Make your changes
|
|
1234
|
+
4. Add tests
|
|
1235
|
+
5. Run the test suite: `bundle exec rspec`
|
|
1236
|
+
6. Submit a pull request
|
|
1237
|
+
|
|
385
1238
|
## License
|
|
386
1239
|
|
|
387
1240
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|