Ruby
View the full source code at https://github.com/growthbook/growthbook-ruby.
Requirements
The Ruby SDK requires Ruby version 2.5.0 or higher.
Installation
Install the gem:
gem install growthbook
Quick start
require 'growthbook'
# Fetch features from a GrowthBook instance
# You should cache this in Redis or similar in production
features_repository = Growthbook::FeatureRepository.new(
endpoint: 'https://cdn.growthbook.io/api/features/MY_API_KEY',
decryption_key: nil
)
features = features_repository.fetch
# Create a context for the current user/request
gb = Growthbook::Context.new(
features: features,
# User attributes for targeting / variation assignment
attributes: {
id: '123',
country: 'US'
}
)
# Use a boolean feature flag
if gb.on? :my_feature_key
puts 'My feature is on!'
end
# Get the value of a multivariate feature with a fallback
btn_color = gb.feature_value(:signup_btn_color, 'pink')
Tracking
Track experiment impressions
When a feature's value is determined by an experiment (A/B test), you typically want to track that assignment event for later analysis.
There are two ways to do this. First is by accessing all impressions at the end of a request:
gb.impressions.each do |key, result|
puts "Assigned variation #{result.variation_id} in experiment #{key}"
end
Second is by using a listener to get alerted in realtime as users are put into experiments:
class MyImpressionListener
def on_experiment_viewed(experiment, result)
puts "Assigned variation #{result.variation_id} in experiment #{experiment.key}"
end
end
gb.listener = MyImpressionListener.new
Track feature usage
GrowthBook can fire a callback whenever a feature is evaluated for a user. This can be useful to update 3rd party tools like NewRelic or DataDog.
Provide a receiver that can receive def on_feature_usage: (String _feature_key, FeatureResult _result) -> void
. There's a convenience class FeatureUsageCallback
with a method you can override but you can provide your own.
class MyFeatureUsageCallback < FeatureUsageCallback
def on_feature_usage(feature_key, feature_result)
puts "on_feature_usage_called with key: #{feature_key} and result #{feature_result}"
end
end
on_feature_usage = MyFeatureUsageCallback.new
# you can pass it into the context
gb = Growthbook::Context.new({
attributes: {
id: 'user-abc123'
},
features: feature_repository.fetch || {},
on_feature_usage: on_feature_usage,
})
# or assign it afterwards
gb.on_feature_usage = on_feature_usage
Using with Rails
You can use the provided Growthbook::FeatureRepository
class along with the Rails cache to fetch features periodically within your usage limits. Here is a controller concern you can use:
require 'growthbook'
module GrowthbookSdk
def growthbook
@growthbook ||= Growthbook::Context.new(
features: growthbook_features_json,
attributes: {},
)
end
# use this as a before_action on your controller
def init_feature_flags
return if current_user.nil?
# TODO: Change this to get your user attributes as a hash in a way that works for your app
growthbook.attributes = current_user.as_json
end
private
def growthbook_features_json
Rails.cache.fetch("growthbook_features", expires_in: 1.hour) do
puts "🌎 Fetching GrowthBook features from the network"
repo = Growthbook::FeatureRepository.new(
endpoint: 'https://cdn.growthbook.io/api/features/java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8',
decryption_key: nil,
)
repo.fetch || {}
end
end
end
And in your ApplicationController
:
class ApplicationController < ActionController::API
include Authentication # your own auth strategy
include GrowthbookSdk # the controller concern code above
before_action :authenticate!
before_action :init_feature_flags # call this once you have a user from which to get attributes
end
The above code exposes the following methods on your application controller:
growthbook
: an instance of the GrowthBook SDK for the requestinit_feature_flags
: a method intended to be used as abefore_action
hook, e.g.before_action :init_feature_flags
It assumes you have a method current_user
that returns the currently-authenticated user, and that it responds to as_json
to return a hash of the targeting attributes.
How this works:
- With each request, the
init_feature_flags
method is called. This creates a new instance ofGrowthbook::Context
- When creating the context for the first time, features are fetched and cached in the Rails cache. Subsequent calls use the cached version until the cache expires.
- Developers can call methods on
growthbook
in their controllers to use the GrowthBook SDK, e.g.growthbook.on?(:dark_mode)
.
You can see the Rails example linked in the Code examples below.
Dev and QA helpers
For dev/QA it's often useful to force specific feature values.
# These take precedence over everything else when determining a feature's value
gb.forced_features = {
my_feature: true,
other_feature: "new value"
}
# Will always be true
gb.is_on?(:my_feature)
# Will always be "new value"
gb.feature_value(:other_feature)
For more predictability during QA, you can also globally disable all random assignment in experiments from running:
gb.enabled = false
Inline experiments
It's also possible to directly run an experiment directly in code without going through a feature flag.
# Simple 50/50 experiment
result = gb.run(Growthbook::InlineExperiment.new(
key: "my-experiment-key",
variations: ["red", "green"]
))
# Whether or not the user was included in the experiment (either true or false)
puts(result.in_experiment ? 'included' : 'excluded')
# The value of the assigned variation (either "red" or "green")
puts(result.value)
# The variation index (either 0 or 1)
puts(result.variation_id)
There are lots of additional options when running inline experiments:
gb.run(Growthbook::InlineExperiment.new(
key: "my-experiment-key",
variations: ["red", "green"],
# Filter by context attributes
condition: {
country: {
"$in": ["US", "CA"]
}
},
# Adjust variation weights from the default 50/50 split
weights: [0.8, 0.2],
# Run for a subset of traffic (0 to 1, default = 1)
coverage: 0.5,
# Use a different context attribute for assigning a variation (default = "id")
hash_attribute: "device_id",
# Use a namespace to run mutually exclusive experiments
namespace: ["pricing-page", 0, 0.25]
))
Working with Encrypted features
You can learn more about SDK Connection Endpoint Encryption.
Create a GrowthBook::Context
with an encrypted payload and a decryption key:
# TODO: Replace these values with your own:
Growthbook::Context.new(
encrypted_features: 'm5ylFM6ndyOJA2OPadubkw==.Uu7ViqgKEt/dWvCyhI46q088PkAEJbnXKf3KPZjf9IEQQ+A8fojNoxw4wIbPX3aj',
decryption_key: 'Zvwv/+uhpFDznZ6SX28Yjg==',
attributes: {
id: '456',
country: 'CA'
}
)
When fetching features from the GrowthBook SDK endpoint, the encrypted features are available on a property encryptedFeatures
instead of plain text on the property features
. Here's an example with networking:
uri = URI('https://cdn.growthbook.io/api/features/MY_API_KEY')
res = Net::HTTP.get_response(uri)
encrypted_features = res.is_a?(Net::HTTPSuccess) ? JSON.parse(res.body)['encryptedFeatures'] : nil
Growthbook::Context.new(
encrypted_features: encrypted_features,
decryption_key: '<key-for-decrypting>',
attributes: {
id: '456',
country: 'CA'
}
)