statsig 0.1.0 → 0.1.5

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: 60035280630edb2172623545efa323cc6b3ec0374b8ebc99ba7f28312a227b21
4
- data.tar.gz: af799a2649633d8cc364d6e69fed72f3a30ff96673212d348f20b5fa0ef83f30
3
+ metadata.gz: dfe488b916b3d18b4f227e9783251537b8197ba6e449521bec56489a1c871988
4
+ data.tar.gz: 49bda9345b2436a8715997b3a57330f10ceb304448106a38519c6d5a8f2ffe01
5
5
  SHA512:
6
- metadata.gz: f69cf4534af68927136ec9d9bf1900ea206e3c86c7bdcdd294e03c0109071449adc44db0d5026f98cae2ff281985022e19ddb6e9a8cc9a275c0f53513c292e0a
7
- data.tar.gz: 39269a606a0291a83c5f486535b7a075ccde3a82a50436598aed93732cd95b27c765220c56de6b50e1e515f7346ec107081dc71ea23be2ee83e33a3e88b745eb
6
+ metadata.gz: b8cf69b8e91ae507188de12dd3addd8ccbc7389bc630d175ee940f00a12aea28adf1080dc335229170ff95db330a04e4c74d42446ff29439c4de9ba6c75506d2
7
+ data.tar.gz: fd23228461be6058ead19eaa9b4fce3705a0717b8eb8970ddcdd0b06bdb4734e9d103e2a910aaa09c31ab175d2d1776db199122dcf4c6d38fc77884b3fef65da
data/lib/evaluator.rb CHANGED
@@ -8,8 +8,6 @@ $fetch_from_server = :fetch_from_server
8
8
  $type_dynamic_config = 'dynamic_config'
9
9
 
10
10
  class Evaluator
11
- include EvaluationHelpers
12
-
13
11
  def initialize(store)
14
12
  @spec_store = store
15
13
  @initialized = true
@@ -80,10 +78,10 @@ class Evaluator
80
78
  return $fetch_from_server if other_gate_result == $fetch_from_server
81
79
  return type == 'pass_gate' ? other_gate_result[:gate_value] : !other_gate_result[:gate_value]
82
80
  when 'ip_based'
83
- value = get_value_from_user(user, field) || get_value_from_ip(user['ip'], field)
81
+ value = get_value_from_user(user, field) || get_value_from_ip(user&.value_lookup['ip'], field)
84
82
  return $fetch_from_server if value == $fetch_from_server
85
83
  when 'ua_based'
86
- value = get_value_from_user(user, field) || get_value_from_ua(user['userAgent'], field)
84
+ value = get_value_from_user(user, field) || get_value_from_ua(user&.value_lookup['userAgent'], field)
87
85
  return $fetch_from_server if value == $fetch_from_server
88
86
  when 'user_field'
89
87
  value = get_value_from_user(user, field)
@@ -102,13 +100,13 @@ class Evaluator
102
100
  case operator
103
101
  # numerical comparison
104
102
  when 'gt'
105
- return compare_numbers(value, target, ->(a, b) { a > b })
103
+ return EvaluationHelpers::compare_numbers(value, target, ->(a, b) { a > b })
106
104
  when 'gte'
107
- return compare_numbers(value, target, ->(a, b) { a >= b })
105
+ return EvaluationHelpers::compare_numbers(value, target, ->(a, b) { a >= b })
108
106
  when 'lt'
109
- return compare_numbers(value, target, ->(a, b) { a < b })
107
+ return EvaluationHelpers::compare_numbers(value, target, ->(a, b) { a < b })
110
108
  when 'lte'
111
- return compare_numbers(value, target, ->(a, b) { a <= b })
109
+ return EvaluationHelpers::compare_numbers(value, target, ->(a, b) { a <= b })
112
110
 
113
111
  # version comparison
114
112
  when 'version_gt'
@@ -126,17 +124,17 @@ class Evaluator
126
124
 
127
125
  # array operations
128
126
  when 'any'
129
- return array_contains(target, value)
127
+ return EvaluationHelpers::array_contains(target, value)
130
128
  when 'none'
131
- return !array_contains(target, value)
129
+ return !EvaluationHelpers::array_contains(target, value)
132
130
 
133
131
  #string
134
132
  when 'str_starts_with_any'
135
- return match_string_in_array(target, value, ->(a, b) { a.start_with?(b) })
133
+ return EvaluationHelpers::match_string_in_array(target, value, ->(a, b) { a.start_with?(b) })
136
134
  when 'str_ends_with_any'
137
- return match_string_in_array(target, value, ->(a, b) { a.end_with?(b) })
135
+ return EvaluationHelpers::match_string_in_array(target, value, ->(a, b) { a.end_with?(b) })
138
136
  when 'str_contains_any'
139
- return match_string_in_array(target, value, ->(a, b) { a.include?(b) })
137
+ return EvaluationHelpers::match_string_in_array(target, value, ->(a, b) { a.include?(b) })
140
138
  when 'str_matches'
141
139
  return (value.is_a?(String) && !(value =~ Regexp.new(target)).nil? rescue false)
142
140
  when 'eq'
data/lib/network.rb CHANGED
@@ -1,11 +1,8 @@
1
- require 'concurrent'
2
1
  require 'http'
3
2
  require 'json'
4
3
  require 'dynamic_config'
5
4
 
6
5
  class Network
7
- include Concurrent::Async
8
-
9
6
  def initialize(server_secret, api)
10
7
  super()
11
8
  unless api.end_with?('/')
@@ -20,7 +17,7 @@ class Network
20
17
 
21
18
  def check_gate(user, gate_name)
22
19
  begin
23
- request_body = JSON.generate({'user' => user&.serialize(), 'gateName' => gate_name})
20
+ request_body = JSON.generate({'user' => user&.serialize, 'gateName' => gate_name})
24
21
  response = @http.post(@api + 'check_gate', body: request_body)
25
22
  return JSON.parse(response.body)
26
23
  rescue
@@ -30,7 +27,7 @@ class Network
30
27
 
31
28
  def get_config(user, dynamic_config_name)
32
29
  begin
33
- request_body = JSON.generate({'user' => user&.serialize(), 'configName' => dynamic_config_name})
30
+ request_body = JSON.generate({'user' => user&.serialize, 'configName' => dynamic_config_name})
34
31
  response = @http.post(@api + 'get_config', body: request_body)
35
32
  return JSON.parse(response.body)
36
33
  rescue
@@ -1,4 +1,3 @@
1
- require 'concurrent'
2
1
  require 'config_result'
3
2
  require 'evaluator'
4
3
  require 'network'
@@ -8,139 +7,137 @@ require 'statsig_user'
8
7
  require 'spec_store'
9
8
 
10
9
  class StatsigDriver
11
- include Concurrent::Async
12
-
13
- def initialize(secret_key)
14
- super()
15
- if !secret_key.is_a?(String) || !secret_key.start_with?('secret-')
16
- raise 'Invalid secret key provided. Provide your project secret key from the Statsig console'
17
- end
18
- @shutdown = false
19
- @secret_key = secret_key
20
- @net = Network.new(secret_key, 'https://api.statsig.com/v1/')
21
- @statsig_metadata = {
22
- 'sdkType' => 'ruby-server',
23
- 'sdkVersion' => Gem::Specification::load('statsig.gemspec')&.version,
24
- }
25
- @logger = StatsigLogger.new(@net, @statsig_metadata)
26
-
27
- downloaded_specs = @net.download_config_specs
28
- unless downloaded_specs.nil?
29
- @initialized = true
30
- end
31
-
32
- @store = SpecStore.new(downloaded_specs)
33
- @evaluator = Evaluator.new(@store)
34
-
35
- @polling_thread = @net.poll_for_changes(-> (config_specs) { @store.process(config_specs) })
36
- end
37
-
38
- def check_gate(user, gate_name)
39
- if !user.nil? && !user.instance_of?(StatsigUser)
40
- raise 'Must provide a valid StatsigUser'
41
- end
42
- if !gate_name.is_a?(String) || gate_name.empty?
43
- raise 'Invalid gate_name provided'
44
- end
45
- check_shutdown
46
- unless @initialized
47
- return false
48
- end
49
-
50
- res = @evaluator.check_gate(user, gate_name)
51
- if res.nil?
52
- res = ConfigResult.new(gate_name)
53
- end
54
-
55
- if res == $fetch_from_server
56
- res = check_gate_fallback(user, gate_name)
57
- end
58
-
59
- @logger.logGateExposure(user, res.name, res.gate_value, res.rule_id)
60
- res.gate_value
61
- end
62
-
63
- def get_config(user, dynamic_config_name)
64
- if !user.nil? && !user.instance_of?(StatsigUser)
65
- raise 'Must provide a valid StatsigUser or nil'
66
- end
67
- if !dynamic_config_name.is_a?(String) || dynamic_config_name.empty?
68
- raise "Invalid dynamic_config_name provided"
69
- end
70
- check_shutdown
71
- unless @initialized
72
- return DynamicConfig.new(dynamic_config_name)
73
- end
74
-
75
- res = @evaluator.get_config(user, dynamic_config_name)
76
- if res.nil?
77
- res = ConfigResult.new(dynamic_config_name)
78
- end
79
-
80
- if res == $fetch_from_server
81
- res = get_config_fallback(user, dynamic_config_name)
82
- end
83
-
84
- result_config = DynamicConfig.new(res.name, res.json_value, res.rule_id)
85
- @logger.logConfigExposure(user, result_config.name, result_config.rule_id)
86
- result_config
87
- end
88
-
89
- def log_event(user, event_name, value = nil, metadata = nil)
90
- if !user.nil? && !user.instance_of?(StatsigUser)
91
- raise 'Must provide a valid StatsigUser or nil'
92
- end
93
- check_shutdown
94
-
95
- event = StatsigEvent.new(event_name)
96
- event.user = user&.serialize
97
- event.value = value
98
- event.metadata = metadata
99
- event.statsig_metadata = @statsig_metadata
100
- @logger.log_event(event)
101
- end
102
-
103
- def shutdown
104
- @shutdown = true
105
- @logger.flush
106
- @polling_thread&.exit
107
- end
108
-
109
- private
110
-
111
- def check_shutdown
112
- if @shutdown
113
- puts 'SDK has been shutdown. Updates in the Statsig Console will no longer reflect.'
114
- end
115
- end
116
-
117
- def check_gate_fallback(user, gate_name)
118
- network_result = @net.check_gate(user, gate_name)
119
- if network_result.nil?
120
- config_result = ConfigResult.new(gate_name)
121
- return config_result
122
- end
123
-
124
- ConfigResult.new(
125
- network_result['name'],
126
- network_result['value'],
127
- {},
128
- network_result['rule_id'],
129
- )
130
- end
131
-
132
- def get_config_fallback(user, dynamic_config_name)
133
- network_result = @net.get_config(user, dynamic_config_name)
134
- if network_result.nil?
135
- config_result = ConfigResult.new(dynamic_config_name)
136
- return config_result
137
- end
138
-
139
- ConfigResult.new(
140
- network_result['name'],
141
- false,
142
- network_result['value'],
143
- network_result['rule_id'],
144
- )
145
- end
146
- end
10
+ def initialize(secret_key)
11
+ super()
12
+ if !secret_key.is_a?(String) || !secret_key.start_with?('secret-')
13
+ raise 'Invalid secret key provided. Provide your project secret key from the Statsig console'
14
+ end
15
+ @shutdown = false
16
+ @secret_key = secret_key
17
+ @net = Network.new(secret_key, 'https://api.statsig.com/v1/')
18
+ @statsig_metadata = {
19
+ 'sdkType' => 'ruby-server',
20
+ 'sdkVersion' => Gem::Specification::load('statsig.gemspec')&.version,
21
+ }
22
+ @logger = StatsigLogger.new(@net, @statsig_metadata)
23
+
24
+ downloaded_specs = @net.download_config_specs
25
+ unless downloaded_specs.nil?
26
+ @initialized = true
27
+ end
28
+
29
+ @store = SpecStore.new(downloaded_specs)
30
+ @evaluator = Evaluator.new(@store)
31
+
32
+ @polling_thread = @net.poll_for_changes(-> (config_specs) { @store.process(config_specs) })
33
+ end
34
+
35
+ def check_gate(user, gate_name)
36
+ if !user.nil? && !user.instance_of?(StatsigUser)
37
+ raise 'Must provide a valid StatsigUser'
38
+ end
39
+ if !gate_name.is_a?(String) || gate_name.empty?
40
+ raise 'Invalid gate_name provided'
41
+ end
42
+ check_shutdown
43
+ unless @initialized
44
+ return false
45
+ end
46
+
47
+ res = @evaluator.check_gate(user, gate_name)
48
+ if res.nil?
49
+ res = ConfigResult.new(gate_name)
50
+ end
51
+
52
+ if res == $fetch_from_server
53
+ res = check_gate_fallback(user, gate_name)
54
+ end
55
+
56
+ @logger.log_gate_exposure(user, res.name, res.gate_value, res.rule_id)
57
+ res.gate_value
58
+ end
59
+
60
+ def get_config(user, dynamic_config_name)
61
+ if !user.nil? && !user.instance_of?(StatsigUser)
62
+ raise 'Must provide a valid StatsigUser or nil'
63
+ end
64
+ if !dynamic_config_name.is_a?(String) || dynamic_config_name.empty?
65
+ raise "Invalid dynamic_config_name provided"
66
+ end
67
+ check_shutdown
68
+ unless @initialized
69
+ return DynamicConfig.new(dynamic_config_name)
70
+ end
71
+
72
+ res = @evaluator.get_config(user, dynamic_config_name)
73
+ if res.nil?
74
+ res = ConfigResult.new(dynamic_config_name)
75
+ end
76
+
77
+ if res == $fetch_from_server
78
+ res = get_config_fallback(user, dynamic_config_name)
79
+ end
80
+
81
+ result_config = DynamicConfig.new(res.name, res.json_value, res.rule_id)
82
+ @logger.log_config_exposure(user, result_config.name, result_config.rule_id)
83
+ result_config
84
+ end
85
+
86
+ def log_event(user, event_name, value = nil, metadata = nil)
87
+ if !user.nil? && !user.instance_of?(StatsigUser)
88
+ raise 'Must provide a valid StatsigUser or nil'
89
+ end
90
+ check_shutdown
91
+
92
+ event = StatsigEvent.new(event_name)
93
+ event.user = user&.serialize
94
+ event.value = value
95
+ event.metadata = metadata
96
+ event.statsig_metadata = @statsig_metadata
97
+ @logger.log_event(event)
98
+ end
99
+
100
+ def shutdown
101
+ @shutdown = true
102
+ @logger.flush
103
+ @polling_thread&.exit
104
+ end
105
+
106
+ private
107
+
108
+ def check_shutdown
109
+ if @shutdown
110
+ puts 'SDK has been shutdown. Updates in the Statsig Console will no longer reflect.'
111
+ end
112
+ end
113
+
114
+ def check_gate_fallback(user, gate_name)
115
+ network_result = @net.check_gate(user, gate_name)
116
+ if network_result.nil?
117
+ config_result = ConfigResult.new(gate_name)
118
+ return config_result
119
+ end
120
+
121
+ ConfigResult.new(
122
+ network_result['name'],
123
+ network_result['value'],
124
+ {},
125
+ network_result['rule_id'],
126
+ )
127
+ end
128
+
129
+ def get_config_fallback(user, dynamic_config_name)
130
+ network_result = @net.get_config(user, dynamic_config_name)
131
+ if network_result.nil?
132
+ config_result = ConfigResult.new(dynamic_config_name)
133
+ return config_result
134
+ end
135
+
136
+ ConfigResult.new(
137
+ network_result['name'],
138
+ false,
139
+ network_result['value'],
140
+ network_result['rule_id'],
141
+ )
142
+ end
143
+ end
@@ -13,11 +13,11 @@ class StatsigLogger
13
13
  def log_event(event)
14
14
  @events.push(event)
15
15
  if @events.length >= 500
16
- flush()
16
+ flush
17
17
  end
18
18
  end
19
19
 
20
- def logGateExposure(user, gate_name, value, rule_id)
20
+ def log_gate_exposure(user, gate_name, value, rule_id)
21
21
  event = StatsigEvent.new($gate_exposure_event)
22
22
  event.user = user
23
23
  event.metadata = {
@@ -29,7 +29,7 @@ class StatsigLogger
29
29
  log_event(event)
30
30
  end
31
31
 
32
- def logConfigExposure(user, config_name, rule_id)
32
+ def log_config_exposure(user, config_name, rule_id)
33
33
  event = StatsigEvent.new($config_exposure_event)
34
34
  event.user = user
35
35
  event.metadata = {
data/lib/statsig_user.rb CHANGED
@@ -15,6 +15,19 @@ class StatsigUser
15
15
  @custom = value.is_a?(Hash) ? value : Hash.new
16
16
  end
17
17
 
18
+ def initialize(user_hash = nil)
19
+ if user_hash.is_a?(Hash)
20
+ @user_id = user_hash['userID']
21
+ @email = user_hash['email']
22
+ @ip = user_hash['ip']
23
+ @user_agent = user_hash['userAgent']
24
+ @country = user_hash['country']
25
+ @locale = user_hash['locale']
26
+ @client_version = user_hash['clientVersion']
27
+ @custom = user_hash['custom']
28
+ end
29
+ end
30
+
18
31
  def serialize
19
32
  {
20
33
  'userID' => @user_id,
metadata CHANGED
@@ -1,75 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-01 00:00:00.000000000 Z
11
+ date: 2021-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: concurrent-ruby
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 1.1.0
23
- type: :development
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.1'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 1.1.0
33
- - !ruby/object:Gem::Dependency
34
- name: http
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '4.4'
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: 4.4.1
19
+ version: '2.1'
43
20
  type: :development
44
21
  prerelease: false
45
22
  version_requirements: !ruby/object:Gem::Requirement
46
23
  requirements:
47
24
  - - "~>"
48
25
  - !ruby/object:Gem::Version
49
- version: '4.4'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: 4.4.1
53
- - !ruby/object:Gem::Dependency
54
- name: browser
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '5.3'
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: 5.3.1
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - "~>"
68
- - !ruby/object:Gem::Version
69
- version: '5.3'
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- version: 5.3.1
26
+ version: '2.1'
73
27
  - !ruby/object:Gem::Dependency
74
28
  name: browser
75
29
  requirement: !ruby/object:Gem::Requirement
@@ -90,26 +44,6 @@ dependencies:
90
44
  - - ">="
91
45
  - !ruby/object:Gem::Version
92
46
  version: 5.3.1
93
- - !ruby/object:Gem::Dependency
94
- name: concurrent-ruby
95
- requirement: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - "~>"
98
- - !ruby/object:Gem::Version
99
- version: '1.1'
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: 1.1.0
103
- type: :runtime
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '1.1'
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- version: 1.1.0
113
47
  - !ruby/object:Gem::Dependency
114
48
  name: http
115
49
  requirement: !ruby/object:Gem::Requirement