unleash 3.2.2 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/add-to-project.yml +14 -0
 - data/.github/workflows/pull_request.yml +79 -0
 - data/.gitignore +5 -1
 - data/.rubocop.yml +114 -6
 - data/README.md +263 -27
 - data/bin/unleash-client +15 -5
 - data/examples/bootstrap.rb +51 -0
 - data/examples/default-toggles.json +42 -0
 - data/examples/simple.rb +5 -4
 - data/lib/unleash/bootstrap/configuration.rb +25 -0
 - data/lib/unleash/bootstrap/handler.rb +22 -0
 - data/lib/unleash/bootstrap/provider/base.rb +14 -0
 - data/lib/unleash/bootstrap/provider/from_file.rb +14 -0
 - data/lib/unleash/bootstrap/provider/from_url.rb +19 -0
 - data/lib/unleash/client.rb +27 -17
 - data/lib/unleash/configuration.rb +41 -19
 - data/lib/unleash/constraint.rb +88 -10
 - data/lib/unleash/context.rb +3 -2
 - data/lib/unleash/feature_toggle.rb +26 -19
 - data/lib/unleash/metrics_reporter.rb +18 -4
 - data/lib/unleash/scheduled_executor.rb +5 -2
 - data/lib/unleash/strategy/application_hostname.rb +1 -0
 - data/lib/unleash/strategy/flexible_rollout.rb +5 -5
 - data/lib/unleash/strategy/remote_address.rb +17 -1
 - data/lib/unleash/toggle_fetcher.rb +51 -42
 - data/lib/unleash/util/http.rb +7 -6
 - data/lib/unleash/variant_definition.rb +5 -4
 - data/lib/unleash/version.rb +1 -1
 - data/lib/unleash.rb +0 -5
 - data/unleash-client.gemspec +3 -2
 - metadata +35 -13
 - data/.travis.yml +0 -15
 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Unleash::Client
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            [
         
     | 
| 
      
 4 
     | 
    
         
            +
            [](https://coveralls.io/github/Unleash/unleash-client-ruby?branch=main)
         
     | 
| 
       5 
5 
     | 
    
         
             
            [](https://badge.fury.io/rb/unleash)
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            Unleash client so you can roll out your features with confidence.
         
     | 
| 
         @@ -10,18 +10,20 @@ Leverage the [Unleash Server](https://github.com/Unleash/unleash) for powerful f 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            ## Supported Ruby Interpreters
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
              * MRI 3.1
         
     | 
| 
       13 
14 
     | 
    
         
             
              * MRI 3.0
         
     | 
| 
       14 
15 
     | 
    
         
             
              * MRI 2.7
         
     | 
| 
       15 
16 
     | 
    
         
             
              * MRI 2.6
         
     | 
| 
       16 
17 
     | 
    
         
             
              * MRI 2.5
         
     | 
| 
       17 
     | 
    
         
            -
              * jruby
         
     | 
| 
      
 18 
     | 
    
         
            +
              * jruby 9.3
         
     | 
| 
      
 19 
     | 
    
         
            +
              * jruby 9.2
         
     | 
| 
       18 
20 
     | 
    
         | 
| 
       19 
21 
     | 
    
         
             
            ## Installation
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
       22 
24 
     | 
    
         | 
| 
       23 
25 
     | 
    
         
             
            ```ruby
         
     | 
| 
       24 
     | 
    
         
            -
            gem 'unleash', '~>  
     | 
| 
      
 26 
     | 
    
         
            +
            gem 'unleash', '~> 4.0.0'
         
     | 
| 
       25 
27 
     | 
    
         
             
            ```
         
     | 
| 
       26 
28 
     | 
    
         | 
| 
       27 
29 
     | 
    
         
             
            And then execute:
         
     | 
| 
         @@ -34,22 +36,45 @@ Or install it yourself as: 
     | 
|
| 
       34 
36 
     | 
    
         | 
| 
       35 
37 
     | 
    
         
             
            ## Configure
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
            It is **required** to configure 
     | 
| 
      
 39 
     | 
    
         
            +
            It is **required** to configure:
         
     | 
| 
      
 40 
     | 
    
         
            +
            - `url` of the unleash server
         
     | 
| 
      
 41 
     | 
    
         
            +
            - `app_name` with the name of the runninng application.
         
     | 
| 
      
 42 
     | 
    
         
            +
            - `custom_http_headers` with `{'Authorization': '<API token>'}` when using Unleash v4.0.0 and later.
         
     | 
| 
       38 
43 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
            Please substitute the example `'https://unleash.herokuapp.com/api'` for the url of your own instance.
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            It is **highly recommended** to configure:
         
     | 
| 
      
 47 
     | 
    
         
            +
            - `instance_id` parameter with a unique identifier for the running instance.
         
     | 
| 
       40 
48 
     | 
    
         | 
| 
       41 
49 
     | 
    
         | 
| 
       42 
50 
     | 
    
         
             
            ```ruby
         
     | 
| 
       43 
51 
     | 
    
         
             
            Unleash.configure do |config|
         
     | 
| 
       44 
     | 
    
         
            -
              config. 
     | 
| 
       45 
     | 
    
         
            -
              config. 
     | 
| 
      
 52 
     | 
    
         
            +
              config.app_name            = 'my_ruby_app'
         
     | 
| 
      
 53 
     | 
    
         
            +
              config.url                 = 'https://unleash.herokuapp.com/api'
         
     | 
| 
      
 54 
     | 
    
         
            +
              config.custom_http_headers = {'Authorization': '<API token>'}
         
     | 
| 
       46 
55 
     | 
    
         
             
            end
         
     | 
| 
       47 
56 
     | 
    
         
             
            ```
         
     | 
| 
       48 
57 
     | 
    
         | 
| 
       49 
58 
     | 
    
         
             
            or instantiate the client with the valid configuration:
         
     | 
| 
       50 
59 
     | 
    
         | 
| 
       51 
60 
     | 
    
         
             
            ```ruby
         
     | 
| 
       52 
     | 
    
         
            -
            UNLEASH = Unleash::Client.new(url: ' 
     | 
| 
      
 61 
     | 
    
         
            +
            UNLEASH = Unleash::Client.new(url: 'https://unleash.herokuapp.com/api', app_name: 'my_ruby_app', custom_http_headers: {'Authorization': '<API token>'})
         
     | 
| 
      
 62 
     | 
    
         
            +
            ```
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            ## Dynamic custom HTTP headers
         
     | 
| 
      
 65 
     | 
    
         
            +
            If you need custom HTTP headers that change during the lifetime of the client, the `custom_http_headers` can be given as a `Proc`.
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 68 
     | 
    
         
            +
            Unleash.configure do |config|
         
     | 
| 
      
 69 
     | 
    
         
            +
              config.app_name            = 'my_ruby_app'
         
     | 
| 
      
 70 
     | 
    
         
            +
              config.url                 = 'https://unleash.herokuapp.com/api'
         
     | 
| 
      
 71 
     | 
    
         
            +
              config.custom_http_headers =  proc do
         
     | 
| 
      
 72 
     | 
    
         
            +
                {
         
     | 
| 
      
 73 
     | 
    
         
            +
                  'Authorization': '<API token>',
         
     | 
| 
      
 74 
     | 
    
         
            +
                  'X-Client-Request-Time': Time.now.iso8601
         
     | 
| 
      
 75 
     | 
    
         
            +
                }
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
       53 
78 
     | 
    
         
             
            ```
         
     | 
| 
       54 
79 
     | 
    
         | 
| 
       55 
80 
     | 
    
         
             
            #### List of Arguments
         
     | 
| 
         @@ -60,19 +85,25 @@ Argument | Description | Required? |  Type |  Default Value| 
     | 
|
| 
       60 
85 
     | 
    
         
             
            `app_name` | Name of your program. | Y | String | N/A |
         
     | 
| 
       61 
86 
     | 
    
         
             
            `instance_id` | Identifier for the running instance of program. Important so you can trace back to where metrics are being collected from. **Highly recommended be be set.** | N | String | random UUID |
         
     | 
| 
       62 
87 
     | 
    
         
             
            `environment` | Environment the program is running on. Could be for example `prod` or `dev`. Not yet in use. | N | String | `default` |
         
     | 
| 
      
 88 
     | 
    
         
            +
            `project_name` | Name of the project to retrieve features from. If not set, all feature flags will be retrieved. | N | String | nil |
         
     | 
| 
       63 
89 
     | 
    
         
             
            `refresh_interval` | How often the unleash client should check with the server for configuration changes. | N | Integer |  15 |
         
     | 
| 
       64 
     | 
    
         
            -
            `metrics_interval` | How often the unleash client should send metrics to server. | N | Integer |  
     | 
| 
      
 90 
     | 
    
         
            +
            `metrics_interval` | How often the unleash client should send metrics to server. | N | Integer | 60 |
         
     | 
| 
       65 
91 
     | 
    
         
             
            `disable_client` | Disables all communication with the Unleash server, effectively taking it *offline*. If set, `is_enabled?` will always answer with the `default_value` and configuration validation is skipped. Defeats the entire purpose of using unleash, but can be useful in when running tests. | N | Boolean | `false` |
         
     | 
| 
       66 
92 
     | 
    
         
             
            `disable_metrics` | Disables sending metrics to Unleash server. | N | Boolean | `false` |
         
     | 
| 
       67 
     | 
    
         
            -
            `custom_http_headers` | Custom headers to send to Unleash. | N | Hash | {} |
         
     | 
| 
      
 93 
     | 
    
         
            +
            `custom_http_headers` | Custom headers to send to Unleash. As of Unleash v4.0.0, the `Authorization` header is required. For example: `{'Authorization': '<API token>'}` | N | Hash/Proc | {} |
         
     | 
| 
       68 
94 
     | 
    
         
             
            `timeout` | How long to wait for the connection to be established or wait in reading state (open_timeout/read_timeout) | N | Integer | 30 |
         
     | 
| 
       69 
     | 
    
         
            -
            `retry_limit` | How many consecutive failures in connecting to the Unleash server are allowed before giving up.  
     | 
| 
      
 95 
     | 
    
         
            +
            `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 |
         
     | 
| 
       70 
96 
     | 
    
         
             
            `backup_file` | Filename to store the last known state from the Unleash server. Best to not change this from the default. | N | String | `Dir.tmpdir + "/unleash-#{app_name}-repo.json` |
         
     | 
| 
       71 
97 
     | 
    
         
             
            `logger` | Specify a custom `Logger` class to handle logs for the Unleash client. | N | Class | `Logger.new(STDOUT)` |
         
     | 
| 
       72 
     | 
    
         
            -
            `log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger:: 
     | 
| 
      
 98 
     | 
    
         
            +
            `log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger::WARN` |
         
     | 
| 
      
 99 
     | 
    
         
            +
            `bootstrap_config` | Bootstrap config on how to loaded data on start-up. This is useful for loading large states on startup without (or before) hitting the network. | N | Unleash::Bootstrap::Configuration | `nil` |
         
     | 
| 
       73 
100 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
            For  
     | 
| 
      
 101 
     | 
    
         
            +
            For a more in-depth look, please see `lib/unleash/configuration.rb`.
         
     | 
| 
       75 
102 
     | 
    
         | 
| 
      
 103 
     | 
    
         
            +
            Environment Variable | Description
         
     | 
| 
      
 104 
     | 
    
         
            +
            ---------|---------
         
     | 
| 
      
 105 
     | 
    
         
            +
            `UNLEASH_BOOTSTRAP_FILE` | File to read bootstrap data from
         
     | 
| 
      
 106 
     | 
    
         
            +
            `UNLEASH_BOOTSTRAP_URL` | URL to read bootstrap data from
         
     | 
| 
       76 
107 
     | 
    
         | 
| 
       77 
108 
     | 
    
         
             
            ## Usage in a plain Ruby Application
         
     | 
| 
       78 
109 
     | 
    
         | 
| 
         @@ -80,7 +111,7 @@ For in a more in depth look, please see `lib/unleash/configuration.rb`. 
     | 
|
| 
       80 
111 
     | 
    
         
             
            require 'unleash'
         
     | 
| 
       81 
112 
     | 
    
         
             
            require 'unleash/context'
         
     | 
| 
       82 
113 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
            @unleash = Unleash::Client.new(url: ' 
     | 
| 
      
 114 
     | 
    
         
            +
            @unleash = Unleash::Client.new(app_name: 'my_ruby_app', url: 'https://unleash.herokuapp.com/api', custom_http_headers: { 'Authorization': '<API token>' })
         
     | 
| 
       84 
115 
     | 
    
         | 
| 
       85 
116 
     | 
    
         
             
            feature_name = "AwesomeFeature"
         
     | 
| 
       86 
117 
     | 
    
         
             
            unleash_context = Unleash::Context.new
         
     | 
| 
         @@ -101,37 +132,145 @@ Put in `config/initializers/unleash.rb`: 
     | 
|
| 
       101 
132 
     | 
    
         | 
| 
       102 
133 
     | 
    
         
             
            ```ruby
         
     | 
| 
       103 
134 
     | 
    
         
             
            Unleash.configure do |config|
         
     | 
| 
       104 
     | 
    
         
            -
              config.url      = 'http://unleash.herokuapp.com/api'
         
     | 
| 
       105 
135 
     | 
    
         
             
              config.app_name = Rails.application.class.parent.to_s
         
     | 
| 
      
 136 
     | 
    
         
            +
              config.url      = 'https://unleash.herokuapp.com/api'
         
     | 
| 
       106 
137 
     | 
    
         
             
              # config.instance_id = "#{Socket.gethostname}"
         
     | 
| 
       107 
138 
     | 
    
         
             
              config.logger   = Rails.logger
         
     | 
| 
       108 
139 
     | 
    
         
             
              config.environment = Rails.env
         
     | 
| 
       109 
140 
     | 
    
         
             
            end
         
     | 
| 
       110 
141 
     | 
    
         | 
| 
       111 
142 
     | 
    
         
             
            UNLEASH = Unleash::Client.new
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
            # Or if preferred:
         
     | 
| 
      
 145 
     | 
    
         
            +
            # Rails.configuration.unleash = Unleash::Client.new
         
     | 
| 
      
 146 
     | 
    
         
            +
            ```
         
     | 
| 
      
 147 
     | 
    
         
            +
            For `config.instance_id` use a string with a unique identification for the running instance.
         
     | 
| 
      
 148 
     | 
    
         
            +
            For example: it could be the hostname, if you only run one App per host.
         
     | 
| 
      
 149 
     | 
    
         
            +
            Or the docker container id, if you are running in docker.
         
     | 
| 
      
 150 
     | 
    
         
            +
            If it is not set the client will generate an unique UUID for each execution.
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
            To have it available in the `rails console` command as well, also add to the file above:
         
     | 
| 
      
 153 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 154 
     | 
    
         
            +
            Rails.application.console do
         
     | 
| 
      
 155 
     | 
    
         
            +
              UNLEASH = Unleash::Client.new
         
     | 
| 
      
 156 
     | 
    
         
            +
              # or
         
     | 
| 
      
 157 
     | 
    
         
            +
              # Rails.configuration.unleash = Unleash::Client.new
         
     | 
| 
      
 158 
     | 
    
         
            +
            end
         
     | 
| 
      
 159 
     | 
    
         
            +
            ```
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
            #### Add Initializer if using [Puma in clustered mode](https://github.com/puma/puma#clustered-mode)
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            That is, multiple workers configured in `puma.rb`:
         
     | 
| 
      
 164 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 165 
     | 
    
         
            +
            workers ENV.fetch("WEB_CONCURRENCY") { 2 }
         
     | 
| 
      
 166 
     | 
    
         
            +
            ```
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
            ##### with `preload_app!`
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
            Then you may keep the client configuration still in `config/initializers/unleash.rb`:
         
     | 
| 
      
 171 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 172 
     | 
    
         
            +
            Unleash.configure do |config|
         
     | 
| 
      
 173 
     | 
    
         
            +
              config.app_name    = Rails.application.class.parent.to_s
         
     | 
| 
      
 174 
     | 
    
         
            +
              config.environment = Rails.env
         
     | 
| 
      
 175 
     | 
    
         
            +
              config.url                 = 'https://unleash.herokuapp.com/api'
         
     | 
| 
      
 176 
     | 
    
         
            +
              config.custom_http_headers = {'Authorization': '<API token>'}
         
     | 
| 
      
 177 
     | 
    
         
            +
            end
         
     | 
| 
      
 178 
     | 
    
         
            +
            ```
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
            But you must ensure that the unleash client is instantiated only after the process is forked.
         
     | 
| 
      
 181 
     | 
    
         
            +
            This is done by creating the client inside the `on_worker_boot` code block in `puma.rb` as below:
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 184 
     | 
    
         
            +
            #...
         
     | 
| 
      
 185 
     | 
    
         
            +
            preload_app!
         
     | 
| 
      
 186 
     | 
    
         
            +
            #...
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
            on_worker_boot do
         
     | 
| 
      
 189 
     | 
    
         
            +
              # ...
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
              ::UNLEASH = Unleash::Client.new
         
     | 
| 
      
 192 
     | 
    
         
            +
            end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
            on_worker_shutdown do
         
     | 
| 
      
 195 
     | 
    
         
            +
              ::UNLEASH.shutdown
         
     | 
| 
      
 196 
     | 
    
         
            +
            end
         
     | 
| 
       112 
197 
     | 
    
         
             
            ```
         
     | 
| 
       113 
     | 
    
         
            -
            For `config.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 it is not set the client will generate an unique UUID for each execution.
         
     | 
| 
       114 
198 
     | 
    
         | 
| 
      
 199 
     | 
    
         
            +
            ##### without `preload_app!`
         
     | 
| 
       115 
200 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
      
 201 
     | 
    
         
            +
            By not using `preload_app!`:
         
     | 
| 
      
 202 
     | 
    
         
            +
            - the `Rails` constant will NOT be available.
         
     | 
| 
      
 203 
     | 
    
         
            +
            - but phased restarts will be possible.
         
     | 
| 
       117 
204 
     | 
    
         | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
      
 205 
     | 
    
         
            +
            You need to ensure that in `puma.rb`:
         
     | 
| 
      
 206 
     | 
    
         
            +
            - loading unleash sdk with `require 'unleash'` explicitly, as it will not be pre-loaded.
         
     | 
| 
      
 207 
     | 
    
         
            +
            - all parameters must be explicitly set in the `on_worker_boot` block, as `config/initializers/unleash.rb` is not read.
         
     | 
| 
      
 208 
     | 
    
         
            +
            - there are no references to `Rails` constant, as that is not yet available.
         
     | 
| 
       119 
209 
     | 
    
         | 
| 
      
 210 
     | 
    
         
            +
            Example for `puma.rb`:
         
     | 
| 
       120 
211 
     | 
    
         
             
            ```ruby
         
     | 
| 
      
 212 
     | 
    
         
            +
            require 'unleash'
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
            #...
         
     | 
| 
      
 215 
     | 
    
         
            +
            # no preload_app!
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
       121 
217 
     | 
    
         
             
            on_worker_boot do
         
     | 
| 
       122 
218 
     | 
    
         
             
              # ...
         
     | 
| 
       123 
219 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
              Unleash. 
     | 
| 
       125 
     | 
    
         
            -
                 
     | 
| 
       126 
     | 
    
         
            -
                 
     | 
| 
       127 
     | 
    
         
            -
                 
     | 
| 
      
 220 
     | 
    
         
            +
              ::UNLEASH = Unleash::Client.new(
         
     | 
| 
      
 221 
     | 
    
         
            +
                app_name: 'my_rails_app',
         
     | 
| 
      
 222 
     | 
    
         
            +
                environment: 'development',
         
     | 
| 
      
 223 
     | 
    
         
            +
                url: 'https://unleash.herokuapp.com/api',
         
     | 
| 
      
 224 
     | 
    
         
            +
                custom_http_headers: {'Authorization': '<API token>'},
         
     | 
| 
      
 225 
     | 
    
         
            +
              )
         
     | 
| 
      
 226 
     | 
    
         
            +
            end
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
            on_worker_shutdown do
         
     | 
| 
      
 229 
     | 
    
         
            +
              ::UNLEASH.shutdown
         
     | 
| 
      
 230 
     | 
    
         
            +
            end
         
     | 
| 
      
 231 
     | 
    
         
            +
            ```
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
            Note that we also added shutdown hooks in `on_worker_shutdown`, to ensure a clean shutdown.
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
            #### Add Initializer if using [Phusion Passenger](https://github.com/phusion/passenger)
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
            The unleash client needs to be configured and instantiated inside the `PhusionPassenger.on_event(:starting_worker_process)` code block due to [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#smart-spawning-caveats):
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
            The initializer in `config/initializers/unleash.rb` should look like:
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 242 
     | 
    
         
            +
            PhusionPassenger.on_event(:starting_worker_process) do |forked|
         
     | 
| 
      
 243 
     | 
    
         
            +
              if forked
         
     | 
| 
      
 244 
     | 
    
         
            +
                Unleash.configure do |config|
         
     | 
| 
      
 245 
     | 
    
         
            +
                  config.app_name    = Rails.application.class.parent.to_s
         
     | 
| 
      
 246 
     | 
    
         
            +
                  # config.instance_id = "#{Socket.gethostname}"
         
     | 
| 
      
 247 
     | 
    
         
            +
                  config.logger      = Rails.logger
         
     | 
| 
      
 248 
     | 
    
         
            +
                  config.environment = Rails.env
         
     | 
| 
      
 249 
     | 
    
         
            +
                  config.url                 = 'https://unleash.herokuapp.com/api'
         
     | 
| 
      
 250 
     | 
    
         
            +
                  config.custom_http_headers = {'Authorization': '<API token>'}
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                UNLEASH = Unleash::Client.new
         
     | 
| 
       128 
254 
     | 
    
         
             
              end
         
     | 
| 
       129 
     | 
    
         
            -
              Rails.configuration.unleash = Unleash::Client.new
         
     | 
| 
       130 
255 
     | 
    
         
             
            end
         
     | 
| 
       131 
256 
     | 
    
         
             
            ```
         
     | 
| 
       132 
257 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
      
 258 
     | 
    
         
            +
            #### Add Initializer hooks when using within [Sidekiq](https://github.com/mperham/sidekiq)
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
            Note that in this case we require that the code block for `Unleash.configure` is set beforehand.
         
     | 
| 
      
 261 
     | 
    
         
            +
            For example in `config/initializers/unleash.rb`.
         
     | 
| 
       134 
262 
     | 
    
         | 
| 
      
 263 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 264 
     | 
    
         
            +
            Sidekiq.configure_server do |config|
         
     | 
| 
      
 265 
     | 
    
         
            +
              config.on(:startup) do
         
     | 
| 
      
 266 
     | 
    
         
            +
                UNLEASH = Unleash::Client.new
         
     | 
| 
      
 267 
     | 
    
         
            +
              end
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
              config.on(:shutdown) do
         
     | 
| 
      
 270 
     | 
    
         
            +
                UNLEASH.shutdown
         
     | 
| 
      
 271 
     | 
    
         
            +
              end
         
     | 
| 
      
 272 
     | 
    
         
            +
            end
         
     | 
| 
      
 273 
     | 
    
         
            +
            ```
         
     | 
| 
       135 
274 
     | 
    
         | 
| 
       136 
275 
     | 
    
         
             
            #### Set Unleash::Context
         
     | 
| 
       137 
276 
     | 
    
         | 
| 
         @@ -172,7 +311,8 @@ if Rails.configuration.unleash.is_enabled? "AwesomeFeature", @unleash_context 
     | 
|
| 
       172 
311 
     | 
    
         
             
            end
         
     | 
| 
       173 
312 
     | 
    
         
             
            ```
         
     | 
| 
       174 
313 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
            If the feature is not found in the server, it will by default return false. 
     | 
| 
      
 314 
     | 
    
         
            +
            If the feature is not found in the server, it will by default return false.
         
     | 
| 
      
 315 
     | 
    
         
            +
            However you can override that by setting the default return value to `true`:
         
     | 
| 
       176 
316 
     | 
    
         | 
| 
       177 
317 
     | 
    
         
             
            ```ruby
         
     | 
| 
       178 
318 
     | 
    
         
             
            if UNLEASH.is_enabled? "AwesomeFeature", @unleash_context, true
         
     | 
| 
         @@ -180,6 +320,40 @@ if UNLEASH.is_enabled? "AwesomeFeature", @unleash_context, true 
     | 
|
| 
       180 
320 
     | 
    
         
             
            end
         
     | 
| 
       181 
321 
     | 
    
         
             
            ```
         
     | 
| 
       182 
322 
     | 
    
         | 
| 
      
 323 
     | 
    
         
            +
            Another possibility is to send a block, [Lambda](https://ruby-doc.org/core-3.0.1/Kernel.html#method-i-lambda) or [Proc](https://ruby-doc.org/core-3.0.1/Proc.html#method-i-yield)
         
     | 
| 
      
 324 
     | 
    
         
            +
            to evaluate the default value:
         
     | 
| 
      
 325 
     | 
    
         
            +
             
     | 
| 
      
 326 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 327 
     | 
    
         
            +
            net_check_proc = proc do |feature_name, context|
         
     | 
| 
      
 328 
     | 
    
         
            +
              context.remote_address.starts_with?("10.0.0.")
         
     | 
| 
      
 329 
     | 
    
         
            +
            end
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
            if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context, &net_check_proc)
         
     | 
| 
      
 332 
     | 
    
         
            +
              puts "AwesomeFeature is enabled by default if you are in the 10.0.0.* network."
         
     | 
| 
      
 333 
     | 
    
         
            +
            end
         
     | 
| 
      
 334 
     | 
    
         
            +
            ```
         
     | 
| 
      
 335 
     | 
    
         
            +
             
     | 
| 
      
 336 
     | 
    
         
            +
            or
         
     | 
| 
      
 337 
     | 
    
         
            +
             
     | 
| 
      
 338 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 339 
     | 
    
         
            +
            awesomeness = 10
         
     | 
| 
      
 340 
     | 
    
         
            +
            @unleash_context.properties[:coolness] = 10
         
     | 
| 
      
 341 
     | 
    
         
            +
             
     | 
| 
      
 342 
     | 
    
         
            +
            if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context) { |feat, ctx| awesomeness >= 6 && ctx.properties[:coolness] >= 8 }
         
     | 
| 
      
 343 
     | 
    
         
            +
              puts "AwesomeFeature is enabled by default if both the user has a high enought coolness and the application has a high enough awesomeness"
         
     | 
| 
      
 344 
     | 
    
         
            +
            end
         
     | 
| 
      
 345 
     | 
    
         
            +
            ```
         
     | 
| 
      
 346 
     | 
    
         
            +
             
     | 
| 
      
 347 
     | 
    
         
            +
            Note:
         
     | 
| 
      
 348 
     | 
    
         
            +
            - The block/lambda/proc can use feature name and context as an arguments.
         
     | 
| 
      
 349 
     | 
    
         
            +
            - The client will evaluate the fallback function once per call of `is_enabled()`.
         
     | 
| 
      
 350 
     | 
    
         
            +
              Please keep this in mind when creating your fallback function!
         
     | 
| 
      
 351 
     | 
    
         
            +
            - The returned value of the block should be a boolean.
         
     | 
| 
      
 352 
     | 
    
         
            +
              However the client will coerce the result to boolean via `!!`.
         
     | 
| 
      
 353 
     | 
    
         
            +
            - If both a `default_value` and `fallback_function` are supplied,
         
     | 
| 
      
 354 
     | 
    
         
            +
              the client will define the default value by `OR`ing the default value and the output of the fallback function.
         
     | 
| 
      
 355 
     | 
    
         
            +
             
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
       183 
357 
     | 
    
         
             
            Alternatively by using `if_enabled` you can send a code block to be executed as a parameter:
         
     | 
| 
       184 
358 
     | 
    
         | 
| 
       185 
359 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -188,6 +362,8 @@ UNLEASH.if_enabled "AwesomeFeature", @unleash_context, true do 
     | 
|
| 
       188 
362 
     | 
    
         
             
            end
         
     | 
| 
       189 
363 
     | 
    
         
             
            ```
         
     | 
| 
       190 
364 
     | 
    
         | 
| 
      
 365 
     | 
    
         
            +
            Note: `if_enabled` only supports `default_value`, but not `fallback_function`.
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
       191 
367 
     | 
    
         
             
            ##### Variations
         
     | 
| 
       192 
368 
     | 
    
         | 
| 
       193 
369 
     | 
    
         
             
            If no variant is found in the server, use the fallback variant.
         
     | 
| 
         @@ -199,6 +375,65 @@ variant = UNLEASH.get_variant "ColorVariants", @unleash_context, fallback_varian 
     | 
|
| 
       199 
375 
     | 
    
         
             
            puts "variant color is: #{variant.payload.fetch('color')}"
         
     | 
| 
       200 
376 
     | 
    
         
             
            ```
         
     | 
| 
       201 
377 
     | 
    
         | 
| 
      
 378 
     | 
    
         
            +
            ## Bootstrapping
         
     | 
| 
      
 379 
     | 
    
         
            +
             
     | 
| 
      
 380 
     | 
    
         
            +
            Bootstrap configuration allows the client to be initialized with a predefined set of toggle states.
         
     | 
| 
      
 381 
     | 
    
         
            +
            Bootstrapping can be configured by providing a bootstrap configuration when initializing the client.
         
     | 
| 
      
 382 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 383 
     | 
    
         
            +
            @unleash = Unleash::Client.new(
         
     | 
| 
      
 384 
     | 
    
         
            +
                url: 'https://unleash.herokuapp.com/api',
         
     | 
| 
      
 385 
     | 
    
         
            +
                app_name: 'my_ruby_app',
         
     | 
| 
      
 386 
     | 
    
         
            +
                custom_http_headers: { 'Authorization': '<API token>' },
         
     | 
| 
      
 387 
     | 
    
         
            +
                bootstrap_config: Unleash::Bootstrap::Configuration.new({
         
     | 
| 
      
 388 
     | 
    
         
            +
                    url: "https://unleash.herokuapp.com/api/client/features",
         
     | 
| 
      
 389 
     | 
    
         
            +
                    url_headers: {'Authorization': '<API token>'}
         
     | 
| 
      
 390 
     | 
    
         
            +
                })
         
     | 
| 
      
 391 
     | 
    
         
            +
            )
         
     | 
| 
      
 392 
     | 
    
         
            +
            ```
         
     | 
| 
      
 393 
     | 
    
         
            +
            The `Bootstrap::Configuration` initializer takes a hash with one of the following options specified:
         
     | 
| 
      
 394 
     | 
    
         
            +
             
     | 
| 
      
 395 
     | 
    
         
            +
            * `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 though the `UNLEASH_BOOTSTRAP_FILE` environment variable.
         
     | 
| 
      
 396 
     | 
    
         
            +
            * `url` - A url pointing to an Unleash server's features endpoint, the code sample above is illustrative. This can also be set though the `UNLEASH_BOOTSTRAP_URL` environment variable.
         
     | 
| 
      
 397 
     | 
    
         
            +
            * `url_headers` - Headers for the GET http request to the `url` above. Only used if the `url` parameter is also set. If this option isn't set then the bootstrapper will use the same url headers as the Unleash client.
         
     | 
| 
      
 398 
     | 
    
         
            +
            * `data` - A raw JSON string as returned by the Unleash server.
         
     | 
| 
      
 399 
     | 
    
         
            +
            * `block` - A lambda containing custom logic if you need it, an example is provided below.
         
     | 
| 
      
 400 
     | 
    
         
            +
             
     | 
| 
      
 401 
     | 
    
         
            +
            You should only specify one type of bootstrapping since only one will be invoked and the others will be ignored.
         
     | 
| 
      
 402 
     | 
    
         
            +
            The order of preference is as follows:
         
     | 
| 
      
 403 
     | 
    
         
            +
             
     | 
| 
      
 404 
     | 
    
         
            +
            - Select a data bootstrapper if it exists.
         
     | 
| 
      
 405 
     | 
    
         
            +
            - If no data bootstrapper exists, select the block bootstrapper.
         
     | 
| 
      
 406 
     | 
    
         
            +
            - If no block bootstrapper exists, select the file bootstrapper from either parameters or the specified environment variable.
         
     | 
| 
      
 407 
     | 
    
         
            +
            - If no file bootstrapper exists, then check for a URL bootstrapper from either the parameters or the specified environment variable.
         
     | 
| 
      
 408 
     | 
    
         
            +
             
     | 
| 
      
 409 
     | 
    
         
            +
             
     | 
| 
      
 410 
     | 
    
         
            +
            Example usage:
         
     | 
| 
      
 411 
     | 
    
         
            +
             
     | 
| 
      
 412 
     | 
    
         
            +
            First saving the toggles locally:
         
     | 
| 
      
 413 
     | 
    
         
            +
            ```shell
         
     | 
| 
      
 414 
     | 
    
         
            +
            curl -H 'Authorization: <API token>' -XGET 'https://unleash.herokuapp.com/api' > ./default-toggles.json
         
     | 
| 
      
 415 
     | 
    
         
            +
            ```
         
     | 
| 
      
 416 
     | 
    
         
            +
             
     | 
| 
      
 417 
     | 
    
         
            +
            Now using them on start up:
         
     | 
| 
      
 418 
     | 
    
         
            +
             
     | 
| 
      
 419 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 420 
     | 
    
         
            +
             
     | 
| 
      
 421 
     | 
    
         
            +
            custom_boostrapper = lambda {
         
     | 
| 
      
 422 
     | 
    
         
            +
              File.read('./default-toggles.json')
         
     | 
| 
      
 423 
     | 
    
         
            +
            }
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
            @unleash = Unleash::Client.new(
         
     | 
| 
      
 426 
     | 
    
         
            +
                app_name: 'my_ruby_app',
         
     | 
| 
      
 427 
     | 
    
         
            +
                url: 'https://unleash.herokuapp.com/api',
         
     | 
| 
      
 428 
     | 
    
         
            +
                custom_http_headers: { 'Authorization': '<API token>' },
         
     | 
| 
      
 429 
     | 
    
         
            +
                bootstrap_config: Unleash::Bootstrap::Configuration.new({
         
     | 
| 
      
 430 
     | 
    
         
            +
                    block: custom_boostrapper
         
     | 
| 
      
 431 
     | 
    
         
            +
                })
         
     | 
| 
      
 432 
     | 
    
         
            +
            )
         
     | 
| 
      
 433 
     | 
    
         
            +
            ```
         
     | 
| 
      
 434 
     | 
    
         
            +
             
     | 
| 
      
 435 
     | 
    
         
            +
            This example could be easily achieved with a file bootstrapper, this is just to illustrate the usage of custom bootstrapping.
         
     | 
| 
      
 436 
     | 
    
         
            +
            Be aware that the client initializer will block until bootstrapping is complete.
         
     | 
| 
       202 
437 
     | 
    
         | 
| 
       203 
438 
     | 
    
         
             
            #### Client methods
         
     | 
| 
       204 
439 
     | 
    
         | 
| 
         @@ -211,6 +446,7 @@ Method Name | Description | Return Type | 
     | 
|
| 
       211 
446 
     | 
    
         
             
            `shutdown` | Save metrics to disk, flush metrics to server, and then kill ToggleFetcher and MetricsReporter threads. A safe shutdown. Not really useful in long running applications, like web applications. | nil |
         
     | 
| 
       212 
447 
     | 
    
         
             
            `shutdown!` | Kill ToggleFetcher and MetricsReporter threads immediately. | nil |
         
     | 
| 
       213 
448 
     | 
    
         | 
| 
      
 449 
     | 
    
         
            +
            For the full method signatures, please see [client.rb](lib/unleash/client.rb)
         
     | 
| 
       214 
450 
     | 
    
         | 
| 
       215 
451 
     | 
    
         
             
            ## Local test client
         
     | 
| 
       216 
452 
     | 
    
         | 
| 
         @@ -228,6 +464,7 @@ This client comes with the all the required strategies out of the box: 
     | 
|
| 
       228 
464 
     | 
    
         | 
| 
       229 
465 
     | 
    
         
             
             * ApplicationHostnameStrategy
         
     | 
| 
       230 
466 
     | 
    
         
             
             * DefaultStrategy
         
     | 
| 
      
 467 
     | 
    
         
            +
             * FlexibleRolloutStrategy
         
     | 
| 
       231 
468 
     | 
    
         
             
             * GradualRolloutRandomStrategy
         
     | 
| 
       232 
469 
     | 
    
         
             
             * GradualRolloutSessionIdStrategy
         
     | 
| 
       233 
470 
     | 
    
         
             
             * GradualRolloutUserIdStrategy
         
     | 
| 
         @@ -235,7 +472,6 @@ This client comes with the all the required strategies out of the box: 
     | 
|
| 
       235 
472 
     | 
    
         
             
             * UnknownStrategy
         
     | 
| 
       236 
473 
     | 
    
         
             
             * UserWithIdStrategy
         
     | 
| 
       237 
474 
     | 
    
         | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
475 
     | 
    
         
             
            ## Development
         
     | 
| 
       240 
476 
     | 
    
         | 
| 
       241 
477 
     | 
    
         
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
    
        data/bin/unleash-client
    CHANGED
    
    | 
         @@ -12,11 +12,13 @@ options = { 
     | 
|
| 
       12 
12 
     | 
    
         
             
              url: 'http://localhost:4242',
         
     | 
| 
       13 
13 
     | 
    
         
             
              demo: false,
         
     | 
| 
       14 
14 
     | 
    
         
             
              disable_metrics: true,
         
     | 
| 
      
 15 
     | 
    
         
            +
              custom_http_headers: {},
         
     | 
| 
       15 
16 
     | 
    
         
             
              sleep: 0.1
         
     | 
| 
       16 
17 
     | 
    
         
             
            }
         
     | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
            OptionParser.new do |opts|
         
     | 
| 
       19 
     | 
    
         
            -
              opts.banner = "Usage: #{__FILE__} [options] feature [ 
     | 
| 
      
 20 
     | 
    
         
            +
              opts.banner = "Usage: #{__FILE__} [options] feature [contextKey1=val1] [contextKey2=val2] \n\n" \
         
     | 
| 
      
 21 
     | 
    
         
            +
              "Where contextKey1 could be user_id, session_id, remote_address or any field in the Context class (or any property within it).\n"
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
              opts.on("-V", "--variant", "Fetch variant for feature") do |v|
         
     | 
| 
       22 
24 
     | 
    
         
             
                options[:variant] = v
         
     | 
| 
         @@ -46,6 +48,13 @@ OptionParser.new do |opts| 
     | 
|
| 
       46 
48 
     | 
    
         
             
                options[:sleep] = s
         
     | 
| 
       47 
49 
     | 
    
         
             
              end
         
     | 
| 
       48 
50 
     | 
    
         | 
| 
      
 51 
     | 
    
         
            +
              opts.on("-H", "--http-headers='Authorization: *:developement.secretstring'",
         
     | 
| 
      
 52 
     | 
    
         
            +
                      "Adds http headers to all requests on the unleash server. Use multiple times for multiple headers.") do |h|
         
     | 
| 
      
 53 
     | 
    
         
            +
                http_header_as_hash = [h].to_h{ |l| l.split(": ") }.transform_keys(&:to_sym)
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                options[:custom_http_headers].merge!(http_header_as_hash)
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
       49 
58 
     | 
    
         
             
              opts.on("-h", "--help", "Prints this help") do
         
     | 
| 
       50 
59 
     | 
    
         
             
                puts opts
         
     | 
| 
       51 
60 
     | 
    
         
             
                exit
         
     | 
| 
         @@ -70,10 +79,11 @@ log_level = \ 
     | 
|
| 
       70 
79 
     | 
    
         
             
              url: options[:url],
         
     | 
| 
       71 
80 
     | 
    
         
             
              app_name: 'unleash-client-ruby-cli',
         
     | 
| 
       72 
81 
     | 
    
         
             
              disable_metrics: options[:metrics],
         
     | 
| 
      
 82 
     | 
    
         
            +
              custom_http_headers: options[:custom_http_headers],
         
     | 
| 
       73 
83 
     | 
    
         
             
              log_level: log_level
         
     | 
| 
       74 
84 
     | 
    
         
             
            )
         
     | 
| 
       75 
85 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
            context_params = ARGV. 
     | 
| 
      
 86 
     | 
    
         
            +
            context_params = ARGV.to_h{ |l| l.split("=") }.transform_keys(&:to_sym)
         
     | 
| 
       77 
87 
     | 
    
         
             
            context_properties = context_params.reject{ |k, _v| [:user_id, :session_id, :remote_address].include? k }
         
     | 
| 
       78 
88 
     | 
    
         
             
            context_params.select!{ |k, _v| [:user_id, :session_id, :remote_address].include? k }
         
     | 
| 
       79 
89 
     | 
    
         
             
            context_params.merge!(properties: context_properties) unless context_properties.nil?
         
     | 
| 
         @@ -97,12 +107,12 @@ if options[:demo] 
     | 
|
| 
       97 
107 
     | 
    
         
             
              end
         
     | 
| 
       98 
108 
     | 
    
         
             
            elsif options[:variant]
         
     | 
| 
       99 
109 
     | 
    
         
             
              variant = @unleash.get_variant(feature_name, unleash_context)
         
     | 
| 
       100 
     | 
    
         
            -
              puts " For feature  
     | 
| 
      
 110 
     | 
    
         
            +
              puts " For feature '#{feature_name}' got variant '#{variant}'"
         
     | 
| 
       101 
111 
     | 
    
         
             
            else
         
     | 
| 
       102 
112 
     | 
    
         
             
              if @unleash.is_enabled?(feature_name, unleash_context)
         
     | 
| 
       103 
     | 
    
         
            -
                puts "  
     | 
| 
      
 113 
     | 
    
         
            +
                puts " '#{feature_name}' is enabled according to unleash"
         
     | 
| 
       104 
114 
     | 
    
         
             
              else
         
     | 
| 
       105 
     | 
    
         
            -
                puts "  
     | 
| 
      
 115 
     | 
    
         
            +
                puts " '#{feature_name}' is disabled according to unleash"
         
     | 
| 
       106 
116 
     | 
    
         
             
              end
         
     | 
| 
       107 
117 
     | 
    
         
             
            end
         
     | 
| 
       108 
118 
     | 
    
         | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'unleash'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'unleash/context'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'unleash/bootstrap/configuration'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            puts ">> START bootstrap.rb"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            @unleash = Unleash::Client.new(
         
     | 
| 
      
 10 
     | 
    
         
            +
              url: 'https://unleash.herokuapp.com/api',
         
     | 
| 
      
 11 
     | 
    
         
            +
              custom_http_headers: { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' },
         
     | 
| 
      
 12 
     | 
    
         
            +
              app_name: 'bootstrap-test',
         
     | 
| 
      
 13 
     | 
    
         
            +
              instance_id: 'local-test-cli',
         
     | 
| 
      
 14 
     | 
    
         
            +
              refresh_interval: 2,
         
     | 
| 
      
 15 
     | 
    
         
            +
              disable_client: true,
         
     | 
| 
      
 16 
     | 
    
         
            +
              disable_metrics: true,
         
     | 
| 
      
 17 
     | 
    
         
            +
              metrics_interval: 2,
         
     | 
| 
      
 18 
     | 
    
         
            +
              retry_limit: 2,
         
     | 
| 
      
 19 
     | 
    
         
            +
              bootstrap_config: Unleash::Bootstrap::Configuration.new(file_path: "examples/default-toggles.json")
         
     | 
| 
      
 20 
     | 
    
         
            +
            )
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            feature_name = "featureX"
         
     | 
| 
      
 23 
     | 
    
         
            +
            unleash_context = Unleash::Context.new
         
     | 
| 
      
 24 
     | 
    
         
            +
            unleash_context.user_id = 123
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            sleep 1
         
     | 
| 
      
 27 
     | 
    
         
            +
            3.times do
         
     | 
| 
      
 28 
     | 
    
         
            +
              if @unleash.is_enabled?(feature_name, unleash_context)
         
     | 
| 
      
 29 
     | 
    
         
            +
                puts "> #{feature_name} is enabled"
         
     | 
| 
      
 30 
     | 
    
         
            +
              else
         
     | 
| 
      
 31 
     | 
    
         
            +
                puts "> #{feature_name} is not enabled"
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
              sleep 1
         
     | 
| 
      
 34 
     | 
    
         
            +
              puts "---"
         
     | 
| 
      
 35 
     | 
    
         
            +
              puts ""
         
     | 
| 
      
 36 
     | 
    
         
            +
              puts ""
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            sleep 3
         
     | 
| 
      
 40 
     | 
    
         
            +
            feature_name = "foobar"
         
     | 
| 
      
 41 
     | 
    
         
            +
            if @unleash.is_enabled?(feature_name, unleash_context, true)
         
     | 
| 
      
 42 
     | 
    
         
            +
              puts "> #{feature_name} is enabled"
         
     | 
| 
      
 43 
     | 
    
         
            +
            else
         
     | 
| 
      
 44 
     | 
    
         
            +
              puts "> #{feature_name} is not enabled"
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            puts "> shutting down client..."
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            @unleash.shutdown
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            puts ">> END bootstrap.rb"
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
              "version": 1,
         
     | 
| 
      
 3 
     | 
    
         
            +
              "features": [
         
     | 
| 
      
 4 
     | 
    
         
            +
                {
         
     | 
| 
      
 5 
     | 
    
         
            +
                  "name": "featureX",
         
     | 
| 
      
 6 
     | 
    
         
            +
                  "enabled": true,
         
     | 
| 
      
 7 
     | 
    
         
            +
                  "strategies": [
         
     | 
| 
      
 8 
     | 
    
         
            +
                    {
         
     | 
| 
      
 9 
     | 
    
         
            +
                      "name": "default"
         
     | 
| 
      
 10 
     | 
    
         
            +
                    }
         
     | 
| 
      
 11 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 12 
     | 
    
         
            +
                },
         
     | 
| 
      
 13 
     | 
    
         
            +
                {
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "name": "featureY",
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "enabled": false,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  "strategies": [
         
     | 
| 
      
 17 
     | 
    
         
            +
                    {
         
     | 
| 
      
 18 
     | 
    
         
            +
                      "name": "baz",
         
     | 
| 
      
 19 
     | 
    
         
            +
                      "parameters": {
         
     | 
| 
      
 20 
     | 
    
         
            +
                        "foo": "bar"
         
     | 
| 
      
 21 
     | 
    
         
            +
                      }
         
     | 
| 
      
 22 
     | 
    
         
            +
                    }
         
     | 
| 
      
 23 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 24 
     | 
    
         
            +
                },
         
     | 
| 
      
 25 
     | 
    
         
            +
                {
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "name": "featureZ",
         
     | 
| 
      
 27 
     | 
    
         
            +
                  "enabled": true,
         
     | 
| 
      
 28 
     | 
    
         
            +
                  "strategies": [
         
     | 
| 
      
 29 
     | 
    
         
            +
                    {
         
     | 
| 
      
 30 
     | 
    
         
            +
                      "name": "default"
         
     | 
| 
      
 31 
     | 
    
         
            +
                    },
         
     | 
| 
      
 32 
     | 
    
         
            +
                    {
         
     | 
| 
      
 33 
     | 
    
         
            +
                      "name": "hola",
         
     | 
| 
      
 34 
     | 
    
         
            +
                      "parameters": {
         
     | 
| 
      
 35 
     | 
    
         
            +
                        "name": "val"
         
     | 
| 
      
 36 
     | 
    
         
            +
                      }
         
     | 
| 
      
 37 
     | 
    
         
            +
                    }
         
     | 
| 
      
 38 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 39 
     | 
    
         
            +
                }
         
     | 
| 
      
 40 
     | 
    
         
            +
              ]
         
     | 
| 
      
 41 
     | 
    
         
            +
            }
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
    
        data/examples/simple.rb
    CHANGED
    
    | 
         @@ -6,7 +6,8 @@ require 'unleash/context' 
     | 
|
| 
       6 
6 
     | 
    
         
             
            puts ">> START simple.rb"
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            # Unleash.configure do |config|
         
     | 
| 
       9 
     | 
    
         
            -
            #   config.url = ' 
     | 
| 
      
 9 
     | 
    
         
            +
            #   config.url = 'https://unleash.herokuapp.com/api'
         
     | 
| 
      
 10 
     | 
    
         
            +
            #   config.custom_http_headers = { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' }
         
     | 
| 
       10 
11 
     | 
    
         
             
            #   config.app_name = 'simple-test'
         
     | 
| 
       11 
12 
     | 
    
         
             
            #   config.refresh_interval = 2
         
     | 
| 
       12 
13 
     | 
    
         
             
            #   config.metrics_interval = 2
         
     | 
| 
         @@ -17,13 +18,13 @@ puts ">> START simple.rb" 
     | 
|
| 
       17 
18 
     | 
    
         
             
            # or:
         
     | 
| 
       18 
19 
     | 
    
         | 
| 
       19 
20 
     | 
    
         
             
            @unleash = Unleash::Client.new(
         
     | 
| 
       20 
     | 
    
         
            -
              url: ' 
     | 
| 
      
 21 
     | 
    
         
            +
              url: 'https://unleash.herokuapp.com/api',
         
     | 
| 
      
 22 
     | 
    
         
            +
              custom_http_headers: { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' },
         
     | 
| 
       21 
23 
     | 
    
         
             
              app_name: 'simple-test',
         
     | 
| 
       22 
24 
     | 
    
         
             
              instance_id: 'local-test-cli',
         
     | 
| 
       23 
25 
     | 
    
         
             
              refresh_interval: 2,
         
     | 
| 
       24 
26 
     | 
    
         
             
              metrics_interval: 2,
         
     | 
| 
       25 
     | 
    
         
            -
              retry_limit: 2 
     | 
| 
       26 
     | 
    
         
            -
              log_level: Logger::DEBUG
         
     | 
| 
      
 27 
     | 
    
         
            +
              retry_limit: 2
         
     | 
| 
       27 
28 
     | 
    
         
             
            )
         
     | 
| 
       28 
29 
     | 
    
         | 
| 
       29 
30 
     | 
    
         
             
            # feature_name = "AwesomeFeature"
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Unleash
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Bootstrap
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Configuration
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_accessor :data, :file_path, :url, :url_headers, :block
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(opts = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                    self.file_path = resolve_value_indifferently(opts, 'file_path') || ENV['UNLEASH_BOOTSTRAP_FILE'] || nil
         
     | 
| 
      
 8 
     | 
    
         
            +
                    self.url = resolve_value_indifferently(opts, 'url') || ENV['UNLEASH_BOOTSTRAP_URL'] || nil
         
     | 
| 
      
 9 
     | 
    
         
            +
                    self.url_headers = resolve_value_indifferently(opts, 'url_headers')
         
     | 
| 
      
 10 
     | 
    
         
            +
                    self.data = resolve_value_indifferently(opts, 'data')
         
     | 
| 
      
 11 
     | 
    
         
            +
                    self.block = resolve_value_indifferently(opts, 'block')
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def valid?
         
     | 
| 
      
 15 
     | 
    
         
            +
                    ![self.data, self.file_path, self.url, self.block].all?(&:nil?)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  private
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def resolve_value_indifferently(opts, key)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    opts[key] || opts[key.to_sym]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'unleash/bootstrap/provider/from_url'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'unleash/bootstrap/provider/from_file'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Unleash
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Bootstrap
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Handler
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :configuration
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(configuration)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    self.configuration = configuration
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # @return [String] JSON string representing data returned from an Unleash server
         
     | 
| 
      
 14 
     | 
    
         
            +
                  def retrieve_toggles
         
     | 
| 
      
 15 
     | 
    
         
            +
                    return configuration.data unless self.configuration.data.nil?
         
     | 
| 
      
 16 
     | 
    
         
            +
                    return configuration.block.call if self.configuration.block.is_a?(Proc)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return Provider::FromFile.read(configuration.file_path) unless self.configuration.file_path.nil?
         
     | 
| 
      
 18 
     | 
    
         
            +
                    return Provider::FromUrl.read(configuration.url, configuration.url_headers) unless self.configuration.url.nil?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     |