statsig 2.1.0 → 2.2.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/lib/spec_store.rb +63 -40
- data/lib/statsig.rb +12 -5
- data/lib/statsig_driver.rb +4 -0
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3870c3be99d3f7295c21311a6e938599bbd0ebd260d4b2c2cb814b48d22bd7f4
         | 
| 4 | 
            +
              data.tar.gz: 618400ee2baa54a9e4bc18ee7e6e1a1cb017349a513919dd5f5e6caa380fb154
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 47fdd678ef31a026f84bf81a184b39fc171c4d35cb7fb9229ecc2ec835051645cd9ed7b4f6ec2c0bfb92c2b59fe6cd9e742afaff176b438192f6d7b8d07f7ceb
         | 
| 7 | 
            +
              data.tar.gz: 6db827709c35c1005a22dbf07e81f3ee8d6f316c89ba760beea2ba4adf2a35ae1d5ad2ea97604411733f61e4d167a6ebcbfa1e0dcef05986aacde37aab76dcf0
         | 
    
        data/lib/spec_store.rb
    CHANGED
    
    | @@ -44,6 +44,8 @@ module Statsig | |
| 44 44 | 
             
                  @secret_key = secret_key
         | 
| 45 45 | 
             
                  @unsupported_configs = Set.new
         | 
| 46 46 |  | 
| 47 | 
            +
                  startTime = (Time.now.to_f * 1000).to_i
         | 
| 48 | 
            +
             | 
| 47 49 | 
             
                  @id_list_thread_pool = Concurrent::FixedThreadPool.new(
         | 
| 48 50 | 
             
                    options.idlist_threadpool_size,
         | 
| 49 51 | 
             
                    name: 'statsig-idlist',
         | 
| @@ -57,7 +59,7 @@ module Statsig | |
| 57 59 | 
             
                    else
         | 
| 58 60 | 
             
                      tracker = @diagnostics.track('initialize','bootstrap', 'process')
         | 
| 59 61 | 
             
                      begin
         | 
| 60 | 
            -
                        if process_specs(options.bootstrap_values)
         | 
| 62 | 
            +
                        if process_specs(options.bootstrap_values).nil?
         | 
| 61 63 | 
             
                          @init_reason = EvaluationReason::BOOTSTRAP
         | 
| 62 64 | 
             
                        end
         | 
| 63 65 | 
             
                      rescue StandardError
         | 
| @@ -68,13 +70,15 @@ module Statsig | |
| 68 70 | 
             
                    end
         | 
| 69 71 | 
             
                  end
         | 
| 70 72 |  | 
| 73 | 
            +
                  failure_details = nil
         | 
| 74 | 
            +
             | 
| 71 75 | 
             
                  unless @options.data_store.nil?
         | 
| 72 76 | 
             
                    @options.data_store.init
         | 
| 73 | 
            -
                    load_config_specs_from_storage_adapter('initialize')
         | 
| 77 | 
            +
                    failure_details = load_config_specs_from_storage_adapter('initialize')
         | 
| 74 78 | 
             
                  end
         | 
| 75 79 |  | 
| 76 80 | 
             
                  if @init_reason == EvaluationReason::UNINITIALIZED
         | 
| 77 | 
            -
                    download_config_specs('initialize')
         | 
| 81 | 
            +
                    failure_details = download_config_specs('initialize')
         | 
| 78 82 | 
             
                  end
         | 
| 79 83 |  | 
| 80 84 | 
             
                  @initial_config_sync_time = @last_config_sync_time == 0 ? -1 : @last_config_sync_time
         | 
| @@ -86,12 +90,18 @@ module Statsig | |
| 86 90 |  | 
| 87 91 | 
             
                  @config_sync_thread = spawn_sync_config_specs_thread
         | 
| 88 92 | 
             
                  @id_lists_sync_thread = spawn_sync_id_lists_thread
         | 
| 93 | 
            +
                  endTime = (Time.now.to_f * 1000).to_i
         | 
| 94 | 
            +
                  @initialization_details = {duration: endTime - startTime, isSDKReady: true, configSpecReady: @init_reason != EvaluationReason::UNINITIALIZED, failureDetails: failure_details}
         | 
| 89 95 | 
             
                end
         | 
| 90 96 |  | 
| 91 97 | 
             
                def is_ready_for_checks
         | 
| 92 98 | 
             
                  @last_config_sync_time != 0
         | 
| 93 99 | 
             
                end
         | 
| 94 100 |  | 
| 101 | 
            +
                def get_initialization_details
         | 
| 102 | 
            +
                  @initialization_details
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 95 105 | 
             
                def shutdown
         | 
| 96 106 | 
             
                  @config_sync_thread&.exit
         | 
| 97 107 | 
             
                  @id_lists_sync_thread&.exit
         | 
| @@ -202,13 +212,19 @@ module Statsig | |
| 202 212 | 
             
                  return if cached_values.nil?
         | 
| 203 213 |  | 
| 204 214 | 
             
                  tracker = @diagnostics.track(context, 'data_store_config_specs', 'process')
         | 
| 205 | 
            -
                  process_specs(cached_values, from_adapter: true)
         | 
| 206 | 
            -
                   | 
| 207 | 
            -
             | 
| 215 | 
            +
                  failure_details = process_specs(cached_values, from_adapter: true)
         | 
| 216 | 
            +
                  if failure_details.nil?
         | 
| 217 | 
            +
                    @init_reason = EvaluationReason::DATA_ADAPTER
         | 
| 218 | 
            +
                    tracker.end(success: true)
         | 
| 219 | 
            +
                  else
         | 
| 220 | 
            +
                    tracker.end(success: false)
         | 
| 221 | 
            +
                    return download_config_specs(context)
         | 
| 222 | 
            +
                  end
         | 
| 223 | 
            +
                  return failure_details
         | 
| 208 224 | 
             
                rescue StandardError
         | 
| 209 225 | 
             
                  # Fallback to network
         | 
| 210 226 | 
             
                  tracker.end(success: false)
         | 
| 211 | 
            -
                  download_config_specs(context)
         | 
| 227 | 
            +
                  return download_config_specs(context)
         | 
| 212 228 | 
             
                end
         | 
| 213 229 |  | 
| 214 230 | 
             
                def save_rulesets_to_storage_adapter(rulesets_string)
         | 
| @@ -253,18 +269,21 @@ module Statsig | |
| 253 269 | 
             
                  tracker = @diagnostics.track(context, 'download_config_specs', 'network_request')
         | 
| 254 270 |  | 
| 255 271 | 
             
                  error = nil
         | 
| 272 | 
            +
                  failure_details = nil
         | 
| 256 273 | 
             
                  begin
         | 
| 257 274 | 
             
                    response, e = @network.download_config_specs(@last_config_sync_time)
         | 
| 258 275 | 
             
                    code = response&.status.to_i
         | 
| 259 276 | 
             
                    if e.is_a? NetworkError
         | 
| 260 277 | 
             
                      code = e.http_code
         | 
| 278 | 
            +
                      failure_details = {statusCode: code, exception: e, reason: "CONFIG_SPECS_NETWORK_ERROR"}
         | 
| 261 279 | 
             
                    end
         | 
| 262 280 | 
             
                    tracker.end(statusCode: code, success: e.nil?, sdkRegion: response&.headers&.[]('X-Statsig-Region'))
         | 
| 263 281 |  | 
| 264 282 | 
             
                    if e.nil?
         | 
| 265 283 | 
             
                      unless response.nil?
         | 
| 266 284 | 
             
                        tracker = @diagnostics.track(context, 'download_config_specs', 'process')
         | 
| 267 | 
            -
                         | 
| 285 | 
            +
                        failure_details = process_specs(response.body.to_s)
         | 
| 286 | 
            +
                        if failure_details.nil?
         | 
| 268 287 | 
             
                          @init_reason = EvaluationReason::NETWORK
         | 
| 269 288 | 
             
                        end
         | 
| 270 289 | 
             
                        tracker.end(success: @init_reason == EvaluationReason::NETWORK)
         | 
| @@ -274,59 +293,63 @@ module Statsig | |
| 274 293 | 
             
                                                       @last_config_sync_time)
         | 
| 275 294 | 
             
                        end
         | 
| 276 295 | 
             
                      end
         | 
| 277 | 
            -
             | 
| 278 | 
            -
                      nil
         | 
| 279 296 | 
             
                    else
         | 
| 280 297 | 
             
                      error = e
         | 
| 281 298 | 
             
                    end
         | 
| 282 299 | 
             
                  rescue StandardError => e
         | 
| 300 | 
            +
                    failure_details = {exception: e, reason: "INTERNAL_ERROR"}
         | 
| 283 301 | 
             
                    error = e
         | 
| 284 302 | 
             
                  end
         | 
| 285 303 |  | 
| 286 304 | 
             
                  @error_callback.call(error) unless error.nil? or @error_callback.nil?
         | 
| 305 | 
            +
                  return failure_details
         | 
| 287 306 | 
             
                end
         | 
| 288 307 |  | 
| 289 308 | 
             
                def process_specs(specs_string, from_adapter: false)
         | 
| 290 309 | 
             
                  if specs_string.nil?
         | 
| 291 | 
            -
                    return  | 
| 310 | 
            +
                    return {reason: "EMPTY_SPEC"}
         | 
| 292 311 | 
             
                  end
         | 
| 293 312 |  | 
| 294 | 
            -
                   | 
| 295 | 
            -
             | 
| 313 | 
            +
                  begin
         | 
| 314 | 
            +
                    specs_json = JSON.parse(specs_string, { symbolize_names: true })
         | 
| 315 | 
            +
                    return {reason: "PARSE_RESPONSE_ERROR"} unless specs_json.is_a? Hash
         | 
| 296 316 |  | 
| 297 | 
            -
             | 
| 298 | 
            -
             | 
| 299 | 
            -
             | 
| 300 | 
            -
             | 
| 301 | 
            -
             | 
| 317 | 
            +
                    hashed_sdk_key_used = specs_json[:hashed_sdk_key_used]
         | 
| 318 | 
            +
                    unless hashed_sdk_key_used.nil? or hashed_sdk_key_used == Statsig::HashUtils.djb2(@secret_key)
         | 
| 319 | 
            +
                      err_boundary.log_exception(Statsig::InvalidSDKKeyResponse.new)
         | 
| 320 | 
            +
                      return {reason: "PARSE_RESPONSE_ERROR"}
         | 
| 321 | 
            +
                    end
         | 
| 302 322 |  | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 308 | 
            -
             | 
| 309 | 
            -
             | 
| 310 | 
            -
             | 
| 311 | 
            -
             | 
| 323 | 
            +
                    new_specs_sync_time = specs_json[:time]
         | 
| 324 | 
            +
                    if new_specs_sync_time.nil? \
         | 
| 325 | 
            +
                      || new_specs_sync_time < @last_config_sync_time \
         | 
| 326 | 
            +
                      || specs_json[:has_updates] != true \
         | 
| 327 | 
            +
                      || specs_json[:feature_gates].nil? \
         | 
| 328 | 
            +
                      || specs_json[:dynamic_configs].nil? \
         | 
| 329 | 
            +
                      || specs_json[:layer_configs].nil?
         | 
| 330 | 
            +
                      return {reason: "PARSE_RESPONSE_ERROR"}
         | 
| 331 | 
            +
                    end
         | 
| 312 332 |  | 
| 313 | 
            -
             | 
| 314 | 
            -
             | 
| 333 | 
            +
                    @last_config_sync_time = new_specs_sync_time
         | 
| 334 | 
            +
                    @unsupported_configs.clear
         | 
| 315 335 |  | 
| 316 | 
            -
             | 
| 336 | 
            +
                    specs_json[:diagnostics]&.each { |key, value| @diagnostics.sample_rates[key.to_s] = value }
         | 
| 317 337 |  | 
| 318 | 
            -
             | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
             | 
| 338 | 
            +
                    @gates = specs_json[:feature_gates]
         | 
| 339 | 
            +
                    @configs = specs_json[:dynamic_configs]
         | 
| 340 | 
            +
                    @layers = specs_json[:layer_configs]
         | 
| 341 | 
            +
                    @condition_map = specs_json[:condition_map]
         | 
| 342 | 
            +
                    @experiment_to_layer = specs_json[:experiment_to_layer]
         | 
| 343 | 
            +
                    @sdk_keys_to_app_ids = specs_json[:sdk_keys_to_app_ids] || {}
         | 
| 344 | 
            +
                    @hashed_sdk_keys_to_app_ids = specs_json[:hashed_sdk_keys_to_app_ids] || {}
         | 
| 325 345 |  | 
| 326 | 
            -
             | 
| 327 | 
            -
             | 
| 346 | 
            +
                    unless from_adapter
         | 
| 347 | 
            +
                      save_rulesets_to_storage_adapter(specs_string)
         | 
| 348 | 
            +
                    end
         | 
| 349 | 
            +
                  rescue StandardError => e
         | 
| 350 | 
            +
                    return {reason: "PARSE_RESPONSE_ERROR"}
         | 
| 328 351 | 
             
                  end
         | 
| 329 | 
            -
                   | 
| 352 | 
            +
                  nil
         | 
| 330 353 | 
             
                end
         | 
| 331 354 |  | 
| 332 355 | 
             
                def get_id_lists_from_adapter(context)
         | 
    
        data/lib/statsig.rb
    CHANGED
    
    | @@ -20,6 +20,13 @@ module Statsig | |
| 20 20 | 
             
                @shared_instance = StatsigDriver.new(secret_key, options, error_callback)
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 | 
            +
              def self.get_initialization_details
         | 
| 24 | 
            +
                if not defined? @shared_instance or @shared_instance.nil?
         | 
| 25 | 
            +
                  return {duration: 0, isSDKReady: false, configSpecReady: false, failure_details: {exception: Statsig::UninitializedError.new, reason: 'INTERNAL_ERROR'}}
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                @shared_instance.get_initialization_details
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 23 30 | 
             
              class GetGateOptions
         | 
| 24 31 | 
             
                attr_accessor :disable_log_exposure, :skip_evaluation, :disable_evaluation_details
         | 
| 25 32 |  | 
| @@ -65,7 +72,7 @@ module Statsig | |
| 65 72 | 
             
              end
         | 
| 66 73 |  | 
| 67 74 | 
             
              ##
         | 
| 68 | 
            -
              # @deprecated - use check_gate(user, gate, options)  | 
| 75 | 
            +
              # @deprecated - use check_gate(user, gate, options) with CheckGateOptions.new(disable_log_exposure: true) as options
         | 
| 69 76 | 
             
              # Gets the boolean result of a gate, evaluated against the given user.
         | 
| 70 77 | 
             
              #
         | 
| 71 78 | 
             
              # @param user A StatsigUser object used for the evaluation
         | 
| @@ -108,7 +115,7 @@ module Statsig | |
| 108 115 | 
             
              end
         | 
| 109 116 |  | 
| 110 117 | 
             
              ##
         | 
| 111 | 
            -
              # @deprecated - use get_config(user, config, options)  | 
| 118 | 
            +
              # @deprecated - use get_config(user, config, options) with GetConfigOptions.new(disable_log_exposure: true) as options
         | 
| 112 119 | 
             
              # Get the values of a dynamic config, evaluated against the given user.
         | 
| 113 120 | 
             
              #
         | 
| 114 121 | 
             
              # @param [StatsigUser] user A StatsigUser object used for the evaluation
         | 
| @@ -152,7 +159,7 @@ module Statsig | |
| 152 159 | 
             
              end
         | 
| 153 160 |  | 
| 154 161 | 
             
              ##
         | 
| 155 | 
            -
              # @deprecated - use get_experiment(user, experiment, options)  | 
| 162 | 
            +
              # @deprecated - use get_experiment(user, experiment, options) with GetExperimentOptions.new(disable_log_exposure: true) as options
         | 
| 156 163 | 
             
              # Get the values of an experiment, evaluated against the given user.
         | 
| 157 164 | 
             
              #
         | 
| 158 165 | 
             
              # @param [StatsigUser] user A StatsigUser object used for the evaluation
         | 
| @@ -199,7 +206,7 @@ module Statsig | |
| 199 206 | 
             
              end
         | 
| 200 207 |  | 
| 201 208 | 
             
              ##
         | 
| 202 | 
            -
              # @deprecated - use get_layer(user, gate, options)  | 
| 209 | 
            +
              # @deprecated - use get_layer(user, gate, options) with GetLayerOptions.new(disable_log_exposure: true) as options
         | 
| 203 210 | 
             
              # Get the values of a layer, evaluated against the given user.
         | 
| 204 211 | 
             
              #
         | 
| 205 212 | 
             
              # @param user A StatsigUser object used for the evaluation
         | 
| @@ -363,7 +370,7 @@ module Statsig | |
| 363 370 | 
             
              def self.get_statsig_metadata
         | 
| 364 371 | 
             
                {
         | 
| 365 372 | 
             
                  'sdkType' => 'ruby-server',
         | 
| 366 | 
            -
                  'sdkVersion' => '2. | 
| 373 | 
            +
                  'sdkVersion' => '2.2.0',
         | 
| 367 374 | 
             
                  'languageVersion' => RUBY_VERSION
         | 
| 368 375 | 
             
                }
         | 
| 369 376 | 
             
              end
         | 
    
        data/lib/statsig_driver.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: statsig
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Statsig, Inc
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-10-11 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         |