growthbook 0.3.0 → 1.0.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/growthbook/conditions.rb +13 -13
- data/lib/growthbook/context.rb +115 -39
- data/lib/growthbook/feature.rb +4 -3
- data/lib/growthbook/feature_result.rb +3 -2
- data/lib/growthbook/feature_rule.rb +92 -32
- data/lib/growthbook/inline_experiment.rb +71 -48
- data/lib/growthbook/inline_experiment_result.rb +57 -38
- data/lib/growthbook/util.rb +29 -42
- data/lib/growthbook.rb +1 -5
- metadata +34 -22
- data/lib/growthbook/client.rb +0 -67
- data/lib/growthbook/experiment.rb +0 -72
- data/lib/growthbook/experiment_result.rb +0 -43
- data/lib/growthbook/lookup_result.rb +0 -44
- data/lib/growthbook/user.rb +0 -165
- data/spec/cases.json +0 -2923
- data/spec/client_spec.rb +0 -57
- data/spec/context_spec.rb +0 -124
- data/spec/json_spec.rb +0 -160
- data/spec/user_spec.rb +0 -213
- data/spec/util_spec.rb +0 -154
@@ -1,43 +0,0 @@
|
|
1
|
-
module Growthbook
|
2
|
-
class ExperimentResult
|
3
|
-
# The experiment that was performed
|
4
|
-
# @return [Growthbook::Experiment, nil] If nil, then the experiment with the required id could not be found
|
5
|
-
attr_reader :experiment
|
6
|
-
|
7
|
-
# The user that was experimented on
|
8
|
-
# @return [Growthbook::User]
|
9
|
-
attr_reader :user
|
10
|
-
|
11
|
-
# The chosen variation. -1 for "not in experiment", 0 for control, 1 for 1st variation, etc.
|
12
|
-
# @return [Integer]
|
13
|
-
attr_reader :variation
|
14
|
-
|
15
|
-
# The data tied to the chosen variation
|
16
|
-
# @return [Hash]
|
17
|
-
attr_reader :data
|
18
|
-
|
19
|
-
@forced = false
|
20
|
-
|
21
|
-
def forced?
|
22
|
-
@forced
|
23
|
-
end
|
24
|
-
|
25
|
-
def shouldTrack?
|
26
|
-
!@forced && @variation >= 0
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize(user = nil, experiment = nil, variation = -1, forced = false)
|
30
|
-
@experiment = experiment
|
31
|
-
@variation = variation
|
32
|
-
@forced = forced
|
33
|
-
|
34
|
-
@data = {}
|
35
|
-
if experiment && experiment.data
|
36
|
-
var = variation < 0 ? 0 : variation
|
37
|
-
experiment.data.each do |k, v|
|
38
|
-
@data[k] = v[var]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module Growthbook
|
2
|
-
class LookupResult
|
3
|
-
# The first matching experiment
|
4
|
-
# @return [Growthbook::Experiment]
|
5
|
-
attr_reader :experiment
|
6
|
-
|
7
|
-
# The chosen variation. -1 for "not in experiment", 0 for control, 1 for 1st variation, etc.
|
8
|
-
# @return [Integer]
|
9
|
-
attr_reader :variation
|
10
|
-
|
11
|
-
# The data tied to the chosen variation
|
12
|
-
# @return [Hash]
|
13
|
-
attr_reader :data
|
14
|
-
|
15
|
-
# The value of the data key that was used to lookup the experiment
|
16
|
-
attr_reader :value
|
17
|
-
|
18
|
-
@forced
|
19
|
-
|
20
|
-
def forced?
|
21
|
-
@forced
|
22
|
-
end
|
23
|
-
|
24
|
-
def shouldTrack?
|
25
|
-
!@forced && @variation >= 0
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(result, key)
|
29
|
-
@experiment = result.experiment
|
30
|
-
@variation = result.variation
|
31
|
-
@forced = result.forced?
|
32
|
-
|
33
|
-
@data = {}
|
34
|
-
if @experiment && @experiment.data
|
35
|
-
var = @variation <0 ? 0 : @variation
|
36
|
-
@experiment.data.each do |k, v|
|
37
|
-
@data[k] = v[var]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
@value = @data[key] || nil
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
data/lib/growthbook/user.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
module Growthbook
|
4
|
-
class User
|
5
|
-
# @returns [String, nil]
|
6
|
-
attr_accessor :id
|
7
|
-
|
8
|
-
# @returns [String, nil]
|
9
|
-
attr_accessor :anonId
|
10
|
-
|
11
|
-
# @returns [Hash, nil]
|
12
|
-
attr_reader :attributes
|
13
|
-
|
14
|
-
# @returns [Array<Growthbook::ExperimentResult>]
|
15
|
-
attr_reader :resultsToTrack
|
16
|
-
|
17
|
-
@client
|
18
|
-
@attributeMap = {}
|
19
|
-
@experimentsTracked
|
20
|
-
|
21
|
-
def initialize(anonId, id, attributes, client)
|
22
|
-
@anonId = anonId
|
23
|
-
@id = id
|
24
|
-
@attributes = attributes
|
25
|
-
@client = client
|
26
|
-
updateAttributeMap
|
27
|
-
|
28
|
-
@resultsToTrack = []
|
29
|
-
@experimentsTracked = Set[]
|
30
|
-
end
|
31
|
-
|
32
|
-
# Set the user attributes
|
33
|
-
#
|
34
|
-
# @params attributes [Hash, nil] Any user attributes you want to use for experiment targeting
|
35
|
-
# Values can be any type, even nested arrays and hashes
|
36
|
-
def attributes=(attributes)
|
37
|
-
@attributes = attributes
|
38
|
-
updateAttributeMap
|
39
|
-
end
|
40
|
-
|
41
|
-
# Run an experiment on this user
|
42
|
-
# @param experiment [Growthbook::Experiment, String] If string, will lookup the experiment by id in the client
|
43
|
-
# @return [Growthbook::ExperimentResult]
|
44
|
-
def experiment(experiment)
|
45
|
-
# If experiments are disabled globally
|
46
|
-
return getExperimentResult unless @client.enabled
|
47
|
-
|
48
|
-
# Make sure experiment is always an object (or nil)
|
49
|
-
id = ""
|
50
|
-
if experiment.is_a? String
|
51
|
-
id = experiment
|
52
|
-
experiment = @client.getExperiment(id)
|
53
|
-
else
|
54
|
-
id = experiment.id
|
55
|
-
override = @client.getExperiment(id)
|
56
|
-
experiment = override if override
|
57
|
-
end
|
58
|
-
|
59
|
-
# No experiment found
|
60
|
-
return getExperimentResult unless experiment
|
61
|
-
|
62
|
-
# User missing required user id type
|
63
|
-
userId = experiment.anon ? @anonId : @id
|
64
|
-
if !userId
|
65
|
-
return getExperimentResult(experiment)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Experiment has targeting rules, check if user passes
|
69
|
-
if experiment.targeting
|
70
|
-
return getExperimentResult(experiment) unless isTargeted(experiment.targeting)
|
71
|
-
end
|
72
|
-
|
73
|
-
# Experiment has a specific variation forced
|
74
|
-
if experiment.force != nil
|
75
|
-
return getExperimentResult(experiment, experiment.force, true)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Choose a variation for the user
|
79
|
-
variation = Growthbook::Util.chooseVariation(userId, experiment)
|
80
|
-
result = getExperimentResult(experiment, variation)
|
81
|
-
|
82
|
-
# Add to the list of experiments that should be tracked in analytics
|
83
|
-
if result.shouldTrack? && !@experimentsTracked.include?(experiment.id)
|
84
|
-
@experimentsTracked << experiment.id
|
85
|
-
@resultsToTrack << result
|
86
|
-
end
|
87
|
-
|
88
|
-
return result
|
89
|
-
end
|
90
|
-
|
91
|
-
# Run the first matching experiment that defines variation data for the requested key
|
92
|
-
# @param key [String, Symbol] The key to look up
|
93
|
-
# @return [Growthbook::LookupResult, nil] If nil, no matching experiments found
|
94
|
-
def lookupByDataKey(key)
|
95
|
-
@client.experiments.each do |exp|
|
96
|
-
if exp.data && exp.data.key?(key)
|
97
|
-
ret = experiment(exp)
|
98
|
-
if ret.variation >= 0
|
99
|
-
return Growthbook::LookupResult.new(ret, key)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
return nil
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
def getExperimentResult(experiment = nil, variation = -1, forced = false)
|
110
|
-
Growthbook::ExperimentResult.new(self, experiment, variation, forced)
|
111
|
-
end
|
112
|
-
|
113
|
-
def flattenUserValues(prefix, val)
|
114
|
-
if val.nil?
|
115
|
-
return []
|
116
|
-
end
|
117
|
-
|
118
|
-
if val.is_a? Hash
|
119
|
-
ret = []
|
120
|
-
val.each do |k, v|
|
121
|
-
ret.concat(flattenUserValues(prefix.length>0 ? prefix.to_s + "." + k.to_s : k.to_s, v))
|
122
|
-
end
|
123
|
-
return ret
|
124
|
-
end
|
125
|
-
|
126
|
-
if val.is_a? Array
|
127
|
-
val = val.join ","
|
128
|
-
elsif !!val == val
|
129
|
-
val = val ? "true" : "false"
|
130
|
-
end
|
131
|
-
|
132
|
-
return [
|
133
|
-
{
|
134
|
-
"k" => prefix.to_s,
|
135
|
-
"v" => val.to_s
|
136
|
-
}
|
137
|
-
]
|
138
|
-
end
|
139
|
-
|
140
|
-
def updateAttributeMap
|
141
|
-
@attributeMap = {}
|
142
|
-
flat = flattenUserValues("", @attributes)
|
143
|
-
flat.each do |item|
|
144
|
-
@attributeMap[item["k"]] = item["v"]
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def isTargeted(rules)
|
149
|
-
pass = true
|
150
|
-
rules.each do |rule|
|
151
|
-
parts = rule.split(" ", 3)
|
152
|
-
if parts.length == 3
|
153
|
-
key = parts[0].strip
|
154
|
-
actual = @attributeMap[key] || ""
|
155
|
-
if !Growthbook::Util.checkRule(actual, parts[1].strip, parts[2].strip)
|
156
|
-
pass = false
|
157
|
-
break
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
return pass
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|