statsig 1.31.1 → 1.32.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1afc6737d00e614429706bdcac895ecd61ef3f858fe152bc21c407470ec8168f
4
- data.tar.gz: cbdb4311dd2505d47ada5f78105ec0203e24edd6274161d16b67b196aa68596b
3
+ metadata.gz: dd71ed81ad0f3658f5b6b480adba4a36f560357c67d6d9869b605e2d0e1e76c0
4
+ data.tar.gz: 59b7da08c4991da0e7b33dc08865612347442970932e71b8069bcd4518d50680
5
5
  SHA512:
6
- metadata.gz: 113b3cbc430583fd2144ab7f6dc9188e3e2c87079fb188bc931c954ffe51c18c849373ed53fee2b71c87f6abb76a54f66519475eb5bfd95bf00c29cc21f2e4c0
7
- data.tar.gz: a4f7632a2af82ea4052ed1c94b9b651e7fd7b6ac88010d4d18b77038d374d2833e25443768152f54ab68f6710f48a6c5ada634199d6b4d55f7501074674d3a82
6
+ metadata.gz: 3775510747a856d9ae300ccad20a74de3cbe000bc3bca59c535c64cbab1bcb46a83069aa917299ee6dd7032f44206eecd39f3602c92f18c39a8ebbb57b45d9e9
7
+ data.tar.gz: e0a03db37029a260614a6eb8bf9edf1e2fe60f84d67fb9e027bf5fd72c3c4f969a7ab3145f55d0bd19f796b7d1733416547688e1076c69e066abd819c99b899c
data/lib/api_config.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'constants'
2
+
3
+ class UnsupportedConfigException < StandardError
4
+ end
5
+
6
+ module Statsig
7
+ class APIConfig
8
+ attr_accessor :name, :type, :is_active, :salt, :default_value, :enabled,
9
+ :rules, :id_type, :entity, :explicit_parameters, :has_shared_params, :target_app_ids
10
+
11
+ def initialize(name:, type:, is_active:, salt:, default_value:, enabled:, rules:, id_type:, entity:,
12
+ explicit_parameters: nil, has_shared_params: nil, target_app_ids: nil)
13
+ @name = name
14
+ @type = type.to_sym unless entity.nil?
15
+ @is_active = is_active
16
+ @salt = salt
17
+ @default_value = JSON.parse(JSON.generate(default_value))
18
+ @enabled = enabled
19
+ @rules = rules
20
+ @id_type = id_type
21
+ @entity = entity.to_sym unless entity.nil?
22
+ @explicit_parameters = explicit_parameters
23
+ @has_shared_params = has_shared_params
24
+ @target_app_ids = target_app_ids
25
+ end
26
+
27
+ def self.from_json(json)
28
+ new(
29
+ name: json[:name],
30
+ type: json[:type],
31
+ is_active: json[:isActive],
32
+ salt: json[:salt],
33
+ default_value: json[:defaultValue] || {},
34
+ enabled: json[:enabled],
35
+ rules: json[:rules]&.map do |rule|
36
+ APIRule.from_json(rule)
37
+ end,
38
+ id_type: json[:idType],
39
+ entity: json[:entity],
40
+ explicit_parameters: json[:explicitParameters],
41
+ has_shared_params: json[:hasSharedParams],
42
+ target_app_ids: json[:targetAppIDs]
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ module Statsig
49
+ class APIRule
50
+
51
+ attr_accessor :name, :pass_percentage, :return_value, :id, :salt,
52
+ :conditions, :id_type, :group_name, :config_delegate, :is_experiment_group
53
+
54
+ def initialize(name:, pass_percentage:, return_value:, id:, salt:, conditions:, id_type:,
55
+ group_name: nil, config_delegate: nil, is_experiment_group: nil)
56
+ @name = name
57
+ @pass_percentage = pass_percentage.to_f
58
+ @return_value = JSON.parse(JSON.generate(return_value))
59
+ @id = id
60
+ @salt = salt
61
+ @conditions = conditions
62
+ @id_type = id_type
63
+ @group_name = group_name
64
+ @config_delegate = config_delegate
65
+ @is_experiment_group = is_experiment_group
66
+ end
67
+
68
+ def self.from_json(json)
69
+ new(
70
+ name: json[:name],
71
+ pass_percentage: json[:passPercentage],
72
+ return_value: json[:returnValue] || {},
73
+ id: json[:id],
74
+ salt: json[:salt],
75
+ conditions: json[:conditions]&.map do |condition|
76
+ APICondition.from_json(condition)
77
+ end,
78
+ id_type: json[:idType],
79
+ group_name: json[:groupName],
80
+ config_delegate: json[:configDelegate],
81
+ is_experiment_group: json[:isExperimentGroup]
82
+ )
83
+ end
84
+ end
85
+ end
86
+
87
+ module Statsig
88
+ class APICondition
89
+
90
+ attr_accessor :type, :target_value, :operator, :field, :additional_values, :id_type
91
+
92
+ def initialize(type:, target_value:, operator:, field:, additional_values:, id_type:)
93
+ @type = type.to_sym unless type.nil?
94
+ @target_value = target_value
95
+ @operator = operator.to_sym unless operator.nil?
96
+ @field = field
97
+ @additional_values = additional_values || {}
98
+ @id_type = id_type
99
+ end
100
+
101
+ def self.from_json(json)
102
+ operator = json[:operator]
103
+ unless operator.nil?
104
+ operator = operator&.downcase&.to_sym
105
+ unless Const::SUPPORTED_OPERATORS.include?(operator)
106
+ raise UnsupportedConfigException
107
+ end
108
+ end
109
+
110
+ type = json[:type]
111
+ unless type.nil?
112
+ type = type&.downcase&.to_sym
113
+ unless Const::SUPPORTED_CONDITION_TYPES.include?(type)
114
+ raise UnsupportedConfigException
115
+ end
116
+ end
117
+
118
+ new(
119
+ type: json[:type],
120
+ target_value: json[:targetValue],
121
+ operator: json[:operator],
122
+ field: json[:field],
123
+ additional_values: json[:additionalValues],
124
+ id_type: json[:idType]
125
+ )
126
+ end
127
+ end
128
+ end
@@ -1,148 +1,138 @@
1
- # typed: true
2
-
3
1
  require_relative 'hash_utils'
4
- require 'sorbet-runtime'
5
2
 
3
+ require 'constants'
6
4
 
7
- module ClientInitializeHelpers
5
+ module Statsig
8
6
  class ResponseFormatter
9
- extend T::Sig
10
-
11
- def initialize(evaluator, user, hash, client_sdk_key)
12
- @evaluator = evaluator
13
- @user = user
14
- @specs = evaluator.spec_store.get_raw_specs
15
- @hash = hash
16
- @client_sdk_key = client_sdk_key
17
- end
18
-
19
- def get_responses(key)
20
- @specs[key]
21
- .map { |name, spec| to_response(name, spec) }
22
- .delete_if { |v| v.nil? }.to_h
23
- end
24
-
25
- private
26
-
27
- sig { params(secondary_exposures: T::Array[T::Hash[String, String]]).returns(T::Array[T::Hash[String, String]]) }
28
- def filter_segments_from_secondary_exposures(secondary_exposures)
29
- secondary_exposures.reject do |exposure|
30
- exposure['gate'].to_s.start_with?('segment:')
7
+ def self.get_responses(entities, evaluator, user, client_sdk_key, hash_algo, include_exposures: true)
8
+ result = {}
9
+ entities.each do |name, spec|
10
+ hashed_name, value = to_response(name, spec, evaluator, user, client_sdk_key, hash_algo, include_exposures)
11
+ if !hashed_name.nil? && !value.nil?
12
+ result[hashed_name] = value
13
+ end
31
14
  end
15
+
16
+ result
32
17
  end
33
18
 
34
- def to_response(config_name, config_spec)
35
- target_app_id = @evaluator.spec_store.get_app_id_for_sdk_key(@client_sdk_key)
36
- config_target_apps = config_spec['targetAppIDs']
19
+ def self.to_response(config_name, config_spec, evaluator, user, client_sdk_key, hash_algo, include_exposures)
20
+ target_app_id = evaluator.spec_store.get_app_id_for_sdk_key(client_sdk_key)
21
+ config_target_apps = config_spec.target_app_ids
37
22
 
38
23
  unless target_app_id.nil? || (!config_target_apps.nil? && config_target_apps.include?(target_app_id))
39
24
  return nil
40
25
  end
41
26
 
42
- eval_result = @evaluator.eval_spec(@user, config_spec)
43
- if eval_result.nil?
27
+ category = config_spec.type
28
+ entity_type = config_spec.entity
29
+ if entity_type == :segment || entity_type == :holdout
44
30
  return nil
45
31
  end
46
32
 
47
- category = config_spec['type']
48
- entity_type = config_spec['entity']
33
+ eval_result = ConfigResult.new(
34
+ name: config_name,
35
+ disable_evaluation_details: true,
36
+ disable_exposures: !include_exposures
37
+ )
38
+ evaluator.eval_spec(user, config_spec, eval_result)
49
39
 
50
40
  result = {}
51
41
 
52
- case category
53
-
54
- when 'feature_gate'
55
- if entity_type == 'segment' || entity_type == 'holdout'
56
- return nil
57
- end
42
+ result[:id_type] = eval_result.id_type
43
+ unless eval_result.group_name.nil?
44
+ result[:group_name] = eval_result.group_name
45
+ end
58
46
 
59
- result['value'] = eval_result.gate_value
60
- result["group_name"] = eval_result.group_name
61
- result["id_type"] = eval_result.id_type
62
- when 'dynamic_config'
63
- id_type = config_spec['idType']
64
- result['value'] = eval_result.json_value
65
- result["group"] = eval_result.rule_id
66
- result["group_name"] = eval_result.group_name
67
- result["id_type"] = eval_result.id_type
68
- result["is_device_based"] = id_type.is_a?(String) && id_type.downcase == 'stableid'
47
+ case category
48
+ when :feature_gate
49
+ result[:value] = eval_result.gate_value
50
+ when :dynamic_config
51
+ id_type = config_spec.id_type
52
+ result[:value] = eval_result.json_value
53
+ result[:group] = eval_result.rule_id
54
+ result[:is_device_based] = id_type.is_a?(String) && id_type.downcase == Statsig::Const::STABLEID
69
55
  else
70
56
  return nil
71
57
  end
72
58
 
73
- if entity_type == 'experiment'
74
- populate_experiment_fields(config_name, config_spec, eval_result, result)
59
+ if entity_type == :experiment
60
+ populate_experiment_fields(name, config_spec, eval_result, result, evaluator)
75
61
  end
76
62
 
77
- if entity_type == 'layer'
78
- populate_layer_fields(config_spec, eval_result, result)
79
- result.delete('id_type') # not exposed for layer configs in /initialize
63
+ if entity_type == :layer
64
+ populate_layer_fields(config_spec, eval_result, result, evaluator, hash_algo, include_exposures)
65
+ result.delete(:id_type) # not exposed for layer configs in /initialize
80
66
  end
81
67
 
82
- hashed_name = hash_name(config_name)
83
- [hashed_name, result.merge(
84
- {
85
- "name" => hashed_name,
86
- "rule_id" => eval_result.rule_id,
87
- "secondary_exposures" => clean_exposures(eval_result.secondary_exposures)
88
- }).compact]
68
+ hashed_name = hash_name(config_name, hash_algo)
69
+
70
+ result[:name] = hashed_name
71
+ result[:rule_id] = eval_result.rule_id
72
+
73
+ if include_exposures
74
+ result[:secondary_exposures] = clean_exposures(eval_result.secondary_exposures)
75
+ end
76
+
77
+ [hashed_name, result]
89
78
  end
90
79
 
91
- def clean_exposures(exposures)
80
+ def self.clean_exposures(exposures)
92
81
  seen = {}
93
82
  exposures.reject do |exposure|
94
- key = "#{exposure["gate"]}|#{exposure["gateValue"]}|#{exposure["ruleID"]}}"
83
+ key = "#{exposure[:gate]}|#{exposure[:gateValue]}|#{exposure[:ruleID]}}"
95
84
  should_reject = seen[key]
96
85
  seen[key] = true
97
86
  should_reject == true
98
87
  end
99
88
  end
100
89
 
101
- def populate_experiment_fields(config_name, config_spec, eval_result, result)
102
- result["is_user_in_experiment"] = eval_result.is_experiment_group
103
- result["is_experiment_active"] = config_spec['isActive'] == true
90
+ def self.populate_experiment_fields(config_name, config_spec, eval_result, result, evaluator)
91
+ result[:is_user_in_experiment] = eval_result.is_experiment_group
92
+ result[:is_experiment_active] = config_spec.is_active == true
104
93
 
105
- if config_spec['hasSharedParams'] != true
94
+ if config_spec.has_shared_params != true
106
95
  return
107
96
  end
108
97
 
109
- result["is_in_layer"] = true
110
- result["explicit_parameters"] = config_spec["explicitParameters"] || []
98
+ result[:is_in_layer] = true
99
+ result[:explicit_parameters] = config_spec.explicit_parameters || []
111
100
 
112
- layer_name = @specs[:experiment_to_layer][config_name]
113
- if layer_name.nil? || @specs[:layers][layer_name].nil?
101
+ layer_name = evaluator.spec_store.experiment_to_layer[config_name]
102
+ if layer_name.nil? || evaluator.spec_store.layers[layer_name].nil?
114
103
  return
115
104
  end
116
105
 
117
- layer = @specs[:layers][layer_name]
118
- result["value"] = layer["defaultValue"].merge(result["value"])
106
+ layer = evaluator.spec_store.layers[layer_name]
107
+ result[:value] = layer[:defaultValue].merge(result[:value])
119
108
  end
120
109
 
121
- def populate_layer_fields(config_spec, eval_result, result)
110
+ def self.populate_layer_fields(config_spec, eval_result, result, evaluator, hash_algo, include_exposures)
122
111
  delegate = eval_result.config_delegate
123
- result["explicit_parameters"] = config_spec["explicitParameters"] || []
112
+ result[:explicit_parameters] = config_spec.explicit_parameters || []
124
113
 
125
114
  if delegate.nil? == false && delegate.empty? == false
126
- delegate_spec = @specs[:configs][delegate]
127
- delegate_result = @evaluator.eval_spec(@user, delegate_spec)
115
+ delegate_spec = evaluator.spec_store.configs[delegate]
128
116
 
129
- result["allocated_experiment_name"] = hash_name(delegate)
130
- result["is_user_in_experiment"] = delegate_result.is_experiment_group
131
- result["is_experiment_active"] = delegate_spec['isActive'] == true
132
- result["explicit_parameters"] = delegate_spec["explicitParameters"] || []
117
+ result[:allocated_experiment_name] = hash_name(delegate, hash_algo)
118
+ result[:is_user_in_experiment] = eval_result.is_experiment_group
119
+ result[:is_experiment_active] = delegate_spec.is_active == true
120
+ result[:explicit_parameters] = delegate_spec.explicit_parameters || []
133
121
  end
134
122
 
135
- result["undelegated_secondary_exposures"] = clean_exposures(eval_result.undelegated_sec_exps || [])
123
+ if include_exposures
124
+ result[:undelegated_secondary_exposures] = clean_exposures(eval_result.undelegated_sec_exps || [])
125
+ end
136
126
  end
137
127
 
138
- def hash_name(name)
139
- case @hash
140
- when 'none'
128
+ def self.hash_name(name, hash_algo)
129
+ case hash_algo
130
+ when Statsig::Const::NONE
141
131
  return name
142
- when 'sha256'
143
- return Statsig::HashUtils.sha256(name)
144
- when 'djb2'
132
+ when Statsig::Const::DJB2
145
133
  return Statsig::HashUtils.djb2(name)
134
+ else
135
+ return Statsig::HashUtils.sha256(name)
146
136
  end
147
137
  end
148
138
  end
data/lib/config_result.rb CHANGED
@@ -1,10 +1,5 @@
1
- # typed: true
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  module Statsig
6
2
  class ConfigResult
7
- extend T::Sig
8
3
 
9
4
  attr_accessor :name
10
5
  attr_accessor :gate_value
@@ -19,25 +14,30 @@ module Statsig
19
14
  attr_accessor :group_name
20
15
  attr_accessor :id_type
21
16
  attr_accessor :target_app_ids
17
+ attr_accessor :disable_evaluation_details
18
+ attr_accessor :disable_exposures
22
19
 
23
20
  def initialize(
24
- name,
25
- gate_value = false,
26
- json_value = {},
27
- rule_id = '',
28
- secondary_exposures = [],
29
- config_delegate = nil,
30
- explicit_parameters = [],
21
+ name:,
22
+ gate_value: false,
23
+ json_value: nil,
24
+ rule_id: nil,
25
+ secondary_exposures: [],
26
+ config_delegate: nil,
27
+ explicit_parameters: nil,
31
28
  is_experiment_group: false,
32
29
  evaluation_details: nil,
33
30
  group_name: nil,
34
- id_type: '',
35
- target_app_ids: nil)
31
+ id_type: nil,
32
+ target_app_ids: nil,
33
+ disable_evaluation_details: false,
34
+ disable_exposures: false
35
+ )
36
36
  @name = name
37
37
  @gate_value = gate_value
38
38
  @json_value = json_value
39
39
  @rule_id = rule_id
40
- @secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
40
+ @secondary_exposures = secondary_exposures
41
41
  @undelegated_sec_exps = @secondary_exposures
42
42
  @config_delegate = config_delegate
43
43
  @explicit_parameters = explicit_parameters
@@ -46,9 +46,10 @@ module Statsig
46
46
  @group_name = group_name
47
47
  @id_type = id_type
48
48
  @target_app_ids = target_app_ids
49
+ @disable_evaluation_details = disable_evaluation_details
50
+ @disable_exposures = disable_exposures
49
51
  end
50
52
 
51
- sig { params(config_name: String, user_persisted_values: UserPersistedValues).returns(T.nilable(ConfigResult)) }
52
53
  def self.from_user_persisted_values(config_name, user_persisted_values)
53
54
  sticky_values = user_persisted_values[config_name]
54
55
  return nil if sticky_values.nil?
@@ -56,22 +57,6 @@ module Statsig
56
57
  from_hash(config_name, sticky_values)
57
58
  end
58
59
 
59
- sig { params(config_name: String, hash: Hash).returns(ConfigResult) }
60
- def self.from_hash(config_name, hash)
61
- new(
62
- config_name,
63
- hash['gate_value'],
64
- hash['json_value'],
65
- hash['rule_id'],
66
- hash['secondary_exposures'],
67
- evaluation_details: EvaluationDetails.persisted(hash['config_sync_time'], hash['init_time']),
68
- group_name: hash['group_name'],
69
- id_type: hash['id_type'],
70
- target_app_ids: hash['target_app_ids']
71
- )
72
- end
73
-
74
- sig { returns(Hash) }
75
60
  def to_hash
76
61
  {
77
62
  json_value: @json_value,
data/lib/constants.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Statsig
2
+ module Const
3
+ EMPTY_STR = ''.freeze
4
+
5
+ SUPPORTED_CONDITION_TYPES = Set.new(%i[
6
+ public fail_gate pass_gate ip_based ua_based user_field
7
+ environment_field current_time user_bucket unit_id
8
+ ]).freeze
9
+
10
+ SUPPORTED_OPERATORS = Set.new(%i[
11
+ gt gte lt lte version_gt version_gte version_lt version_lte
12
+ version_eq version_neq any none any_case_sensitive none_case_sensitive
13
+ str_starts_with_any str_ends_with_any str_contains_any str_contains_none
14
+ str_matches eq neq before after on in_segment_list not_in_segment_list
15
+ ]).freeze
16
+
17
+ APP_VERSION = 'app_version'.freeze
18
+ APPVERSION = 'appversion'.freeze
19
+ BROWSER_NAME = 'browser_name'.freeze
20
+ BROWSER_VERSION = 'browser_version'.freeze
21
+ BROWSERNAME = 'browsername'.freeze
22
+ BROWSERVERSION = 'browserversion'.freeze
23
+ CML_SHA_256 = 'sha256'.freeze
24
+ CML_USER_ID = 'userID'.freeze
25
+ COUNTRY = 'country'.freeze
26
+ DEFAULT = 'default'.freeze
27
+ DISABLED = 'disabled'.freeze
28
+ DJB2 = 'djb2'.freeze
29
+ EMAIL = 'email'.freeze
30
+ FALSE = 'false'.freeze
31
+ IP = 'ip'.freeze
32
+ LAYER = :layer
33
+ LOCALE = 'locale'.freeze
34
+ NONE = 'none'.freeze
35
+ OS_NAME = 'os_name'.freeze
36
+ OS_VERSION = 'os_version'.freeze
37
+ OSNAME = 'osname'.freeze
38
+ OSVERSION = 'osversion'.freeze
39
+ OVERRIDE = 'override'.freeze
40
+ Q_RIGHT_CHEVRON = 'Q>'.freeze
41
+ STABLEID = 'stableid'.freeze
42
+ STATSIG_RUBY_SDK = 'statsig-ruby-sdk'.freeze
43
+ TRUE = 'true'.freeze
44
+ USER_AGENT = 'user_agent'.freeze
45
+ USER_ID = 'user_id'.freeze
46
+ USERAGENT = 'useragent'.freeze
47
+ USERID = 'userid'.freeze
48
+
49
+ # Persisted Evaluations
50
+ GATE_VALUE = 'gate_value'.freeze
51
+ JSON_VALUE = 'json_value'.freeze
52
+ RULE_ID = 'rule_id'.freeze
53
+ SECONDARY_EXPOSURES = 'secondary_exposures'.freeze
54
+ GROUP_NAME = 'group_name'.freeze
55
+ ID_TYPE = 'id_type'.freeze
56
+ TARGET_APP_IDS = 'target_app_ids'.freeze
57
+ CONFIG_SYNC_TIME = 'config_sync_time'.freeze
58
+ INIT_TIME = 'init_time'.freeze
59
+ end
60
+ end
data/lib/diagnostics.rb CHANGED
@@ -1,15 +1,7 @@
1
- # typed: true
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  module Statsig
6
2
  class Diagnostics
7
- extend T::Sig
8
-
9
- sig { returns(T::Hash[String, T::Array[T::Hash[Symbol, T.untyped]]]) }
10
3
  attr_reader :markers
11
4
 
12
- sig { returns(T::Hash[String, Numeric]) }
13
5
  attr_accessor :sample_rates
14
6
 
15
7
  def initialize()
@@ -17,16 +9,6 @@ module Statsig
17
9
  @sample_rates = {}
18
10
  end
19
11
 
20
- sig do
21
- params(
22
- key: String,
23
- action: String,
24
- step: T.any(String, NilClass),
25
- tags: T::Hash[Symbol, T.untyped],
26
- context: String
27
- ).void
28
- end
29
-
30
12
  def mark(key, action, step, tags, context)
31
13
  marker = {
32
14
  key: key,
@@ -47,14 +29,6 @@ module Statsig
47
29
  @markers[context].push(marker)
48
30
  end
49
31
 
50
- sig do
51
- params(
52
- context: String,
53
- key: String,
54
- step: T.any(String, NilClass),
55
- tags: T::Hash[Symbol, T.untyped]
56
- ).returns(Tracker)
57
- end
58
32
  def track(context, key, step = nil, tags = {})
59
33
  tracker = Tracker.new(self, context, key, step, tags)
60
34
  tracker.start(**tags)
@@ -87,17 +61,6 @@ module Statsig
87
61
  API_CALL_KEYS = %w[check_gate get_config get_experiment get_layer].freeze
88
62
 
89
63
  class Tracker
90
- extend T::Sig
91
-
92
- sig do
93
- params(
94
- diagnostics: Diagnostics,
95
- context: String,
96
- key: String,
97
- step: T.any(String, NilClass),
98
- tags: T::Hash[Symbol, T.untyped]
99
- ).void
100
- end
101
64
  def initialize(diagnostics, context, key, step, tags = {})
102
65
  @diagnostics = diagnostics
103
66
  @context = context
@@ -115,4 +78,4 @@ module Statsig
115
78
  end
116
79
  end
117
80
  end
118
- end
81
+ end
@@ -1,7 +1,3 @@
1
- # typed: false
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  ##
6
2
  # Contains the current experiment/dynamic config values from Statsig
7
3
  #
@@ -9,46 +5,28 @@ require 'sorbet-runtime'
9
5
  #
10
6
  # Experiments Documentation: https://docs.statsig.com/experiments-plus
11
7
  class DynamicConfig
12
- extend T::Sig
13
8
 
14
- sig { returns(String) }
15
9
  attr_accessor :name
16
10
 
17
- sig { returns(T::Hash[String, T.untyped]) }
18
11
  attr_accessor :value
19
12
 
20
- sig { returns(String) }
21
13
  attr_accessor :rule_id
22
14
 
23
- sig { returns(T.nilable(String)) }
24
15
  attr_accessor :group_name
25
16
 
26
- sig { returns(String) }
27
17
  attr_accessor :id_type
28
18
 
29
- sig { returns(T.nilable(Statsig::EvaluationDetails)) }
30
19
  attr_accessor :evaluation_details
31
20
 
32
- sig do
33
- params(
34
- name: String,
35
- value: T::Hash[String, T.untyped],
36
- rule_id: String,
37
- group_name: T.nilable(String),
38
- id_type: String,
39
- evaluation_details: T.nilable(Statsig::EvaluationDetails)
40
- ).void
41
- end
42
21
  def initialize(name, value = {}, rule_id = '', group_name = nil, id_type = '', evaluation_details = nil)
43
22
  @name = name
44
- @value = value
23
+ @value = value || {}
45
24
  @rule_id = rule_id
46
25
  @group_name = group_name
47
26
  @id_type = id_type
48
27
  @evaluation_details = evaluation_details
49
28
  end
50
29
 
51
- sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
52
30
  ##
53
31
  # Get the value for the given key (index), falling back to the default_value if it cannot be found.
54
32
  #
@@ -59,7 +37,6 @@ class DynamicConfig
59
37
  @value[index]
60
38
  end
61
39
 
62
- sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
63
40
  ##
64
41
  # Get the value for the given key (index), falling back to the default_value if it cannot be found
65
42
  # or is found to have a different type from the default_value.
@@ -1,15 +1,10 @@
1
- # typed: true
2
-
3
1
  require 'statsig_errors'
4
- require 'sorbet-runtime'
5
2
 
6
3
  $endpoint = 'https://statsigapi.net/v1/sdk_exception'
7
4
 
8
5
  module Statsig
9
6
  class ErrorBoundary
10
- extend T::Sig
11
7
 
12
- sig { params(sdk_key: String).void }
13
8
  def initialize(sdk_key)
14
9
  @sdk_key = sdk_key
15
10
  @seen = Set.new
@@ -1,4 +1,3 @@
1
- # typed: true
2
1
  module Statsig
3
2
 
4
3
  module EvaluationReason
@@ -25,6 +24,10 @@ module Statsig
25
24
  @server_time = (Time.now.to_i * 1000).to_s
26
25
  end
27
26
 
27
+ def self.unsupported(config_sync_time, init_time)
28
+ EvaluationDetails.new(config_sync_time, init_time, EvaluationReason::UNSUPPORTED)
29
+ end
30
+
28
31
  def self.unrecognized(config_sync_time, init_time)
29
32
  EvaluationDetails.new(config_sync_time, init_time, EvaluationReason::UNRECOGNIZED)
30
33
  end