@@ -56,6 +56,86 @@ client.group('org_456', user_id: 'user_123')
5656client.shutdown
5757```
5858
59+ ## Installation Prompt
60+
61+ Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:
62+
63+ ```
64+ Integrate Klime for customer analytics. Klime tracks user activity to identify which customers are healthy vs at risk of churning.
65+
66+ ANALYTICS MODES (determine which applies):
67+ - Companies & Teams: Your customers are companies with multiple team members (SaaS, enterprise tools)
68+ → Use identify() + group() + track()
69+ - Individual Customers: Your customers are individuals with private accounts (consumer apps, creator tools)
70+ → Use identify() + track() only (no group() needed)
71+
72+ KEY CONCEPTS:
73+ - Every track() call requires either user_id OR group_id (no anonymous events)
74+ - Use group_id alone for org-level events (webhooks, cron jobs, system metrics)
75+ - group() links a user to a company AND sets company traits (only for Companies & Teams mode)
76+ - Order doesn't matter - events before identify/group still get attributed correctly
77+
78+ BEST PRACTICES:
79+ - Initialize client ONCE at app startup (initializer or singleton)
80+ - Store write key in KLIME_WRITE_KEY environment variable
81+ - Call shutdown on process exit to flush remaining events (auto-registered via at_exit)
82+
83+ Add to Gemfile: gem 'klime'
84+ Then run: bundle install
85+
86+ Or install directly: gem install klime
87+
88+ require 'klime'
89+
90+ client = Klime::Client.new(write_key: ENV['KLIME_WRITE_KEY'])
91+
92+ # Identify users at signup/login:
93+ client.identify('usr_abc123', { email: '[email protected] ', name: 'Jane Smith' }) 94+
95+ # Track key activities:
96+ client.track('Report Generated', { report_type: 'revenue' }, user_id: 'usr_abc123')
97+ client.track('Feature Used', { feature: 'export', format: 'csv' }, user_id: 'usr_abc123')
98+ client.track('Teammate Invited', { role: 'member' }, user_id: 'usr_abc123')
99+
100+ # If Companies & Teams mode: link user to their company and set company traits
101+ client.group('org_456', { name: 'Acme Inc', plan: 'enterprise' }, user_id: 'usr_abc123')
102+
103+ INTEGRATION WORKFLOW:
104+
105+ Phase 1: Discover
106+ Explore the codebase to understand:
107+ 1. What framework is used? (Rails, Sinatra, Hanami, Rack, etc.)
108+ 2. Where is user identity available? (e.g., current_user.id, @current_user.id, session[:user_id], warden.user)
109+ 3. Is this Companies & Teams or Individual Customers?
110+ - Look for: organization, workspace, tenant, team, account models → Companies & Teams (use group())
111+ - No company/org concept, just individual users → Individual Customers (skip group())
112+ 4. Where do core user actions happen? (controllers, service objects, jobs, callbacks)
113+ 5. Is there existing analytics? (search: segment, posthog, mixpanel, amplitude, track)
114+ Match your integration style to the framework's conventions.
115+
116+ Phase 2: Instrument
117+ Add these calls using idiomatic patterns for the framework:
118+ - Initialize client once (Rails: config/initializers/klime.rb, Sinatra: before app.run, Rack: middleware)
119+ - identify() in auth/login success handler
120+ - group() when user-org association is established (Companies & Teams mode only)
121+ - track() for key user actions (see below)
122+
123+ WHAT TO TRACK:
124+ Active engagement (primary): feature usage, resource creation, collaboration, completing flows
125+ Session signals (secondary): login/session start, dashboard access - distinguishes "low usage" from "churned"
126+ Do NOT track: every request, health checks, before_action filters, background jobs
127+
128+ Phase 3: Verify
129+ Confirm: client initialized, shutdown handled, identify/group/track calls added
130+
131+ Phase 4: Summarize
132+ Report what you added:
133+ - Files modified and what was added to each
134+ - Events being tracked (list event names and what triggers them)
135+ - How user_id is obtained (and group_id if Companies & Teams mode)
136+ - Any assumptions made or questions
137+ ```
138+
59139## API Reference
60140
61141### Constructor
@@ -69,43 +149,48 @@ Klime::Client.new(
69149 max_queue_size: nil , # Optional: Max queued events (default: 1000)
70150 retry_max_attempts: nil , # Optional: Max retry attempts (default: 5)
71151 retry_initial_delay: nil , # Optional: Initial retry delay in ms (default: 1000)
72- flush_on_shutdown: nil # Optional: Auto-flush on exit (default: true)
152+ flush_on_shutdown: nil , # Optional: Auto-flush on exit (default: true)
153+ on_error: nil , # Optional: Callback for batch failures
154+ on_success: nil # Optional: Callback for successful sends
73155)
74156```
75157
76158### Methods
77159
78- #### ` track(event_name, properties = nil, user_id: nil, group_id: nil, ip: nil ) `
160+ #### ` track(event_name, properties = nil, user_id: nil, group_id: nil) `
79161
80- Track a user event. A ` user_id ` is required for events to be useful in Klime.
162+ Track an event. Events can be attributed in two ways:
163+ - ** User events** : Provide ` user_id ` to track user activity (most common)
164+ - ** Group events** : Provide ` group_id ` without ` user_id ` for organization-level events
81165
82166``` ruby
167+ # User event (most common)
83168client.track(' Button Clicked' , {
84169 button_name: ' Sign up' ,
85170 plan: ' pro'
86171}, user_id: ' user_123' )
87172
88- # With IP address (for geolocation )
89- client.track(' Button Clicked ' , {
90- button_name: ' Sign up ' ,
91- plan : ' pro '
92- }, user_id : ' user_123 ' , ip: ' 192.168.1.1 ' )
173+ # Group event (for webhooks, cron jobs, system events )
174+ client.track(' Events Received ' , {
175+ count: 100 ,
176+ source : ' webhook '
177+ }, group_id : ' org_456 ' )
93178```
94179
95- > ** Advanced ** : The ` group_id ` parameter is available for multi-tenant scenarios where a user belongs to multiple organizations and you need to specify which organization context the event occurred in.
180+ > ** Note ** : The ` group_id ` parameter can also be combined with ` user_id ` for multi-tenant scenarios where you need to specify which organization context a user event occurred in.
96181
97- #### ` identify(user_id, traits = nil, ip: nil ) `
182+ #### ` identify(user_id, traits = nil) `
98183
99184Identify a user with traits.
100185
101186``` ruby
102187client.identify(' user_123' , {
103188104189 name: ' Stefan'
105- }, ip: ' 192.168.1.1 ' )
190+ })
106191```
107192
108- #### ` group(group_id, traits = nil, user_id: nil, ip: nil ) `
193+ #### ` group(group_id, traits = nil, user_id: nil) `
109194
110195Associate a user with a group and/or set group traits.
111196
@@ -147,9 +232,33 @@ client.shutdown
147232- ** Automatic Batching** : Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
148233- ** Automatic Retries** : Failed requests are automatically retried with exponential backoff
149234- ** Thread-Safe** : Safe to use from multiple threads
235+ - ** Fork-Safe** : Automatically detects Puma/Unicorn forks and restarts the worker thread
150236- ** Process Exit Handling** : Automatically flushes events on process exit (via ` at_exit ` )
151237- ** Zero Dependencies** : Uses only Ruby standard library
152238
239+ ## Performance
240+
241+ When you call ` track() ` , ` identify() ` , or ` group() ` , the SDK:
242+
243+ 1 . Adds the event to an in-memory queue (microseconds)
244+ 2 . Returns immediately without waiting for network I/O
245+
246+ Events are sent to Klime's servers in a background thread. This means:
247+
248+ - ** No network blocking** : HTTP requests happen asynchronously in a background thread
249+ - ** No latency impact** : Tracking calls add < 1ms to your request handling time
250+ - ** Automatic batching** : Events are queued and sent in batches (default: every 2 seconds or 20 events)
251+
252+ ``` ruby
253+ # This returns immediately - no HTTP request is made here
254+ client.track(' Button Clicked' , { button: ' signup' }, user_id: ' user_123' )
255+
256+ # Your code continues without waiting
257+ render json: { success: true }
258+ ```
259+
260+ The only blocking operation is ` flush() ` , which waits for all queued events to be sent. This is typically only called during graceful shutdown.
261+
153262## Configuration
154263
155264### Default Values
@@ -161,6 +270,28 @@ client.shutdown
161270- ` retry_initial_delay ` : 1000ms
162271- ` flush_on_shutdown ` : true
163272
273+ ### Logging
274+
275+ ``` ruby
276+ Klime .configure do |config |
277+ config.logger = Rails .logger
278+ end
279+ ```
280+
281+ ### Callbacks
282+
283+ ``` ruby
284+ Klime .configure do |config |
285+ config.on_error = Proc .new { |error , batch |
286+ Sentry .capture_exception(error)
287+ }
288+
289+ config.on_success = Proc .new { |response |
290+ Rails .logger.info " Sent #{ response.accepted } events"
291+ }
292+ end
293+ ```
294+
164295## Error Handling
165296
166297The SDK automatically handles:
@@ -169,6 +300,8 @@ The SDK automatically handles:
169300- ** Permanent errors** (400, 401): Logs error and drops event
170301- ** Rate limiting** : Respects ` Retry-After ` header
171302
303+ For synchronous operations, use bang methods (` track! ` , ` identify! ` , ` group! ` ) which raise ` Klime::SendError ` on failure.
304+
172305## Size Limits
173306
174307- Maximum event size: 200KB
@@ -183,12 +316,12 @@ Events exceeding these limits are rejected and logged.
183316# config/initializers/klime.rb
184317require ' klime'
185318
186- KLIME = Klime :: Client . new (
187- write_key: ENV [ ' KLIME_WRITE_KEY ' ]
188- )
319+ Klime .configure do | config |
320+ config.logger = Rails .logger
321+ end
189322
190- # Ensure graceful shutdown
191- at_exit { KLIME .shutdown }
323+ Klime .client = Klime :: Client . new ( write_key: ENV [ ' KLIME_WRITE_KEY ' ])
324+ KLIME = Klime .client
192325```
193326
194327``` ruby
@@ -197,13 +330,21 @@ class ButtonsController < ApplicationController
197330 def click
198331 KLIME .track(' Button Clicked' , {
199332 button_name: params[:button_name ]
200- }, user_id: current_user&.id&.to_s, ip: request.remote_ip )
333+ }, user_id: current_user&.id&.to_s)
201334
202335 render json: { success: true }
203336 end
204337end
205338```
206339
340+ For Puma with multiple workers, add to ` config/puma.rb ` :
341+
342+ ``` ruby
343+ on_worker_boot do
344+ Klime .restart!
345+ end
346+ ```
347+
207348## Sinatra Example
208349
209350``` ruby
@@ -217,7 +358,7 @@ post '/api/button-clicked' do
217358
218359 client.track(' Button Clicked' , {
219360 button_name: data[' buttonName' ]
220- }, user_id: data[' userId' ], ip: request.ip )
361+ }, user_id: data[' userId' ])
221362
222363 { success: true }.to_json
223364end
226367at_exit { client.shutdown }
227368```
228369
229- ## Rack Middleware Example
230-
231- ``` ruby
232- # lib/klime_middleware.rb
233- require ' klime'
234-
235- class KlimeMiddleware
236- def initialize (app , write_key: )
237- @app = app
238- @client = Klime ::Client .new (write_key: write_key)
239- end
240-
241- def call (env )
242- status, headers, response = @app .call(env)
243-
244- # Track page views for authenticated users
245- user_id = env[' rack.session' ]&.dig(' user_id' )
246- if user_id && env[' REQUEST_METHOD' ] == ' GET' && status == 200
247- @client .track(' Page View' , {
248- path: env[' PATH_INFO' ],
249- method: env[' REQUEST_METHOD' ]
250- }, user_id: user_id, ip: env[' REMOTE_ADDR' ])
251- end
252-
253- [status, headers, response]
254- end
255- end
256- ```
257-
258370## Requirements
259371
260372- Ruby 2.6 or higher
0 commit comments