Ruby
The Shipeasy Ruby gem — fork-safe singleton, Rails railtie, local evaluation, configs, kill switches, and metric tracking.
/docs/ — the same Markdown shipeasy docs get --sdk ruby overview returns, served raw at https://shipeasy-ai.github.io/sdk-ruby/pages/overview.md. Edit it in the SDK repo, not here.shipeasy-sdk is the server-side Ruby gem for the Shipeasy
hosted service: feature gates (flags), dynamic configs, kill switches, A/B
experiments, metric tracking, see() error reporting, and i18n view helpers for
Rails. It uses your server key and must never be embedded in a browser. It is
Rails-friendly but works in plain Ruby, Sinatra, Hanami, and serverless too.
Install
# Gemfile
gem "shipeasy-sdk"Full wiring — frameworks, options, env vars — is in Installation.
Mental model: configure once, bind a Client per user
There are exactly two things to learn:
Shipeasy.configure { |c| ... }— call it once at boot with your server key and an optionalattributestransform (your user object → the Shipeasy attribute hash). This is the whole setup story.Shipeasy::Client.new(user)— construct a cheap, user-bound handle per request and read with no user argument (the user is bound at construction).
# boot (config/initializers/shipeasy.rb)
Shipeasy.configure do |c|
c.api_key = ENV.fetch("SHIPEASY_SERVER_KEY")
c.attributes = ->(u) { { "user_id" => u.id, "plan" => u.plan } }
end
# per request — construct once per callsite (cheap; binds the user)
flags = Shipeasy::Client.new(current_user)
flags.get_flag("new_checkout") # NO user arg — bound at construction
flags.get_config("button_color")
assignment = flags.universe("checkout").assign # <=1 experiment; auto-logs exposure
assignment.get("label", "Buy") # variant ?? universe default ?? fallback
flags.track("purchase", { revenue: 49 }) # on conversion
flags.get_killswitch("payments")What the bound Client does
Everything you need per request is on Shipeasy::Client.new(user) — no user
argument on any call:
get_flag(name, default: false)·get_flag_detail(name)get_config(name, decode = nil, default: nil)get_killswitch(name, switch_key = nil)universe(name).assign→ anAssignment(.name/.group/.enrolled?/.get(field, fallback = nil)); auto-logs one deduped exposure when enrolledtrack(event_name, props = {})
So an experiment is end-to-end Client-only. Constructing a
Shipeasy::Client.new(user) before Shipeasy.configure raises Shipeasy::Error.
The configure family
| call | when |
|---|---|
Shipeasy.configure { ... } | production — your server key |
Shipeasy.configure_for_testing(...) | unit tests — no network, seed overrides |
Shipeasy.configure_for_offline(...) | evaluate real rules from a snapshot / file |
After any of them, you read the same way: Shipeasy::Client.new(user).
Pages
- installation — gem, frameworks (Rails / Sinatra / serverless),
configure - configuration —
Shipeasy.configure, keys, attributes, one-shot vs poll, options - flags —
get_flag+get_flag_detail - configs —
get_config - killswitches —
get_killswitch, named switches - error-reporting —
see()structured reporting - testing —
configure_for_testing,configure_for_offline, overrides - openfeature —
Shipeasy::OpenFeature::Provider - advanced — anon-id middleware, private attributes, sticky bucketing, manual exposure, SSR
The blocks below are the SDK repo's own snippets — the same ones shipeasy docs get --sdk ruby release/flags returns, with a worked example baked in.
Feature flags
Evaluate the feature gate new_checkout on a user-bound Client. Assumes
Shipeasy.configure ran at startup — see Installation.
Basic check
# construct once per callsite (cheap; binds the user + runs the attributes transform)
flags = Shipeasy::Client.new(current_user)
# get_flag(name, default: false)
# name — the gate key (required)
# default — returned ONLY when the value can't be resolved (client not ready /
# gate absent); a gate that evaluates to false returns false
if flags.get_flag("new_checkout", default: false)
# ship it
endWhy it resolved that way — get_flag_detail
flags = Shipeasy::Client.new(current_user)
# returns a FlagDetail (.value, .reason); reason ∈ RULE_MATCH / DEFAULT / OFF /
# OVERRIDE / FLAG_NOT_FOUND / CLIENT_NOT_READY
detail = flags.get_flag_detail("new_checkout")
logger.info("flag=new_checkout value=#{detail.value} reason=#{detail.reason}")React to flag changes (long-running server)
# requires configure(poll: true); fires after a poll fetches NEW data (200, not 304)
unsubscribe = Shipeasy.on_change { reload_local_cache! }
# ... later: unsubscribe.callDynamic configs
Read the dynamic config billing_copy with a fallback default.
Assumes
Shipeasy.configureran at startup — see Installation.
# construct once per callsite (cheap; binds the user)
flags = Shipeasy::Client.new(current_user)
# get_config(name, decode = nil, default: nil)
# name — the config key
# decode — optional proc run on a present value, e.g. ->(v) { v["max"] }
# default — returned only when the config key is absent
value = flags.get_config("billing_copy", default: "blue")Kill switches
Read the kill switch payments (true = killed). Assumes
Shipeasy.configure ran at startup — see Installation.
Whole switch
# construct once per callsite (cheap; binds the user)
flags = Shipeasy::Client.new(current_user)
# get_killswitch(name, switch_key = nil)
# name — the kill switch key (required)
# switch_key — optional named per-key switch to read
if flags.get_killswitch("payments")
# killed → take the safe path
endA named per-key switch
flags = Shipeasy::Client.new(current_user)
provider = "stripe" # pass the thing you're about to do as the switch key
# A configured switch returns its own boolean; an unconfigured key falls back to
# the kill switch's top-level value.
if flags.get_killswitch("payments", provider)
use_backup_processor
endTrack a conversion
Track a metric/conversion event from the bound Client. Metrics in the dashboard
are computed from these events. Assumes Shipeasy.configure ran at startup —
see Installation.
Track an event
# construct once per callsite (cheap; binds the user)
flags = Shipeasy::Client.new(current_user)
# track(event_name, props = {})
# event_name — the event your metric is built on (required)
# props — optional payload; numeric/string fields you can sum/filter on
# in a metric (private attributes are stripped before egress)
flags.track("checkout_started", { amount: 49, currency: "usd" })Fire-and-forget (never blocks your response) and a no-op under
configure_for_testing / configure_for_offline. The unit is the bound user
(user_id, else anonymous_id); with no unit the call is a no-op.
Track without properties
flags = Shipeasy::Client.new(current_user)
flags.track("checkout_started") # props are optional