growthbook 0.3.0 → 1.1.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.
@@ -1,37 +1,82 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Growthbook
4
+ # Internal class that overrides the default value of a Feature based on a set of requirements.
4
5
  class FeatureRule
5
- # @return [Hash , nil]
6
+ # @return [Hash , nil] Optional targeting condition
6
7
  attr_reader :condition
7
- # @return [Float , nil]
8
+
9
+ # @return [Float , nil] What percent of users should be included in the experiment (between 0 and 1, inclusive)
8
10
  attr_reader :coverage
9
- # @return [T , nil]
11
+
12
+ # @return [T , nil] Immediately force a specific value (ignore every other option besides condition and coverage)
10
13
  attr_reader :force
11
- # @return [T[] , nil]
14
+
15
+ # @return [T[] , nil] Run an experiment (A/B test) and randomly choose between these variations
12
16
  attr_reader :variations
13
- # @return [String , nil]
17
+
18
+ # @return [String , nil] The globally unique tracking key for the experiment (default to the feature key)
14
19
  attr_reader :key
15
- # @return [Float[] , nil]
20
+
21
+ # @return [Float[] , nil] How to weight traffic between variations. Must add to 1.
16
22
  attr_reader :weights
17
- # @return [Array , nil]
23
+
24
+ # @return [String , nil] Adds the experiment to a namespace
18
25
  attr_reader :namespace
19
- # @return [String , nil]
26
+
27
+ # @return [String , nil] What user attribute should be used to assign variations (defaults to id)
20
28
  attr_reader :hash_attribute
21
29
 
30
+ # @return [Integer , nil] The hash version to use (default to 1)
31
+ attr_reader :hash_version
32
+
33
+ # @return [BucketRange , nil] A more precise version of coverage
34
+ attr_reader :range
35
+
36
+ # @return [BucketRanges[] , nil] Ranges for experiment variations
37
+ attr_reader :ranges
38
+
39
+ # @return [VariationMeta[] , nil] Meta info about the experiment variations
40
+ attr_reader :meta
41
+
42
+ # @return [Filter[] , nil] Array of filters to apply to the rule
43
+ attr_reader :filters
44
+
45
+ # @return [String , nil] Seed to use for hashing
46
+ attr_reader :seed
47
+
48
+ # @return [String , nil] Human-readable name for the experiment
49
+ attr_reader :name
50
+
51
+ # @return [String , nil] The phase id of the experiment
52
+ attr_reader :phase
53
+
54
+ # @return [TrackData[] , nil] Array of tracking calls to fire
55
+ attr_reader :tracks
56
+
22
57
  def initialize(rule)
23
- @coverage = getOption(rule, :coverage)
24
- @force = getOption(rule, :force)
25
- @variations = getOption(rule, :variations)
26
- @key = getOption(rule, :key)
27
- @weights = getOption(rule, :weights)
28
- @namespace = getOption(rule, :namespace)
29
- @hash_attribute = getOption(rule, :hash_attribute) || getOption(rule, :hashAttribute)
30
-
31
- cond = getOption(rule, :condition)
58
+ @coverage = get_option(rule, :coverage)
59
+ @force = get_option(rule, :force)
60
+ @variations = get_option(rule, :variations)
61
+ @key = get_option(rule, :key)
62
+ @weights = get_option(rule, :weights)
63
+ @namespace = get_option(rule, :namespace)
64
+ @hash_attribute = get_option(rule, :hash_attribute) || get_option(rule, :hashAttribute)
65
+ @hash_version = get_option(rule, :hash_version) || get_option(rule, :hashVersion)
66
+ @range = get_option(rule, :range)
67
+ @ranges = get_option(rule, :ranges)
68
+ @meta = get_option(rule, :meta)
69
+ @filters = get_option(rule, :filters)
70
+ @seed = get_option(rule, :seed)
71
+ @name = get_option(rule, :name)
72
+ @phase = get_option(rule, :phase)
73
+ @tracks = get_option(rule, :tracks)
74
+
75
+ cond = get_option(rule, :condition)
32
76
  @condition = Growthbook::Conditions.parse_condition(cond) unless cond.nil?
33
77
  end
34
78
 
79
+ # @return [Growthbook::InlineExperiment, nil]
35
80
  def to_experiment(feature_key)
36
81
  return nil unless @variations
37
82
 
@@ -41,34 +86,51 @@ module Growthbook
41
86
  coverage: @coverage,
42
87
  weights: @weights,
43
88
  hash_attribute: @hash_attribute,
44
- namespace: @namespace
89
+ hash_version: @hash_version,
90
+ namespace: @namespace,
91
+ meta: @meta,
92
+ ranges: @ranges,
93
+ filters: @filters,
94
+ name: @name,
95
+ phase: @phase,
96
+ seed: @seed
45
97
  )
46
98
  end
47
99
 
48
- def is_experiment?
49
- !!@variations
100
+ def experiment?
101
+ return false if @variations.nil?
102
+
103
+ !@variations&.empty?
50
104
  end
51
105
 
52
- def is_force?
53
- !is_experiment? && !@force.nil?
106
+ def force?
107
+ !experiment? && !@force.nil?
54
108
  end
55
109
 
56
110
  def to_json(*_args)
57
- res = {}
58
- res['condition'] = @condition unless @condition.nil?
59
- res['coverage'] = @coverage unless @coverage.nil?
60
- res['force'] = @force unless @force.nil?
61
- res['variations'] = @variations unless @variations.nil?
62
- res['key'] = @key unless @key.nil?
63
- res['weights'] = @weights unless @weights.nil?
64
- res['namespace'] = @namespace unless @namespace.nil?
65
- res['hashAttribute'] = @hash_attribute unless @hash_attribute.nil?
66
- res
111
+ {
112
+ 'condition' => @condition,
113
+ 'coverage' => @coverage,
114
+ 'force' => @force,
115
+ 'variations' => @variations,
116
+ 'key' => @key,
117
+ 'weights' => @weights,
118
+ 'namespace' => @namespace,
119
+ 'hashAttribute' => @hash_attribute,
120
+ 'range' => @range,
121
+ 'ranges' => @ranges,
122
+ 'meta' => @meta,
123
+ 'filters' => @filters,
124
+ 'seed' => @seed,
125
+ 'name' => @name,
126
+ 'phase' => @phase,
127
+ 'tracks' => @tracks
128
+ }.compact
67
129
  end
68
130
 
69
131
  private
70
132
 
71
- def getOption(hash, key)
133
+ def get_option(hash, key)
72
134
  return hash[key.to_sym] if hash.key?(key.to_sym)
73
135
  return hash[key.to_s] if hash.key?(key.to_s)
74
136
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # FNV
4
+ # {https://github.com/jakedouglas/fnv-ruby Source}
5
+ class FNV
6
+ INIT32 = 0x811c9dc5
7
+ INIT64 = 0xcbf29ce484222325
8
+ PRIME32 = 0x01000193
9
+ PRIME64 = 0x100000001b3
10
+ MOD32 = 4_294_967_296
11
+ MOD64 = 18_446_744_073_709_551_616
12
+
13
+ def fnv1a_32(data)
14
+ hash = INIT32
15
+
16
+ data.bytes.each do |byte|
17
+ hash = hash ^ byte
18
+ hash = (hash * PRIME32) % MOD32
19
+ end
20
+
21
+ hash
22
+ end
23
+ end
@@ -1,80 +1,103 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Growthbook
4
+ # Class for creating an inline experiment for evaluating
4
5
  class InlineExperiment
5
- # @returns [String]
6
+ # @return [String] The globally unique identifier for the experiment
6
7
  attr_accessor :key
7
8
 
8
- # @returns [Any]
9
+ # @return [Array<String, Integer, Hash>] The different variations to choose between
9
10
  attr_accessor :variations
10
11
 
11
- # @returns [Bool]
12
- attr_accessor :active
13
-
14
- # @returns [Integer, nil]
15
- attr_accessor :force
16
-
17
- # @returns [Array<Float>, nil]
12
+ # @return [Array<Float>] How to weight traffic between variations. Must add to 1.
18
13
  attr_accessor :weights
19
14
 
20
- # @returns [Float]
15
+ # @return [true, false] If set to false, always return the control (first variation)
16
+ attr_accessor :active
17
+
18
+ # @return [Float] What percent of users should be included in the experiment (between 0 and 1, inclusive)
21
19
  attr_accessor :coverage
22
20
 
23
- # @returns [Hash, nil]
21
+ # @return [Array<Hash>] Array of ranges, one per variation
22
+ attr_accessor :ranges
23
+
24
+ # @return [Hash] Optional targeting condition
24
25
  attr_accessor :condition
25
26
 
26
- # @returns [Array]
27
+ # @return [String, nil] Adds the experiment to a namespace
27
28
  attr_accessor :namespace
28
29
 
29
- # @returns [String]
30
+ # @return [integer, nil] All users included in the experiment will be forced into the specific variation index
31
+ attr_accessor :force
32
+
33
+ # @return [String] What user attribute should be used to assign variations (defaults to id)
30
34
  attr_accessor :hash_attribute
31
35
 
32
- # Constructor for an Experiment
33
- #
34
- # @param options [Hash]
35
- # @option options [Array<Any>] :variations The variations to pick between
36
- # @option options [String] :key The unique identifier for this experiment
37
- # @option options [Float] :coverage (1.0) The percent of elegible traffic to include in the experiment
38
- # @option options [Array<Float>] :weights The relative weights of the variations.
39
- # Length must be the same as the number of variations. Total should add to 1.0.
40
- # Default is an even split between variations
41
- # @option options [Boolean] :anon (false) If false, the experiment uses the logged-in user id for bucketing
42
- # If true, the experiment uses the anonymous id for bucketing
43
- # @option options [Array<String>] :targeting Array of targeting rules in the format "key op value"
44
- # where op is one of: =, !=, <, >, ~, !~
45
- # @option options [Integer, nil] :force If an integer, force all users to get this variation
46
- # @option options [Hash] :data Data to attach to the variations
47
- def initialize(options = {})
48
- @key = getOption(options, :key, '').to_s
49
- @variations = getOption(options, :variations, [])
50
- @active = getOption(options, :active, true)
51
- @force = getOption(options, :force)
52
- @weights = getOption(options, :weights)
53
- @coverage = getOption(options, :coverage, 1)
54
- @condition = getOption(options, :condition)
55
- @namespace = getOption(options, :namespace)
56
- @hash_attribute = getOption(options, :hash_attribute) || getOption(options, :hashAttribute) || 'id'
57
- end
36
+ # @return [Integer] The hash version to use (default to 1)
37
+ attr_accessor :hash_version
58
38
 
59
- def getOption(hash, key, default = nil)
60
- return hash[key.to_sym] if hash.key?(key.to_sym)
61
- return hash[key.to_s] if hash.key?(key.to_s)
39
+ # @return [Array<Hash>] Meta info about the variations
40
+ attr_accessor :meta
62
41
 
63
- default
42
+ # @return [Array<Hash>] Array of filters to apply
43
+ attr_accessor :filters
44
+
45
+ # @return [String, nil] The hash seed to use
46
+ attr_accessor :seed
47
+
48
+ # @return [String] Human-readable name for the experiment
49
+ attr_accessor :name
50
+
51
+ # @return [String, nil] Id of the current experiment phase
52
+ attr_accessor :phase
53
+
54
+ def initialize(options = {})
55
+ @key = get_option(options, :key, '').to_s
56
+ @variations = get_option(options, :variations, [])
57
+ @weights = get_option(options, :weights)
58
+ @active = get_option(options, :active, true)
59
+ @coverage = get_option(options, :coverage, 1.0)
60
+ @ranges = get_option(options, :ranges)
61
+ @condition = get_option(options, :condition)
62
+ @namespace = get_option(options, :namespace)
63
+ @force = get_option(options, :force)
64
+ @hash_attribute = get_option(options, :hash_attribute) || get_option(options, :hashAttribute) || 'id'
65
+ @hash_version = get_option(options, :hash_version) || get_option(options, :hashVersion)
66
+ @meta = get_option(options, :meta)
67
+ @filters = get_option(options, :filters)
68
+ @seed = get_option(options, :seed)
69
+ @name = get_option(options, :name)
70
+ @phase = get_option(options, :phase)
64
71
  end
65
72
 
66
73
  def to_json(*_args)
67
74
  res = {}
68
75
  res['key'] = @key
69
76
  res['variations'] = @variations
70
- res['active'] = @active if @active != true && !@active.nil?
71
- res['force'] = @force unless @force.nil?
72
77
  res['weights'] = @weights unless @weights.nil?
78
+ res['active'] = @active if @active != true && !@active.nil?
73
79
  res['coverage'] = @coverage if @coverage != 1 && !@coverage.nil?
74
- res['condition'] = @condition unless @condition.nil?
75
- res['namespace'] = @namespace unless @namespace.nil?
80
+ res['ranges'] = @ranges
81
+ res['condition'] = @condition
82
+ res['namespace'] = @namespace
83
+ res['force'] = @force unless @force.nil?
76
84
  res['hashAttribute'] = @hash_attribute if @hash_attribute != 'id' && !@hash_attribute.nil?
77
- res
85
+ res['hashVersion'] = @hash_version
86
+ res['meta'] = @meta
87
+ res['filters'] = @filters
88
+ res['seed'] = @seed
89
+ res['name'] = @name
90
+ res['phase'] = @phase
91
+ res.compact
92
+ end
93
+
94
+ private
95
+
96
+ def get_option(hash, key, default = nil)
97
+ return hash[key.to_sym] if hash.key?(key.to_sym)
98
+ return hash[key.to_s] if hash.key?(key.to_s)
99
+
100
+ default
78
101
  end
79
102
  end
80
103
  end
@@ -1,62 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Growthbook
4
+ # Result of running an experiment.
4
5
  class InlineExperimentResult
5
- # Whether or not the user is in the experiment
6
- # @return [Bool]
6
+ # @return [Boolean] Whether or not the user is part of the experiment
7
7
  attr_reader :in_experiment
8
8
 
9
- # The array index of the assigned variation
10
- # @return [Integer]
9
+ # @return [Integer] The array index of the assigned variation
11
10
  attr_reader :variation_id
12
11
 
13
- # The assigned variation value
14
- # @return [Any]
12
+ # @return [Any] The array value of the assigned variation
15
13
  attr_reader :value
16
14
 
17
- # If the variation was randomly assigned based on user attribute hashes
18
- # @return [Bool]
15
+ # @return [Bool] If a hash was used to assign a variation
19
16
  attr_reader :hash_used
20
17
 
21
- # The attribute used to split traffic
22
- # @return [String]
18
+ # @return [String] The user attribute used to assign a variation
23
19
  attr_reader :hash_attribute
24
20
 
25
- # The value of the hashAttribute
26
- # @return [String]
21
+ # @return [String] The value of that attribute
27
22
  attr_reader :hash_value
28
23
 
24
+ # @return [String, nil] The id of the feature (if any) that the experiment came from
29
25
  attr_reader :feature_id
30
26
 
31
- def initialize(
32
- hash_used,
33
- in_experiment,
34
- variation_id,
35
- value,
36
- hash_attribute,
37
- hash_value,
38
- feature_id
39
- )
40
-
41
- @hash_used = hash_used
42
- @in_experiment = in_experiment
43
- @variation_id = variation_id
44
- @value = value
45
- @hash_attribute = hash_attribute
46
- @hash_value = hash_value
47
- @feature_id = feature_id
27
+ # @return [String] The unique key for the assigned variation
28
+ attr_reader :key
29
+
30
+ # @return [Float] The hash value used to assign a variation (float from 0 to 1)
31
+ attr_reader :bucket
32
+
33
+ # @return [String , nil] Human-readable name for the experiment
34
+ attr_reader :name
35
+
36
+ # @return [Boolean] Used for holdout groups
37
+ attr_accessor :passthrough
38
+
39
+ def initialize(options = {})
40
+ @key = options[:key]
41
+ @in_experiment = options[:in_experiment]
42
+ @variation_id = options[:variation_id]
43
+ @value = options[:value]
44
+ @hash_used = options[:hash_used]
45
+ @hash_attribute = options[:hash_attribute]
46
+ @hash_value = options[:hash_value]
47
+ @feature_id = options[:feature_id]
48
+ @bucket = options[:bucket]
49
+ @name = options[:name]
50
+ @passthrough = options[:passthrough]
51
+ end
52
+
53
+ # If the variation was randomly assigned based on user attribute hashes
54
+ # @return [Bool]
55
+ def hash_used?
56
+ @hash_used
57
+ end
58
+
59
+ # Whether or not the user is in the experiment
60
+ # @return [Bool]
61
+ def in_experiment?
62
+ @in_experiment
48
63
  end
49
64
 
50
65
  def to_json(*_args)
51
- res = {}
52
- res['inExperiment'] = @in_experiment
53
- res['hashUsed'] = @hash_used
54
- res['variationId'] = @variation_id
55
- res['value'] = @value
56
- res['hashAttribute'] = @hash_attribute
57
- res['hashValue'] = @hash_value
58
- res['featureId'] = @feature_id.to_s
59
- res
66
+ {
67
+ 'inExperiment' => @in_experiment,
68
+ 'variationId' => @variation_id,
69
+ 'value' => @value,
70
+ 'hashUsed' => @hash_used,
71
+ 'hashAttribute' => @hash_attribute,
72
+ 'hashValue' => @hash_value,
73
+ 'featureId' => @feature_id.to_s,
74
+ 'key' => @key.to_s,
75
+ 'bucket' => @bucket,
76
+ 'name' => @name,
77
+ 'passthrough' => @passthrough
78
+ }.compact
60
79
  end
61
80
  end
62
81
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Growthbook
4
+ # Extendable class that can be used as the tracking callback
5
+ class TrackingCallback
6
+ def on_experiment_viewed(_experiment, _result); end
7
+ end
8
+ end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fnv'
3
+ require 'base64'
4
+ require 'bigdecimal'
5
+ require 'bigdecimal/util'
4
6
 
5
7
  module Growthbook
8
+ # internal use only
6
9
  class Util
7
- def self.checkRule(actual, op, desired)
10
+ def self.check_rule(actual, op, desired)
8
11
  # Check if both strings are numeric so we can do natural ordering
9
12
  # for greater than / less than operators
10
13
  numeric = begin
@@ -15,9 +18,9 @@ module Growthbook
15
18
 
16
19
  case op
17
20
  when '='
18
- numeric ? Float(actual) == Float(desired) : actual == desired
21
+ numeric ? Float(actual).to_d == Float(desired).to_d : actual == desired
19
22
  when '!='
20
- numeric ? Float(actual) != Float(desired) : actual != desired
23
+ numeric ? Float(actual).to_d != Float(desired).to_d : actual != desired
21
24
  when '>'
22
25
  numeric ? Float(actual) > Float(desired) : actual > desired
23
26
  when '<'
@@ -39,100 +42,86 @@ module Growthbook
39
42
  end
40
43
  end
41
44
 
42
- def self.chooseVariation(userId, experiment)
43
- testId = experiment.id
44
- weights = experiment.getScaledWeights
45
+ # @return [Float, nil] Hash, or nil if the hash version is invalid
46
+ def self.get_hash(seed:, value:, version:)
47
+ return (FNV.new.fnv1a_32(value + seed) % 1000) / 1000.0 if version == 1
48
+ return (FNV.new.fnv1a_32(FNV.new.fnv1a_32(seed + value).to_s) % 10_000) / 10_000.0 if version == 2
45
49
 
46
- # Hash the user id and testName to a number from 0 to 1
47
- n = (FNV.new.fnv1a_32(userId + testId) % 1000) / 1000.0
48
-
49
- cumulativeWeight = 0
50
-
51
- match = -1
52
- i = 0
53
- weights.each do |weight|
54
- cumulativeWeight += weight
55
- if n < cumulativeWeight
56
- match = i
57
- break
58
- end
59
- i += 1
60
- end
61
-
62
- match
50
+ nil
63
51
  end
64
52
 
65
- def self.hash(str)
66
- (FNV.new.fnv1a_32(str) % 1000) / 1000.0
67
- end
53
+ def self.in_namespace?(hash_value, namespace)
54
+ return false if namespace.nil?
55
+
56
+ n = get_hash(seed: "__#{namespace[0]}", value: hash_value, version: 1)
57
+ return false if n.nil?
68
58
 
69
- def self.in_namespace(userId, namespace)
70
- n = hash("#{userId}__#{namespace[0]}")
71
59
  n >= namespace[1] && n < namespace[2]
72
60
  end
73
61
 
74
- def self.get_equal_weights(numVariations)
75
- return [] if numVariations < 1
62
+ def self.get_equal_weights(num_variations)
63
+ return [] if num_variations < 1
76
64
 
77
65
  weights = []
78
- (1..numVariations).each do |_i|
79
- weights << (1.0 / numVariations)
66
+ (1..num_variations).each do |_i|
67
+ weights << (1.0 / num_variations)
80
68
  end
81
69
  weights
82
70
  end
83
71
 
84
72
  # Determine bucket ranges for experiment variations
85
- def self.get_bucket_ranges(numVariations, coverage = 1, weights = [])
73
+ def self.get_bucket_ranges(num_variations, coverage, weights)
86
74
  # Make sure coverage is within bounds
87
- coverage = 1 if coverage.nil?
88
- coverage = 0 if coverage.negative?
89
- coverage = 1 if coverage > 1
75
+ coverage = 1.0 if coverage.nil?
76
+ coverage = 0.0 if coverage.negative?
77
+ coverage = 1.0 if coverage > 1
90
78
 
91
79
  # Default to equal weights
92
- weights = get_equal_weights(numVariations) if !weights || weights.length != numVariations
80
+ weights = get_equal_weights(num_variations) if !weights || weights.length != num_variations
93
81
 
94
82
  # If weights don't add up to 1 (or close to it), default to equal weights
95
83
  total = weights.sum
96
- weights = get_equal_weights(numVariations) if total < 0.99 || total > 1.01
84
+ weights = get_equal_weights(num_variations) if total < 0.99 || total > 1.01
97
85
 
98
86
  # Convert weights to ranges
99
- cumulative = 0
87
+ cumulative = 0.0
100
88
  ranges = []
101
89
  weights.each do |w|
102
90
  start = cumulative
103
91
  cumulative += w
104
- ranges << [start, start + coverage * w]
92
+ ranges << [start, start + (coverage * w)]
105
93
  end
106
94
 
107
95
  ranges
108
96
  end
109
97
 
110
98
  # Chose a variation based on a hash and range
111
- def self.choose_variation(n, ranges)
99
+ def self.choose_variation(num, ranges)
112
100
  ranges.each_with_index do |range, i|
113
- return i if n >= range[0] && n < range[1]
101
+ return i if num >= range[0] && num < range[1]
114
102
  end
115
103
  -1
116
104
  end
117
105
 
118
106
  # Get an override variation from a url querystring
119
107
  # e.g. http://localhost?my-test=1 will return `1` for id `my-test`
120
- def self.get_query_string_override(id, url, numVariations)
108
+ def self.get_query_string_override(id, url, num_variations)
121
109
  # Skip if url is empty
122
- return nil if url == ''
110
+ return nil if url == '' || id.nil?
123
111
 
124
112
  # Parse out the query string
125
113
  parsed = URI(url)
126
- return nil unless parsed.query
114
+ parsed_query = parsed.query
115
+ return nil if parsed_query.nil?
127
116
 
128
- qs = URI.decode_www_form(parsed.query)
117
+ qs = URI.decode_www_form(parsed_query)
129
118
 
130
119
  # Look for `id` in the querystring and get the value
131
120
  vals = qs.assoc(id)
132
121
  return nil unless vals
133
122
 
134
123
  val = vals.last
135
- return nill unless val
124
+ return nil unless val
136
125
 
137
126
  # Parse the value as an integer
138
127
  n = begin
@@ -144,9 +133,13 @@ module Growthbook
144
133
  # Make sure the integer is within range
145
134
  return nil if n.nil?
146
135
  return nil if n.negative?
147
- return nil if n >= numVariations
136
+ return nil if n >= num_variations
148
137
 
149
138
  n
150
139
  end
140
+
141
+ def self.in_range?(num, range)
142
+ num >= range[0] && num < range[1]
143
+ end
151
144
  end
152
145
  end
data/lib/growthbook.rb CHANGED
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # The GrowthBook SDK
3
4
  module Growthbook
4
5
  end
5
6
 
6
- require 'growthbook/client'
7
7
  require 'growthbook/conditions'
8
8
  require 'growthbook/context'
9
- require 'growthbook/experiment'
10
- require 'growthbook/experiment_result'
9
+ require 'growthbook/decryption_util'
11
10
  require 'growthbook/feature'
11
+ require 'growthbook/feature_repository'
12
12
  require 'growthbook/feature_result'
13
13
  require 'growthbook/feature_rule'
14
+ require 'growthbook/fnv'
14
15
  require 'growthbook/inline_experiment'
15
16
  require 'growthbook/inline_experiment_result'
16
- require 'growthbook/lookup_result'
17
- require 'growthbook/user'
17
+ require 'growthbook/tracking_callback'
18
18
  require 'growthbook/util'