amplitude-experiment 1.0.0.beta.6 → 1.0.0.beta.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -2
- data/lib/amplitude-experiment.rb +8 -3
- data/lib/experiment/factory.rb +20 -4
- data/lib/experiment/local/cache.rb +53 -0
- data/lib/experiment/local/client.rb +84 -0
- data/lib/experiment/local/config.rb +35 -0
- data/lib/experiment/local/evaluation/evaluation.rb +48 -0
- data/lib/experiment/local/fetcher.rb +45 -0
- data/lib/experiment/local/poller.rb +48 -0
- data/lib/experiment/{client.rb → remote/client.rb} +2 -2
- data/lib/experiment/{config.rb → remote/config.rb} +1 -1
- data/lib/experiment/version.rb +1 -1
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c1c474415a95c82dd30feb5730a517d3df489f39ac6b307583a66d20b844fc3
|
4
|
+
data.tar.gz: f215b72c1dc816aa205c68d3b9fac5b4685ca4ee90cacc035f6f2106bd1cf9eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e55171f2998fcebf9f9a49a785445e1731b0b86a0d98a5b0ff8111702cb7a5c03df8c3e641492acaa30bb45b3750fc237c79d6a7ee964424cd8ebb5061180835
|
7
|
+
data.tar.gz: 3ec5bfd97ed595841541074b9f7079e94a7b0452a8b53ecdd722dfd18956ed40f12d26503c04d6a9a84ae2ec2c9d4119dea0420b4566bb591f959296710df080
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ gem install amplitude-experiment --pre
|
|
25
25
|
```
|
26
26
|
|
27
27
|
|
28
|
-
## Quick Start
|
28
|
+
## Remote Evaluation Quick Start
|
29
29
|
```ruby
|
30
30
|
require 'amplitude-experiment'
|
31
31
|
|
@@ -33,7 +33,7 @@ require 'amplitude-experiment'
|
|
33
33
|
apiKey = 'YOUR-API-KEY'
|
34
34
|
|
35
35
|
# (2) Initialize the experiment client
|
36
|
-
experiment = AmplitudeExperiment.
|
36
|
+
experiment = AmplitudeExperiment.initialize_remote(api_key)
|
37
37
|
|
38
38
|
# (3) Fetch variants for a user
|
39
39
|
user = AmplitudeExperiment::User.new(user_id: 'user@company.com', device_id: 'abcezas123', user_properties: {'premium' => true})
|
@@ -64,6 +64,33 @@ unless variant.nil?
|
|
64
64
|
end
|
65
65
|
```
|
66
66
|
|
67
|
+
## Local Evaluation Quick Start
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require 'amplitude-experiment'
|
71
|
+
|
72
|
+
# (1) Get your deployment's API key
|
73
|
+
apiKey = 'YOUR-API-KEY'
|
74
|
+
|
75
|
+
# (2) Initialize the experiment client
|
76
|
+
experiment = AmplitudeExperiment.initialize_local(api_key)
|
77
|
+
|
78
|
+
# (3) Start the local evaluation client
|
79
|
+
experiment.start
|
80
|
+
|
81
|
+
# (4) Evaluate a user
|
82
|
+
user = AmplitudeExperiment::User.new(user_id: 'user@company.com', device_id: 'abcezas123', user_properties: {'premium' => true})
|
83
|
+
variants = experiment.evaluate(user)
|
84
|
+
variant = variants['YOUR-FLAG-KEY']
|
85
|
+
unless variant.nil?
|
86
|
+
if variant.value == 'on'
|
87
|
+
# Flag is on
|
88
|
+
else
|
89
|
+
# Flag is off
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
67
94
|
## More Information
|
68
95
|
Please visit our :100:[Developer Center](https://www.docs.developers.amplitude.com/experiment/sdks/ruby-sdk/) for more instructions on using our the SDK.
|
69
96
|
|
data/lib/amplitude-experiment.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
require 'experiment/version'
|
2
|
-
require 'experiment/
|
2
|
+
require 'experiment/persistent_http_client'
|
3
|
+
require 'experiment/remote/config'
|
3
4
|
require 'experiment/cookie'
|
4
5
|
require 'experiment/user'
|
5
6
|
require 'experiment/variant'
|
6
7
|
require 'experiment/factory'
|
7
|
-
require 'experiment/
|
8
|
-
require 'experiment/client'
|
8
|
+
require 'experiment/remote/client'
|
9
|
+
require 'experiment/local/client'
|
10
|
+
require 'experiment/local/config'
|
11
|
+
require 'experiment/local/cache'
|
12
|
+
require 'experiment/local/fetcher'
|
13
|
+
require 'experiment/local/poller'
|
9
14
|
|
10
15
|
# Amplitude Experiment Module
|
11
16
|
module AmplitudeExperiment
|
data/lib/experiment/factory.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Provides factory methods for storing singleton instance of Client
|
2
2
|
module AmplitudeExperiment
|
3
|
-
@
|
3
|
+
@local_instance = {}
|
4
|
+
@remote_instance = {}
|
4
5
|
@default_instance = '$default_instance'
|
5
6
|
|
6
7
|
# Initializes a singleton Client. This method returns a default singleton instance, subsequent calls to
|
@@ -8,8 +9,23 @@ module AmplitudeExperiment
|
|
8
9
|
#
|
9
10
|
# @param [String] api_key The environment API Key
|
10
11
|
# @param [Config] config Optional Config.
|
11
|
-
def self.
|
12
|
-
|
13
|
-
|
12
|
+
def self.initialize_remote(api_key, config = nil)
|
13
|
+
unless @local_instance.key?(@default_instance)
|
14
|
+
@local_instance.store(@default_instance, RemoteEvaluationClient.new(api_key, config))
|
15
|
+
end
|
16
|
+
@local_instance.fetch(@default_instance)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Initializes a local evaluation Client. A local evaluation client can evaluate local flags or experiments for a
|
20
|
+
# user without requiring a remote call to the amplitude evaluation server. In order to best leverage local
|
21
|
+
# evaluation, all flags, and experiments being evaluated server side should be configured as local.
|
22
|
+
#
|
23
|
+
# @param [String] api_key The environment API Key
|
24
|
+
# @param [Config] config Optional Config.
|
25
|
+
def self.initialize_local(api_key, config = nil)
|
26
|
+
unless @remote_instance.key?(@default_instance)
|
27
|
+
@remote_instance.store(@default_instance, LocalEvaluationClient.new(api_key, config))
|
28
|
+
end
|
29
|
+
@remote_instance.fetch(@default_instance)
|
14
30
|
end
|
15
31
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module AmplitudeExperiment
|
5
|
+
# InMemoryFlagConfigCache
|
6
|
+
# The place to store the flag configs fetched from the server
|
7
|
+
class InMemoryFlagConfigCache
|
8
|
+
attr_accessor :cache
|
9
|
+
|
10
|
+
def initialize(flag_configs = {})
|
11
|
+
@semaphore = Mutex.new
|
12
|
+
@cache = flag_configs
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(flag_key)
|
16
|
+
@semaphore.synchronize do
|
17
|
+
@cache.fetch(flag_key, nil)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def caches
|
22
|
+
@semaphore.synchronize do
|
23
|
+
@cache
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def put(flag_key, flag_config)
|
28
|
+
@semaphore.synchronize do
|
29
|
+
@cache.store(flag_key, flag_config.clone)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def put_all(flag_configs)
|
34
|
+
@semaphore.synchronize do
|
35
|
+
flag_configs.each do |key, value|
|
36
|
+
@cache.store(key, value.clone) if value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(flag_key)
|
42
|
+
@semaphore.synchronize do
|
43
|
+
@cache.delete(flag_key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def clear
|
48
|
+
@semaphore.synchronize do
|
49
|
+
@cache = {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'logger'
|
3
|
+
require 'experiment/local/evaluation/evaluation'
|
4
|
+
|
5
|
+
module AmplitudeExperiment
|
6
|
+
# Main client for fetching variant data.
|
7
|
+
class LocalEvaluationClient
|
8
|
+
# Creates a new Experiment Client instance.
|
9
|
+
#
|
10
|
+
# @param [String] api_key The environment API Key
|
11
|
+
# @param [LocalEvaluationConfig] config The config object
|
12
|
+
def initialize(api_key, config = nil)
|
13
|
+
@api_key = api_key
|
14
|
+
@config = config || LocalEvaluationConfig.new
|
15
|
+
@cache = InMemoryFlagConfigCache.new(@config.bootstrap)
|
16
|
+
@logger = Logger.new($stdout)
|
17
|
+
@logger.level = if @config.debug
|
18
|
+
Logger::DEBUG
|
19
|
+
else
|
20
|
+
Logger::INFO
|
21
|
+
end
|
22
|
+
@fetcher = LocalEvaluationFetcher.new(api_key, @config.debug, @config.server_url)
|
23
|
+
@poller = FlagConfigPoller.new(@fetcher, @cache, @config.debug)
|
24
|
+
|
25
|
+
raise ArgumentError, 'Experiment API key is empty' if @api_key.nil? || @api_key.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Locally evaluates flag variants for a user.
|
29
|
+
#
|
30
|
+
# @param [User] user The user to evaluate
|
31
|
+
# @param [String[]] flag_keys The flags to evaluate with the user. If empty, all flags from the flag cache are evaluated
|
32
|
+
#
|
33
|
+
# @return [Hash[String, Variant]] The evaluated variants
|
34
|
+
def evaluate(user, flag_keys = [])
|
35
|
+
flag_configs = []
|
36
|
+
if flag_keys.empty?
|
37
|
+
@cache.cache.each do |_, value|
|
38
|
+
flag_configs.push(value)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
flag_configs = get_flag_configs(flag_keys)
|
42
|
+
end
|
43
|
+
flag_configs_str = flag_configs.to_json
|
44
|
+
user_str = user.to_json
|
45
|
+
@logger.debug("[Experiment] Evaluate: User: #{user_str} - Rules: #{flag_configs_str}") if @config.debug
|
46
|
+
result_json = evaluation(flag_configs_str, user_str)
|
47
|
+
@logger.debug(`[Experiment] evaluate - result: #{variants}`) if @config.debug
|
48
|
+
result = JSON.parse(result_json)
|
49
|
+
variants = {}
|
50
|
+
result.each do |key, value|
|
51
|
+
next if value['isDefaultVariant']
|
52
|
+
|
53
|
+
variant_key = value['variant']['key']
|
54
|
+
variant_payload = value['variant']['payload']
|
55
|
+
variants.store(key, Variant.new(variant_key, variant_payload))
|
56
|
+
end
|
57
|
+
variants
|
58
|
+
end
|
59
|
+
|
60
|
+
# Fetch initial flag configurations and start polling for updates.
|
61
|
+
# You must call this function to begin polling for flag config updates.
|
62
|
+
def start
|
63
|
+
@poller.start
|
64
|
+
end
|
65
|
+
|
66
|
+
# Stop polling for flag configurations. Close resource like connection pool with client
|
67
|
+
def stop
|
68
|
+
@poller.stop
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def get_flag_configs(flag_keys = [])
|
74
|
+
return @cache.cache if flag_keys.empty?
|
75
|
+
|
76
|
+
flag_configs = []
|
77
|
+
flag_keys.each do |key|
|
78
|
+
flag_config = @cache.get(key)
|
79
|
+
flag_configs.push(flag_config) if flag_config
|
80
|
+
end
|
81
|
+
flag_configs
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module AmplitudeExperiment
|
2
|
+
# LocalEvaluationConfig
|
3
|
+
class LocalEvaluationConfig
|
4
|
+
# Default server url
|
5
|
+
DEFAULT_SERVER_URL = 'https://api.lab.amplitude.com'.freeze
|
6
|
+
|
7
|
+
# Set to true to log some extra information to the console.
|
8
|
+
# @return [Boolean] the value of debug
|
9
|
+
attr_accessor :debug
|
10
|
+
|
11
|
+
# The server endpoint from which to request variants.
|
12
|
+
# @return [String] the value of server url
|
13
|
+
attr_accessor :server_url
|
14
|
+
|
15
|
+
# The server endpoint from which to request variants.
|
16
|
+
# @return [Hash] the value of bootstrap
|
17
|
+
attr_accessor :bootstrap
|
18
|
+
|
19
|
+
# The server endpoint from which to request variants.
|
20
|
+
# @return [long] the value of flag config polling interval in million seconds
|
21
|
+
attr_accessor :flag_config_polling_interval_millis
|
22
|
+
|
23
|
+
# @param [Boolean] debug Set to true to log some extra information to the console.
|
24
|
+
# @param [String] server_url The server endpoint from which to request variants.
|
25
|
+
# @param [Hash] bootstrap The value of bootstrap.
|
26
|
+
# @param [long] flag_config_polling_interval_millis The value of flag config polling interval in million seconds.
|
27
|
+
def initialize(server_url = DEFAULT_SERVER_URL, bootstrap = {},
|
28
|
+
flag_config_polling_interval_millis = 30_000, debug: false)
|
29
|
+
@debug = debug || false
|
30
|
+
@server_url = server_url
|
31
|
+
@bootstrap = bootstrap
|
32
|
+
@flag_config_polling_interval_millis = flag_config_polling_interval_millis
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# rubocop:disable all
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
# The evaluation wrapper
|
5
|
+
module EvaluationInterop
|
6
|
+
extend FFI::Library
|
7
|
+
host_os = RbConfig::CONFIG['host_os']
|
8
|
+
cpu = RbConfig::CONFIG['host_cpu']
|
9
|
+
evaluation_dir = File.dirname(__FILE__)
|
10
|
+
ffi_lib ["#{evaluation_dir}/lib/macosX64/libevaluation_interop.dylib"] if host_os =~ /darwin|mac os/ && cpu =~ /x86_64/
|
11
|
+
ffi_lib ["#{evaluation_dir}/lib/macosArm64/libevaluation_interop.dylib"] if host_os =~ /darwin|mac os/ && cpu =~ /arm64/
|
12
|
+
ffi_lib ["#{evaluation_dir}/lib/linuxX64/libevaluation_interop.so"] if host_os =~ /linux/ && cpu =~ /x86_64/
|
13
|
+
ffi_lib ["#{evaluation_dir}/lib/linuxArm64/libevaluation_interop.so"] if host_os =~ /linux/ && cpu =~ /arm64/
|
14
|
+
|
15
|
+
class Root < FFI::Struct
|
16
|
+
layout :evaluate, callback([:string, :string], :pointer)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Kotlin < FFI::Struct
|
20
|
+
layout :root, Root
|
21
|
+
end
|
22
|
+
|
23
|
+
class Libevaluation_interop_ExportedSymbols < FFI::Struct
|
24
|
+
layout :DisposeStablePointer, callback([:pointer], :void),
|
25
|
+
:DisposeString, callback([:string], :void),
|
26
|
+
:IsInstance, callback([:pointer, :string], :pointer),
|
27
|
+
:createNullableByte, callback([:string], :pointer),
|
28
|
+
:createNullableShort, callback([:pointer], :pointer),
|
29
|
+
:createNullableInt, callback([:pointer], :pointer),
|
30
|
+
:createNullableLong, callback([:pointer], :pointer),
|
31
|
+
:createNullableFloat, callback([:pointer], :pointer),
|
32
|
+
:createNullableDouble, callback([:pointer], :pointer),
|
33
|
+
:createNullableChar, callback([:pointer], :pointer),
|
34
|
+
:createNullableBoolean, callback([:pointer], :pointer),
|
35
|
+
:createNullableUnit, callback([], :pointer),
|
36
|
+
:kotlin, Kotlin
|
37
|
+
end
|
38
|
+
|
39
|
+
attach_function :libevaluation_interop_symbols, [], Libevaluation_interop_ExportedSymbols.by_ref
|
40
|
+
end
|
41
|
+
|
42
|
+
def evaluation(rule_json, user_json)
|
43
|
+
lib = EvaluationInterop.libevaluation_interop_symbols()
|
44
|
+
fn = lib[:kotlin][:root][:evaluate]
|
45
|
+
evaluation_result = fn.call(rule_json, user_json)
|
46
|
+
evaluation_result.read_string
|
47
|
+
end
|
48
|
+
# rubocop:disable all
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module AmplitudeExperiment
|
2
|
+
# LocalEvaluationFetcher
|
3
|
+
# Fetch local evaluation mode flag configs from the Experiment API server.
|
4
|
+
# These flag configs can be used to perform local evaluation.
|
5
|
+
class LocalEvaluationFetcher
|
6
|
+
FLAG_CONFIG_TIMEOUT = 5000
|
7
|
+
|
8
|
+
def initialize(api_key, debug, server_url = 'https://api.lab.amplitude.com')
|
9
|
+
@api_key = api_key
|
10
|
+
@uri = "#{server_url}/sdk/rules?eval_mode=local"
|
11
|
+
@debug = debug
|
12
|
+
@http = PersistentHttpClient.get(@uri, { read_timeout: FLAG_CONFIG_TIMEOUT })
|
13
|
+
end
|
14
|
+
|
15
|
+
# Fetch local evaluation mode flag configs from the Experiment API server.
|
16
|
+
# These flag configs can be used to perform local evaluation.
|
17
|
+
#
|
18
|
+
# @return [Hash] The flag configs
|
19
|
+
def fetch
|
20
|
+
# fetch flag_configs
|
21
|
+
headers = {
|
22
|
+
'Authorization' => "Api-Key #{@api_key}",
|
23
|
+
'Content-Type' => 'application/json;charset=utf-8'
|
24
|
+
}
|
25
|
+
request = Net::HTTP::Get.new(@uri, headers)
|
26
|
+
response = @http.request(request)
|
27
|
+
raise `flagConfigs - received error response: #{response.status}: #{response.body}` unless response.is_a?(Net::HTTPOK)
|
28
|
+
|
29
|
+
flag_configs = parse(response.body)
|
30
|
+
@logger.debug("[Experiment] Fetch flag configs: #{request.body}") if @debug
|
31
|
+
flag_configs
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse(flag_configs_str)
|
37
|
+
flag_config_obj = {}
|
38
|
+
flag_configs_array = JSON.parse(flag_configs_str)
|
39
|
+
flag_configs_array.each do |flag_config|
|
40
|
+
flag_config_obj.store(flag_config['flagKey'], flag_config) if flag_config.key?('flagKey')
|
41
|
+
end
|
42
|
+
flag_config_obj
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module AmplitudeExperiment
|
2
|
+
# FlagConfigPoller
|
3
|
+
# update the flag_config cache value
|
4
|
+
class FlagConfigPoller
|
5
|
+
FLAG_CONFIG_POLLING_INTERVAL_MILLIS = 30_000
|
6
|
+
|
7
|
+
def initialize(fetcher, cache, debug, poll_interval_millis: FLAG_CONFIG_POLLING_INTERVAL_MILLIS)
|
8
|
+
@fetcher = fetcher
|
9
|
+
@cache = cache
|
10
|
+
@poll_interval_millis = poll_interval_millis
|
11
|
+
@logger = Logger.new($stdout)
|
12
|
+
@debug = debug
|
13
|
+
@poller_thread = nil
|
14
|
+
@is_running = false
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fetch initial flag configurations and start polling for updates.
|
18
|
+
# You must call this function to begin polling for flag config updates.
|
19
|
+
# Calling this function while the poller is already running does nothing.
|
20
|
+
def start
|
21
|
+
return if @is_running
|
22
|
+
|
23
|
+
@logger.debug('[Experiment] poller - start') if @debug
|
24
|
+
run
|
25
|
+
end
|
26
|
+
|
27
|
+
# Stop polling for flag configurations.
|
28
|
+
# Calling this function while the poller is not running will do nothing.
|
29
|
+
def stop
|
30
|
+
@poller_thread&.exit
|
31
|
+
@is_running = false
|
32
|
+
@poller_thread = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def run
|
38
|
+
@is_running = true
|
39
|
+
flag_configs = @fetcher.fetch
|
40
|
+
@cache.clear
|
41
|
+
@cache.put_all(flag_configs)
|
42
|
+
@poller_thread = Thread.new do
|
43
|
+
sleep @poll_interval_millis
|
44
|
+
run
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -5,14 +5,14 @@ require 'logger'
|
|
5
5
|
|
6
6
|
module AmplitudeExperiment
|
7
7
|
# Main client for fetching variant data.
|
8
|
-
class
|
8
|
+
class RemoteEvaluationClient
|
9
9
|
# Creates a new Experiment Client instance.
|
10
10
|
#
|
11
11
|
# @param [String] api_key The environment API Key
|
12
12
|
# @param [Config] config
|
13
13
|
def initialize(api_key, config = nil)
|
14
14
|
@api_key = api_key
|
15
|
-
@config = config ||
|
15
|
+
@config = config || RemoteEvaluationConfig.new
|
16
16
|
@logger = Logger.new($stdout)
|
17
17
|
@logger.level = if @config.debug
|
18
18
|
Logger::DEBUG
|
data/lib/experiment/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amplitude-experiment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.beta.
|
4
|
+
version: 1.0.0.beta.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amplitude
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0.9'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: ffi
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.15.5
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.15.5
|
111
125
|
description: Amplitude Experiment Ruby Server SDK
|
112
126
|
email:
|
113
127
|
- sdk@amplitude.com
|
@@ -119,11 +133,17 @@ files:
|
|
119
133
|
- Gemfile
|
120
134
|
- README.md
|
121
135
|
- lib/amplitude-experiment.rb
|
122
|
-
- lib/experiment/client.rb
|
123
|
-
- lib/experiment/config.rb
|
124
136
|
- lib/experiment/cookie.rb
|
125
137
|
- lib/experiment/factory.rb
|
138
|
+
- lib/experiment/local/cache.rb
|
139
|
+
- lib/experiment/local/client.rb
|
140
|
+
- lib/experiment/local/config.rb
|
141
|
+
- lib/experiment/local/evaluation/evaluation.rb
|
142
|
+
- lib/experiment/local/fetcher.rb
|
143
|
+
- lib/experiment/local/poller.rb
|
126
144
|
- lib/experiment/persistent_http_client.rb
|
145
|
+
- lib/experiment/remote/client.rb
|
146
|
+
- lib/experiment/remote/config.rb
|
127
147
|
- lib/experiment/user.rb
|
128
148
|
- lib/experiment/variant.rb
|
129
149
|
- lib/experiment/version.rb
|