- Notifications
You must be signed in to change notification settings - Fork43
Unleash client SDK for Ruby
License
Unleash/unleash-ruby-sdk
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Unleash is a private, secure, and scalablefeature management platform built to reduce the risk of releasing new features and accelerate software development. This Ruby SDK is designed to help you integrate with Unleash and evaluate feature flags inside your application.
You can use this client withUnleash Enterprise orUnleash Open Source.
Migrating to v6
If you usecustom strategies or override built-in ones, read the completemigration guide before upgrading to v6.
- MRI 3.4
- MRI 3.3
- MRI 3.2
- MRI 3.1
- MRI 3.0
- MRI 2.7
- jruby 9.4
Add this line to your application's Gemfile:
gem'unleash','~> 6.3.0'
And then execute:
$ bundleOr install it yourself as:
$ gem install unleashIt isrequired to configure:
app_namewith the name of the running applicationurlof your Unleash servercustom_http_headerswith{'Authorization': '<YOUR_API_TOKEN>'}when using Unleash v4+
It ishighly recommended to configure:
instance_idparameter with a unique identifier for the running instance
Unleash.configuredo |config|config.app_name='my_ruby_app'config.url='<YOUR_UNLEASH_URL>/api'config.custom_http_headers={'Authorization':'<YOUR_API_TOKEN>'}end
or instantiate the client with the valid configuration:
UNLEASH=Unleash::Client.new(url:'<YOUR_UNLEASH_URL>/api',app_name:'my_ruby_app',custom_http_headers:{'Authorization':'<YOUR_API_TOKEN>'})
If you need custom HTTP headers that change during the lifetime of the client, you can passcustom_http_headers as aProc.
Unleash.configuredo |config|config.app_name='my_ruby_app'config.url='<YOUR_UNLEASH_URL>/api'config.custom_http_headers=procdo{'Authorization':'<YOUR_API_TOKEN>','X-Client-Request-Time':Time.now.iso8601}endend
| Argument | Description | Required? | Type | Default value |
|---|---|---|---|---|
url | Unleash server URL. | Y | String | N/A |
app_name | Name of your program. | Y | String | N/A |
instance_id | Identifier for the running instance of your program—set this to be able trace where metrics are being collected from. | N | String | random UUID |
environment | Unleash context option, for example,prod ordev. Not yet in use.Not the same as the SDK'sUnleash environment. | N | String | default |
project_name | Name of the project to retrieve feature flags from. If not set, all feature flags will be retrieved. | N | String | nil |
refresh_interval | How often the Unleash client should check with the server for configuration changes. | N | Integer | 15 |
metrics_interval | How often the Unleash client should send metrics to server. | N | Integer | 60 |
disable_client | Disables all communication with the Unleash server, effectively taking itoffline. If set,is_enabled? always answer with thedefault_value and configuration validation is skipped. Will also forcefully setdisable_metrics totrue. Defeats the entire purpose of using Unleash, except when running tests. | N | Boolean | false |
disable_metrics | Disables sending metrics to Unleash server. If thedisable_client option is set totrue, then this option will also be set totrue, regardless of the value provided. | N | Boolean | false |
custom_http_headers | Custom headers to send to Unleash. As of Unleash v4.0.0, theAuthorization header is required. For example:{'Authorization': '<YOUR_API_TOKEN>'}. | N | Hash/Proc | {} |
timeout | How long to wait for the connection to be established or wait in reading state (open_timeout/read_timeout) | N | Integer | 30 |
retry_limit | How many consecutive failures in connecting to the Unleash server are allowed before giving up. The default is to retry indefinitely. | N | Float::INFINITY | 5 |
backup_file | Filename to store the last known state from the Unleash server. It is best to not change this from the default. | N | String | Dir.tmpdir + "/unleash-#{app_name}-repo.json |
logger | Specify a customLogger class to handle logs for the Unleash client. | N | Class | Logger.new(STDOUT) |
log_level | Change the log level for theLogger class. Constant fromLogger::Severity. | N | Constant | Logger::WARN |
bootstrap_config | Bootstrap config for loading data on startup—useful for loading large states on startup without (or before) hitting the network. | N | Unleash::Bootstrap::Configuration | nil |
strategies | Strategies manager that holds all strategies and allows to add custom strategies. | N | Unleash::Strategies | Unleash::Strategies.new |
For a more in-depth look, please seelib/unleash/configuration.rb.
| Environment Variable | Description |
|---|---|
UNLEASH_BOOTSTRAP_FILE | File to read bootstrap data from |
UNLEASH_BOOTSTRAP_URL | URL to read bootstrap data from |
require'unleash'require'unleash/context'@unleash=Unleash::Client.new(app_name:'my_ruby_app',url:'<YOUR_UNLEASH_URL>/api',custom_http_headers:{'Authorization':'<YOUR_API_TOKEN>'})feature_name="AwesomeFeature"unleash_context=Unleash::Context.newunleash_context.user_id=123if@unleash.is_enabled?(feature_name,unleash_context)puts"#{feature_name} is enabled according to unleash"elseputs"#{feature_name} is disabled according to unleash"endif@unleash.is_disabled?(feature_name,unleash_context)puts"#{feature_name} is disabled according to unleash"elseputs"#{feature_name} is enabled according to unleash"end
The initializer setup varies depending on whether you’re using a standard setup, Puma in clustered mode, Phusion Passenger, or Sidekiq.
Put inconfig/initializers/unleash.rb:
Unleash.configuredo |config|config.app_name=Rails.application.class.module_parent_nameconfig.url='<YOUR_UNLEASH_URL>'# config.instance_id = "#{Socket.gethostname}"config.logger=Rails.loggerconfig.custom_http_headers={'Authorization':'<YOUR_API_TOKEN>'}endUNLEASH=Unleash::Client.new# Or if preferred:# Rails.configuration.unleash = Unleash::Client.new
Forconfig.instance_id use a string with a unique identification for the running instance. For example, it could be the hostname if you only run one App per host, or the docker container ID, if you are running in Docker.If not set, the client will generate a unique UUID for each execution.
To have it available in therails console command as well, also add to the file above:
Rails.application.consoledoUNLEASH=Unleash::Client.new# or# Rails.configuration.unleash = Unleash::Client.newend
1.b Add Initializer if usingPuma in clustered mode
That is, multiple workers configured inpuma.rb:
workersENV.fetch("WEB_CONCURRENCY"){2}
Then you may keep the client configuration still inconfig/initializers/unleash.rb:
Unleash.configuredo |config|config.app_name=Rails.application.class.parent.to_sconfig.url='<YOUR_UNLEASH_URL>/api'config.custom_http_headers={'Authorization':'<YOUR_API_TOKEN>'}end
But you must ensure that the Unleash client is instantiated only after the process is forked.This is done by creating the client inside theon_worker_boot code block inpuma.rb as below:
#...preload_app!#...on_worker_bootdo# ... ::UNLEASH=Unleash::Client.newendon_worker_shutdowndo ::UNLEASH.shutdownend
By not usingpreload_app!:
- The
Railsconstant willnot be available. - Phased restarts will be possible.
You need to ensure that inpuma.rb:
- The Unleash SDK is loaded with
require 'unleash'explicitly, as it will not be pre-loaded. - All parameters are set explicitly in the
on_worker_bootblock, asconfig/initializers/unleash.rbis not read. - There are no references to
Railsconstant, as that is not yet available.
Example forpuma.rb:
require'unleash'#...# no preload_app!on_worker_bootdo# ... ::UNLEASH=Unleash::Client.new(app_name:'my_rails_app',url:'<YOUR_UNLEASH_URL>/api',custom_http_headers:{'Authorization':'<YOUR_API_TOKEN>'},)endon_worker_shutdowndo ::UNLEASH.shutdownend
Note that we also added shutdown hooks inon_worker_shutdown, to ensure a clean shutdown.
1.c Add Initializer if usingPhusion Passenger
The Unleash client needs to be configured and instantiated inside thePhusionPassenger.on_event(:starting_worker_process) code block due tosmart spawning:
The initializer inconfig/initializers/unleash.rb should look like:
PhusionPassenger.on_event(:starting_worker_process)do |forked|ifforkedUnleash.configuredo |config|config.app_name=Rails.application.class.parent.to_s# config.instance_id = "#{Socket.gethostname}"config.logger=Rails.loggerconfig.url='<YOUR_UNLEASH_URL>/api'config.custom_http_headers={'Authorization':'<YOUR_API_TOKEN>'}endUNLEASH=Unleash::Client.newendend
1.d Add Initializer hooks when using withinSidekiq
Note that in this case, we require that the code block forUnleash.configure is set beforehand.For example inconfig/initializers/unleash.rb.
Sidekiq.configure_serverdo |config|config.on(:startup)doUNLEASH=Unleash::Client.newendconfig.on(:shutdown)doUNLEASH.shutdownendend
Add the following method and callback in the application controller to have@unleash_context set for all requests:
Add inapp/controllers/application_controller.rb:
before_action:set_unleash_contextprivatedefset_unleash_context@unleash_context=Unleash::Context.new(session_id:session.id,remote_address:request.remote_ip,user_id:session[:user_id])end
Alternatively, you can add this method only to the controllers that use Unleash.
Then wherever in your application that you need a feature toggle, you can use:
ifUNLEASH.is_enabled?"AwesomeFeature",@unleash_contextputs"AwesomeFeature is enabled"end
or if client is set inRails.configuration.unleash:
ifRails.configuration.unleash.is_enabled?"AwesomeFeature",@unleash_contextputs"AwesomeFeature is enabled"end
If you don't want to check a feature is disabled withunless, you can also useis_disabled?:
# so instead of:unlessUNLEASH.is_enabled?"AwesomeFeature",@unleash_contextputs"AwesomeFeature is disabled"end# it might be more intelligible:ifUNLEASH.is_disabled?"AwesomeFeature",@unleash_contextputs"AwesomeFeature is disabled"end
If the feature is not found in the server, it will by default return false.However, you can override that by setting the default return value totrue:
ifUNLEASH.is_enabled?"AwesomeFeature",@unleash_context,trueputs"AwesomeFeature is enabled by default"end# orifUNLEASH.is_disabled?"AwesomeFeature",@unleash_context,trueputs"AwesomeFeature is disabled by default"end
Another possibility is to send a block,Lambda orProcto evaluate the default value:
net_check_proc=procdo |feature_name,context|context.remote_address.starts_with?("10.0.0.")endifUNLEASH.is_enabled?("AwesomeFeature",@unleash_context, &net_check_proc)puts"AwesomeFeature is enabled by default if you are in the 10.0.0.* network."end
or
awesomeness=10@unleash_context.properties[:coolness]=10ifUNLEASH.is_enabled?("AwesomeFeature",@unleash_context){ |feat,ctx|awesomeness >=6 &&ctx.properties[:coolness] >=8}puts"AwesomeFeature is enabled by default if both the user has a high enough coolness and the application has a high enough awesomeness"end
Note:
- The block/lambda/proc can use the feature name and context as arguments.
- The client will evaluate the fallback function once per call of
is_enabled().Please keep this in mind when creating your fallback function. - The returned value of the block should be a boolean.However, the client will coerce the result to a boolean via
!!. - If both a
default_valueandfallback_functionare supplied,the client will define the default value byORing the default value and the output of the fallback function.
Alternatively by usingif_enabled (orif_disabled) you can send a code block to be executed as a parameter:
UNLEASH.if_enabled"AwesomeFeature",@unleash_context,truedoputs"AwesomeFeature is enabled by default"end
Note:if_enabled (andif_disabled) only supportdefault_value, but notfallback_function.
If no flag is found in the server, use the fallback variant.
fallback_variant=Unleash::Variant.new(name:'default',enabled:true,payload:{"color"=>"blue"})variant=UNLEASH.get_variant"ColorVariants",@unleash_context,fallback_variantputs"variant color is:#{variant.payload.fetch('color')}"
Bootstrap configuration allows the client to be initialized with a predefined set of toggle states.Bootstrapping can be configured by providing a bootstrap configuration when initializing the client.
@unleash=Unleash::Client.new(url:'<YOUR_UNLEASH_URL>/api',app_name:'my_ruby_app',custom_http_headers:{'Authorization':'<YOUR_API_TOKEN>'},bootstrap_config:Unleash::Bootstrap::Configuration.new({url:"<YOUR_UNLEASH_URL>/api/client/features",url_headers:{'Authorization':'<YOUR_API_TOKEN>'}}))
TheBootstrap::Configuration initializer takes a hash with one of the following options specified:
file_path- An absolute or relative path to a file containing a JSON string of the response body from the Unleash server. This can also be set through theUNLEASH_BOOTSTRAP_FILEenvironment variable.url- A url pointing to an Unleash server's features endpoint, the code sample above is illustrative. This can also be set through theUNLEASH_BOOTSTRAP_URLenvironment variable.url_headers- Headers for the GET HTTP request to theurlabove. Only used if theurlparameter is also set. If this option isn't set then the bootstrapper will use the same url headers as the Unleash client.data- A raw JSON string as returned by the Unleash server.block- A lambda containing custom logic if you need it, an example is provided below.
You should only specify one type of bootstrapping since only one will be invoked and the others will be ignored.The order of preference is as follows:
- Select a data bootstrapper if it exists.
- If no data bootstrapper exists, select the block bootstrapper.
- If no block bootstrapper exists, select the file bootstrapper from either parameters or the specified environment variable.
- If no file bootstrapper exists, then check for a URL bootstrapper from either the parameters or the specified environment variable.
Example usage:
First, save the toggles locally:
curl -H'Authorization: <YOUR_API_TOKEN>' -XGET'<YOUR_UNLEASH_URL>/api'> ./default-toggles.json
Then use them on startup:
custom_boostrapper=lambda{File.read('./default-toggles.json')}@unleash=Unleash::Client.new(app_name:'my_ruby_app',url:'<YOUR_UNLEASH_URL>/api',custom_http_headers:{'Authorization':'<YOUR_API_TOKEN>'},bootstrap_config:Unleash::Bootstrap::Configuration.new({block:custom_boostrapper}))
This example could be easily achieved with a file bootstrapper, this is just to illustrate the usage of custom bootstrapping.Be aware that the client initializer will block until bootstrapping is complete.
| Method name | Description | Return type |
|---|---|---|
is_enabled? | Checks if a feature toggle is enabled or not | Boolean |
enabled? | A more idiomatic Ruby alias for theis_enabled? method | Boolean |
if_enabled | Runs a code block, if a feature is enabled | yield |
is_disabled? | Checks if feature toggle is enabled or not | Boolean |
disabled? | A more idiomatic Ruby alias for theis_disabled? method | Boolean |
if_disabled | Runs a code block, if a feature is disabled | yield |
get_variant | Gets variant for a given feature | Unleash::Variant |
shutdown | Saves metrics to disk, flushes metrics to server, and then killsToggleFetcher andMetricsReporter threads—a safe shutdown, not generally needed in long-running applications, like web applications | nil |
shutdown! | KillsToggleFetcher andMetricsReporter threads immediately | nil |
For the full method signatures, seeclient.rb.
# cli unleash client:bundle exec bin/unleash-client --help# or a simple sample implementation (with values hardcoded):bundle exec examples/simple.rbThis client comes with all the required strategies out of the box:
- ApplicationHostnameStrategy
- DefaultStrategy
- FlexibleRolloutStrategy
- GradualRolloutRandomStrategy
- GradualRolloutSessionIdStrategy
- GradualRolloutUserIdStrategy
- RemoteAddressStrategy
- UnknownStrategy
- UserWithIdStrategy
You can addcustom activation strategies using configuration.In order for the strategy to work correctly it should support two methodsname andis_enabled?.
classMyCustomStrategydefname'myCustomStrategy'enddefis_enabled?(params={},context=nil)trueendendUnleash.configuredo |config|config.strategies.add(MyCustomStrategy.new)end
After checking out the repo, runbin/setup to install dependencies.Then, runbundle exec rake spec to run the tests.You can also runbin/console for an interactive prompt that will allow you to experiment.
This SDK is also built against the Unleash Client Specification tests.To run the Ruby SDK against this test suite, you'll need to have a copy on your machine, you can clone the repository directly using:
git clone --branch v$(ruby echo_client_spec_version.rb) https://github.com/Unleash/client-specification.git
After doing this,bundle exec rake spec will also run the client specification tests.
To install this gem onto your local machine, runbundle exec rake install.
To release a new version, follow these steps:
- Update version number:
- Increment the version number in the
./lib/unleash/version.rbfile according toSemantic Versioning guidelines.
- Increment the version number in the
- Update documentation:
- If the update includes a major or minor version change, update theInstallation section inREADME.md.
- UpdateCHANGELOG.md following the format onKeep a Changelog.
- Commit changes:
- Commit the changes with a message like:
chore: bump version to x.y.z.
- Commit the changes with a message like:
- Release the gem:
- On the
mainbranch, runbundle exec rake releaseto create a git tag for the new version, push commits and tags to origin, and publish.gemfile torubygems.org.
- On the
Bug reports and pull requests are welcome on GitHub athttps://github.com/unleash/unleash-ruby-sdk.
Be sure to run bothbundle exec rspec andbundle exec rubocop in your branch before creating a pull request.
Please include tests with any pull requests, to avoid regressions.
Check out our guide for more information on how to build and scalefeature flag systems.
About
Unleash client SDK for Ruby
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.