-
Notifications
You must be signed in to change notification settings - Fork 0
CodeExamples
Complete, copy-pasteable examples for every public method of the Convert Ruby SDK. The public API is snake_case; only the wire payloads are camelCase (translated internally). All examples assume a single CONVERT_SDK client built once at boot (see Initialization).
require "convert_sdk"
# Fetch mode.
CONVERT_SDK = ConvertSdk.create(
sdk_key: ENV.fetch("CONVERT_SDK_KEY"),
sdk_key_secret: ENV["CONVERT_SDK_KEY_SECRET"],
environment: "prod"
)
# Direct-data mode (no network fetch).
CONVERT_SDK = ConvertSdk.create(data: load_pre_fetched_config)Client#on(event, &block) subscribes to a SystemEvents value (or its matching string) and returns self. Deferred one-shot events replay to late subscribers.
CONVERT_SDK.on("ready") do |_payload, _err|
# decisions are available from here on
end
CONVERT_SDK.on(ConvertSdk::SystemEvents::BUCKETING) do |payload, _err|
# payload => { visitor_id:, experience_key:, variation_key: }
end
CONVERT_SDK.on(ConvertSdk::SystemEvents::CONVERSION) do |payload, _err|
# payload => { visitor_id:, goal_key: }
endcreate_context(visitor_id = nil, attributes = nil) returns a fresh, independent Context (or nil for a blank visitor id). Attributes accept symbol or string keys.
# Visitor id only.
context = CONVERT_SDK.create_context("visitor-123")
# Visitor id + initial attributes.
context = CONVERT_SDK.create_context("visitor-123", { country: "US", plan: "premium" })run_experience(key, attributes = nil) returns a BucketedVariation on a hit or a Sentinel on a miss — never raises, never a bare nil.
variation = context.run_experience("homepage-test")
case variation&.key
when nil then render_default # sentinel miss (key is nil)
when "treatment" then render_treatment
else render_variation(variation.key)
endPer-call attributes are merged over the context's attributes (per-call wins):
variation = context.run_experience("homepage-test", { country: "DE" })run_experiences(attributes = nil) returns an Array<BucketedVariation> — misses are filtered out, so the array contains only variations the visitor was actually bucketed into.
context.run_experiences.each do |variation|
activate(variation.experience_key, variation.key)
endrun_feature(key, attributes = nil) returns a frozen BucketedFeature (or an Array when several bucketed variations carry the feature). A miss is a DISABLED BucketedFeature, never a sentinel — branch on #status.
feature = context.run_feature("new-checkout")
if feature.status == ConvertSdk::FeatureStatus::ENABLED
render_new_checkout(feature.variables["headline"])
else
render_legacy_checkout
endrun_features(attributes = nil) returns an Array<BucketedFeature> — the full roster, every declared feature evaluated for this visitor (enabled + disabled).
context.run_features.each do |feature|
toggle(feature.key, on: feature.status == ConvertSdk::FeatureStatus::ENABLED)
endtrack_conversion(goal_key, goal_data: nil, force_multiple_transactions: false) records a conversion, deduplicated per visitor per goal. Returns self.
# Bare conversion.
context.track_conversion("signup")
# With revenue / transaction data (snake_case goal-data keys).
context.track_conversion(
"purchase",
goal_data: { amount: 49.99, products_count: 3, transaction_id: "tx-42" }
)The eight accepted goal_data keys are listed in Return Types & Sentinels. Unknown keys are rejected and debug-logged.
By default a goal records once per visitor. For renewals or repeat purchases, bypass the dedup check:
context.track_conversion(
"purchase",
goal_data: { amount: 49.99, transaction_id: "tx-43" },
force_multiple_transactions: true
)force_multiple_transactions: true enqueues the repeat transaction without re-marking the goal.
Pass enable_tracking: false in the per-call attributes hash to evaluate but not report (e.g. a consent-denied flow). Bucketing, sticky persistence, and the bucketing lifecycle event still fire — only the outbound network enqueue is skipped.
variation = context.run_experience("homepage-test", { enable_tracking: false })
context.run_experiences({ enable_tracking: false })The global tracking: false config switch always wins over a per-call true. See Tracking Control.
set_default_segments(segments) attaches default report-segments for the visitor (filtered to the platform report keys), merged into the visitor's stored segments. Returns self. Supply the wire keys (visitorType, customSegments, …).
context.set_default_segments({ "visitorType" => "new", "customSegments" => ["beta"] })run_custom_segments(segment_keys, attributes = nil) evaluates named custom segments against the visitor's properties and attaches matching ids under customSegments in StoreData. Returns a propagated RuleError sentinel, or nil.
context.run_custom_segments(["high-value", "returning"])
# With per-call rule data.
context.run_custom_segments(["high-value"], { ruleData: { lifetime_value: 1250 } })See Segments.
update_visitor_properties(properties) merges sticky properties into both the in-memory attributes (so a later decision on this context sees them immediately) and the store (so a later context for the same visitor sees them — stickiness). Returns self.
context.update_visitor_properties({ plan: "premium", account_age_days: 120 })See Visitor Context.
get_visitor_data returns the visitor's stored, string-keyed StoreData, or the empty shape { "bucketing" => {}, "segments" => {}, "goals" => {} } when nothing is stored.
data = context.get_visitor_data
data["bucketing"] # sticky variation assignments
data["segments"] # attached report-segments
data["goals"] # converted goals (dedup state)get_config_entity(key, entity_type) looks up a config entity by key. entity_type is one of :experience, :feature, :goal (symbol or string). Returns the frozen entity hash, or nil on a miss (debug-logged).
experience = context.get_config_entity("homepage-test", :experience)
feature = context.get_config_entity("new-checkout", :feature)
goal = context.get_config_entity("purchase", :goal)flush(reason = nil) (alias release_queues) delivers queued events synchronously and returns self. An empty queue is a no-op. Long-running servers also drain via the background flush timer; short-lived processes must flush explicitly before exit.
CONVERT_SDK.flush
CONVERT_SDK.flush("shutdown") # optional human-readable reason (logged)
CONVERT_SDK.release_queues # the frozen-name alias — same pathpostfork explicitly re-arms the client after a fork — rarely needed (automatic Process._fork detection covers the common runtimes). Returns self. See Fork Safety & Runtime Recipes.
# e.g. after Process.daemon, or in a worker-boot hook for belt-and-braces
CONVERT_SDK.postfork- Return Types & Sentinels — what each call returns
- Fork Safety & Runtime Recipes — per-runtime wiring for flush and fork
- Tracking Control — the consent switches
Copyrights © 2026 All Rights Reserved by Convert Insights, Inc.
Getting Started
Ruby SDK
- Quickstart
- Installation
- Initialization
- Configuration
- Return Types & Sentinels
- Code Examples
- Fork Safety & Runtime Recipes
- Tracking Control
Core Concepts
- Experiences & Variations
- Feature Flags
- Bucketing Algorithm
- Rule Evaluation
- Segments
- Data Management
- Event System
- API Communication
How-To Guides
- Running Experiences
- Running Features
- Tracking Conversions
- Visitor Context
- Persistent DataStore
- Troubleshooting
Contributing