statsig 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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