footprinted 0.2.1 → 0.3.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: 2969bfdd4365c5aa27fe37a0a74d77283ad0b0053c221442496fd837fd210bfa
4
- data.tar.gz: 37992d2955029ec6b2e03aa841bb5f06f5d0acf868819ffd2ac56bbc2b38b6de
3
+ metadata.gz: 20ea0ce295c5fe9031013e323a46e744b92de81e6e433db994dc4df485373e31
4
+ data.tar.gz: '09c5e324348d00dcb53b8fbe7e581e22ee45e9d194f706d48685678cc999359a'
5
5
  SHA512:
6
- metadata.gz: e72627f90b0b0357ef8fffccbea1c1ae6204ccbc45144f5f992dbfdb3cc4d6716413ea173f22996412eea7f49023f6078c6841f67324b5a76ab40e18ad4d99f6
7
- data.tar.gz: 88ef984e78aa8b40fd634e4beb7be934dd3187aa2d8391b1da754cd981b64a6e48766211239d20264c87948300b0abfbf0037500189d8f0332e6616142afcccb
6
+ metadata.gz: 69f4c70cdddadd0bcf208bcecc337ab484ae5ac6991748488e4620a5d712c69ae6b444b26ceb6c0ad10a036c4652185a938509c203348d16b5cac0a28ae9c7a4
7
+ data.tar.gz: 5aaa74017232c3f54cc6db64155d75d762a7f758f04a7e886474e7af9cd579173264c744447f86f0eae56b84b096cb984f462bd40105e35a1ea80e1e8283448f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-02-15
4
+
5
+ - **Async mode now extracts geo data at enqueue time** when `request:` is passed
6
+ - Cloudflare headers are captured before the job is enqueued
7
+ - Background jobs no longer need MaxMind to get geo data
8
+ - Falls back to MaxMind lookup in the job if `request:` wasn't passed
9
+ - No breaking changes — existing code works unchanged
10
+
3
11
  ## [0.2.1] - 2026-02-09
4
12
 
5
13
  - Fix async mode: change Railtie to Engine so `TrackJob` is autoloaded
data/README.md CHANGED
@@ -245,7 +245,41 @@ add_index :footprints, :device_id
245
245
  add_index :footprints, :app_version
246
246
  ```
247
247
 
248
- Then write to both the column and the metadata hash in your tracking code. The gem stays generic; your app adds the columns it needs.
248
+ Your tracking calls stay the same just pass everything in `metadata` as before. To auto-promote metadata keys into their dedicated columns, add a `before_save` callback in an initializer:
249
+
250
+ ```ruby
251
+ # config/initializers/footprinted_extensions.rb
252
+
253
+ # String columns that map 1:1 from metadata
254
+ FOOTPRINT_PROMOTED_STRING_COLUMNS = %w[device_id app_version platform].freeze
255
+
256
+ # Integer columns that need casting
257
+ FOOTPRINT_PROMOTED_INTEGER_COLUMNS = %w[cpu_cores memory_gb].freeze
258
+
259
+ Rails.configuration.to_prepare do
260
+ Footprinted::Footprint.class_eval do
261
+ before_save :promote_metadata_columns
262
+
263
+ private
264
+
265
+ def promote_metadata_columns
266
+ return if metadata.blank?
267
+
268
+ m = metadata.stringify_keys
269
+
270
+ FOOTPRINT_PROMOTED_STRING_COLUMNS.each do |key|
271
+ self[key] = m[key] if self[key].blank? && m[key].present?
272
+ end
273
+
274
+ FOOTPRINT_PROMOTED_INTEGER_COLUMNS.each do |key|
275
+ self[key] = m[key].to_i if self[key].blank? && m[key].present?
276
+ end
277
+ end
278
+ end
279
+ end
280
+ ```
281
+
282
+ This works regardless of how footprints are created — via `TrackJob` (async), direct `.create!`, or the Rails console. The gem stays generic; your app adds the columns and promotion logic it needs.
249
283
 
250
284
  > [!TIP]
251
285
  > Which metadata keys to promote depends on your use case. A licensing SaaS might promote `device_id` + `app_version`. An e-commerce app might promote `product_id` + `session_id`. A CMS might promote `page_url` + `referrer`. Keep the JSONB for everything else.
@@ -276,6 +310,21 @@ When async is enabled, `track` and `track_<event_type>` calls enqueue a `Footpri
276
310
 
277
311
  You need a working ActiveJob backend (Sidekiq, Solid Queue, etc.) for this to work.
278
312
 
313
+ ### Geolocation in async mode
314
+
315
+ When you pass `request:` in async mode, geolocation data is extracted **immediately** (before enqueueing) using Cloudflare headers via [`trackdown`](https://github.com/rameerez/trackdown). This means:
316
+
317
+ - **No MaxMind database needed** in your background workers if you use Cloudflare
318
+ - Geo data is captured at request time, not in the job
319
+ - If `request:` is not passed, the job falls back to MaxMind lookup
320
+
321
+ ```ruby
322
+ # In your controller — pass request: for Cloudflare geo extraction
323
+ @product.track(:purchase, ip: request.remote_ip, request: request)
324
+ ```
325
+
326
+ This is the recommended pattern for async mode behind Cloudflare.
327
+
279
328
  ## Geolocation via Trackdown
280
329
 
281
330
  `footprinted` automatically resolves geolocation data from IP addresses using the [`trackdown`](https://github.com/rameerez/trackdown) gem. For every footprint, the following fields are populated:
@@ -11,6 +11,13 @@ module Footprinted
11
11
  attrs = attributes.symbolize_keys
12
12
  attrs[:occurred_at] = Time.parse(attrs[:occurred_at]) if attrs[:occurred_at].is_a?(String)
13
13
 
14
+ # Log geo data status for debugging
15
+ if attrs[:country_code].present?
16
+ Rails.logger.debug { "[Footprinted] TrackJob received pre-extracted geo: #{attrs[:country_code]}/#{attrs[:city]}" }
17
+ else
18
+ Rails.logger.debug { "[Footprinted] TrackJob has no pre-extracted geo, will attempt lookup for #{attrs[:ip]}" }
19
+ end
20
+
14
21
  trackable.footprints.create!(attrs)
15
22
  end
16
23
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- footprinted (0.2.1)
4
+ footprinted (0.3.0)
5
5
  rails (>= 7.0)
6
6
  trackdown (~> 0.3)
7
7
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- footprinted (0.2.1)
4
+ footprinted (0.3.0)
5
5
  rails (>= 7.0)
6
6
  trackdown (~> 0.3)
7
7
 
@@ -28,6 +28,7 @@ module Footprinted
28
28
  }
29
29
 
30
30
  if Footprinted.configuration.async
31
+ Footprinted::Model.enrich_with_geo_data!(attrs, ip, request)
31
32
  Footprinted::TrackJob.perform_later(
32
33
  self.class.name, id,
33
34
  attrs.merge(occurred_at: attrs[:occurred_at].iso8601)
@@ -52,6 +53,7 @@ module Footprinted
52
53
  }
53
54
 
54
55
  if Footprinted.configuration.async
56
+ Footprinted::Model.enrich_with_geo_data!(attrs, ip, request)
55
57
  Footprinted::TrackJob.perform_later(
56
58
  self.class.name, id,
57
59
  attrs.merge(occurred_at: attrs[:occurred_at].iso8601)
@@ -63,5 +65,27 @@ module Footprinted
63
65
  record
64
66
  end
65
67
  end
68
+
69
+ # Extract geo data from request (Cloudflare headers) before enqueueing
70
+ # This allows async jobs to have geo data without needing MaxMind
71
+ def self.enrich_with_geo_data!(attrs, ip, request)
72
+ return unless request && defined?(Trackdown)
73
+
74
+ location = Trackdown.locate(ip.to_s, request: request)
75
+ attrs.merge!(
76
+ country_code: location.country_code,
77
+ country_name: location.country_name,
78
+ city: location.city,
79
+ region: location.region,
80
+ continent: location.continent,
81
+ timezone: location.timezone,
82
+ latitude: location.latitude,
83
+ longitude: location.longitude
84
+ )
85
+
86
+ if location.country_code.present?
87
+ Rails.logger.debug { "[Footprinted] Extracted geo at enqueue: #{location.country_code}/#{location.city} for #{ip}" }
88
+ end
89
+ end
66
90
  end
67
91
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Footprinted
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: footprinted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-09 00:00:00.000000000 Z
10
+ date: 2026-02-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails