amplitude-experiment 1.0.0.beta.6 → 1.0.0.beta.9
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/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/evaluation/lib/linuxArm64/libevaluation_interop.so +0 -0
- data/lib/experiment/local/evaluation/lib/linuxArm64/libevaluation_interop_api.h +82 -0
- data/lib/experiment/local/evaluation/lib/linuxX64/libevaluation_interop.so +0 -0
- data/lib/experiment/local/evaluation/lib/linuxX64/libevaluation_interop_api.h +82 -0
- data/lib/experiment/local/evaluation/lib/macosArm64/libevaluation_interop.dylib +0 -0
- data/lib/experiment/local/evaluation/lib/macosArm64/libevaluation_interop_api.h +82 -0
- data/lib/experiment/local/evaluation/lib/macosX64/libevaluation_interop.dylib +0 -0
- data/lib/experiment/local/evaluation/lib/macosX64/libevaluation_interop_api.h +82 -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 +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ca6e5960f24ff9df262ce2dabff4b487e3cf26dbd1a6a48010da199fcf0ee63
|
4
|
+
data.tar.gz: b368a468d27f7d739b34272f20175049e839b4ba3ba620eb2ce229a829e9fa3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdb8f048df75ec0f1a55e2adb0191bd1c9c58f6c4137a0d05d5c32b141090935ceba034f6ff7f2dbd534d029c1620a800c01944ad688f2921fc265d279327d57
|
7
|
+
data.tar.gz: 71b1cdadbab86a9f77e3a6f4cd55064128d3194029c095473e838e77ad2bcbc5b2410065c182f05378ced43a4803d03d4856d99096b230b17ae6817fe91bc0e5
|
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,82 @@
|
|
1
|
+
#ifndef KONAN_LIBEVALUATION_INTEROP_H
|
2
|
+
#define KONAN_LIBEVALUATION_INTEROP_H
|
3
|
+
#ifdef __cplusplus
|
4
|
+
extern "C" {
|
5
|
+
#endif
|
6
|
+
#ifdef __cplusplus
|
7
|
+
typedef bool libevaluation_interop_KBoolean;
|
8
|
+
#else
|
9
|
+
typedef _Bool libevaluation_interop_KBoolean;
|
10
|
+
#endif
|
11
|
+
typedef unsigned short libevaluation_interop_KChar;
|
12
|
+
typedef signed char libevaluation_interop_KByte;
|
13
|
+
typedef short libevaluation_interop_KShort;
|
14
|
+
typedef int libevaluation_interop_KInt;
|
15
|
+
typedef long long libevaluation_interop_KLong;
|
16
|
+
typedef unsigned char libevaluation_interop_KUByte;
|
17
|
+
typedef unsigned short libevaluation_interop_KUShort;
|
18
|
+
typedef unsigned int libevaluation_interop_KUInt;
|
19
|
+
typedef unsigned long long libevaluation_interop_KULong;
|
20
|
+
typedef float libevaluation_interop_KFloat;
|
21
|
+
typedef double libevaluation_interop_KDouble;
|
22
|
+
typedef float __attribute__ ((__vector_size__ (16))) libevaluation_interop_KVector128;
|
23
|
+
typedef void* libevaluation_interop_KNativePtr;
|
24
|
+
struct libevaluation_interop_KType;
|
25
|
+
typedef struct libevaluation_interop_KType libevaluation_interop_KType;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
libevaluation_interop_KNativePtr pinned;
|
29
|
+
} libevaluation_interop_kref_kotlin_Byte;
|
30
|
+
typedef struct {
|
31
|
+
libevaluation_interop_KNativePtr pinned;
|
32
|
+
} libevaluation_interop_kref_kotlin_Short;
|
33
|
+
typedef struct {
|
34
|
+
libevaluation_interop_KNativePtr pinned;
|
35
|
+
} libevaluation_interop_kref_kotlin_Int;
|
36
|
+
typedef struct {
|
37
|
+
libevaluation_interop_KNativePtr pinned;
|
38
|
+
} libevaluation_interop_kref_kotlin_Long;
|
39
|
+
typedef struct {
|
40
|
+
libevaluation_interop_KNativePtr pinned;
|
41
|
+
} libevaluation_interop_kref_kotlin_Float;
|
42
|
+
typedef struct {
|
43
|
+
libevaluation_interop_KNativePtr pinned;
|
44
|
+
} libevaluation_interop_kref_kotlin_Double;
|
45
|
+
typedef struct {
|
46
|
+
libevaluation_interop_KNativePtr pinned;
|
47
|
+
} libevaluation_interop_kref_kotlin_Char;
|
48
|
+
typedef struct {
|
49
|
+
libevaluation_interop_KNativePtr pinned;
|
50
|
+
} libevaluation_interop_kref_kotlin_Boolean;
|
51
|
+
typedef struct {
|
52
|
+
libevaluation_interop_KNativePtr pinned;
|
53
|
+
} libevaluation_interop_kref_kotlin_Unit;
|
54
|
+
|
55
|
+
|
56
|
+
typedef struct {
|
57
|
+
/* Service functions. */
|
58
|
+
void (*DisposeStablePointer)(libevaluation_interop_KNativePtr ptr);
|
59
|
+
void (*DisposeString)(const char* string);
|
60
|
+
libevaluation_interop_KBoolean (*IsInstance)(libevaluation_interop_KNativePtr ref, const libevaluation_interop_KType* type);
|
61
|
+
libevaluation_interop_kref_kotlin_Byte (*createNullableByte)(libevaluation_interop_KByte);
|
62
|
+
libevaluation_interop_kref_kotlin_Short (*createNullableShort)(libevaluation_interop_KShort);
|
63
|
+
libevaluation_interop_kref_kotlin_Int (*createNullableInt)(libevaluation_interop_KInt);
|
64
|
+
libevaluation_interop_kref_kotlin_Long (*createNullableLong)(libevaluation_interop_KLong);
|
65
|
+
libevaluation_interop_kref_kotlin_Float (*createNullableFloat)(libevaluation_interop_KFloat);
|
66
|
+
libevaluation_interop_kref_kotlin_Double (*createNullableDouble)(libevaluation_interop_KDouble);
|
67
|
+
libevaluation_interop_kref_kotlin_Char (*createNullableChar)(libevaluation_interop_KChar);
|
68
|
+
libevaluation_interop_kref_kotlin_Boolean (*createNullableBoolean)(libevaluation_interop_KBoolean);
|
69
|
+
libevaluation_interop_kref_kotlin_Unit (*createNullableUnit)(void);
|
70
|
+
|
71
|
+
/* User functions. */
|
72
|
+
struct {
|
73
|
+
struct {
|
74
|
+
const char* (*evaluate)(const char* rules, const char* user);
|
75
|
+
} root;
|
76
|
+
} kotlin;
|
77
|
+
} libevaluation_interop_ExportedSymbols;
|
78
|
+
extern libevaluation_interop_ExportedSymbols* libevaluation_interop_symbols(void);
|
79
|
+
#ifdef __cplusplus
|
80
|
+
} /* extern "C" */
|
81
|
+
#endif
|
82
|
+
#endif /* KONAN_LIBEVALUATION_INTEROP_H */
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef KONAN_LIBEVALUATION_INTEROP_H
|
2
|
+
#define KONAN_LIBEVALUATION_INTEROP_H
|
3
|
+
#ifdef __cplusplus
|
4
|
+
extern "C" {
|
5
|
+
#endif
|
6
|
+
#ifdef __cplusplus
|
7
|
+
typedef bool libevaluation_interop_KBoolean;
|
8
|
+
#else
|
9
|
+
typedef _Bool libevaluation_interop_KBoolean;
|
10
|
+
#endif
|
11
|
+
typedef unsigned short libevaluation_interop_KChar;
|
12
|
+
typedef signed char libevaluation_interop_KByte;
|
13
|
+
typedef short libevaluation_interop_KShort;
|
14
|
+
typedef int libevaluation_interop_KInt;
|
15
|
+
typedef long long libevaluation_interop_KLong;
|
16
|
+
typedef unsigned char libevaluation_interop_KUByte;
|
17
|
+
typedef unsigned short libevaluation_interop_KUShort;
|
18
|
+
typedef unsigned int libevaluation_interop_KUInt;
|
19
|
+
typedef unsigned long long libevaluation_interop_KULong;
|
20
|
+
typedef float libevaluation_interop_KFloat;
|
21
|
+
typedef double libevaluation_interop_KDouble;
|
22
|
+
typedef float __attribute__ ((__vector_size__ (16))) libevaluation_interop_KVector128;
|
23
|
+
typedef void* libevaluation_interop_KNativePtr;
|
24
|
+
struct libevaluation_interop_KType;
|
25
|
+
typedef struct libevaluation_interop_KType libevaluation_interop_KType;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
libevaluation_interop_KNativePtr pinned;
|
29
|
+
} libevaluation_interop_kref_kotlin_Byte;
|
30
|
+
typedef struct {
|
31
|
+
libevaluation_interop_KNativePtr pinned;
|
32
|
+
} libevaluation_interop_kref_kotlin_Short;
|
33
|
+
typedef struct {
|
34
|
+
libevaluation_interop_KNativePtr pinned;
|
35
|
+
} libevaluation_interop_kref_kotlin_Int;
|
36
|
+
typedef struct {
|
37
|
+
libevaluation_interop_KNativePtr pinned;
|
38
|
+
} libevaluation_interop_kref_kotlin_Long;
|
39
|
+
typedef struct {
|
40
|
+
libevaluation_interop_KNativePtr pinned;
|
41
|
+
} libevaluation_interop_kref_kotlin_Float;
|
42
|
+
typedef struct {
|
43
|
+
libevaluation_interop_KNativePtr pinned;
|
44
|
+
} libevaluation_interop_kref_kotlin_Double;
|
45
|
+
typedef struct {
|
46
|
+
libevaluation_interop_KNativePtr pinned;
|
47
|
+
} libevaluation_interop_kref_kotlin_Char;
|
48
|
+
typedef struct {
|
49
|
+
libevaluation_interop_KNativePtr pinned;
|
50
|
+
} libevaluation_interop_kref_kotlin_Boolean;
|
51
|
+
typedef struct {
|
52
|
+
libevaluation_interop_KNativePtr pinned;
|
53
|
+
} libevaluation_interop_kref_kotlin_Unit;
|
54
|
+
|
55
|
+
|
56
|
+
typedef struct {
|
57
|
+
/* Service functions. */
|
58
|
+
void (*DisposeStablePointer)(libevaluation_interop_KNativePtr ptr);
|
59
|
+
void (*DisposeString)(const char* string);
|
60
|
+
libevaluation_interop_KBoolean (*IsInstance)(libevaluation_interop_KNativePtr ref, const libevaluation_interop_KType* type);
|
61
|
+
libevaluation_interop_kref_kotlin_Byte (*createNullableByte)(libevaluation_interop_KByte);
|
62
|
+
libevaluation_interop_kref_kotlin_Short (*createNullableShort)(libevaluation_interop_KShort);
|
63
|
+
libevaluation_interop_kref_kotlin_Int (*createNullableInt)(libevaluation_interop_KInt);
|
64
|
+
libevaluation_interop_kref_kotlin_Long (*createNullableLong)(libevaluation_interop_KLong);
|
65
|
+
libevaluation_interop_kref_kotlin_Float (*createNullableFloat)(libevaluation_interop_KFloat);
|
66
|
+
libevaluation_interop_kref_kotlin_Double (*createNullableDouble)(libevaluation_interop_KDouble);
|
67
|
+
libevaluation_interop_kref_kotlin_Char (*createNullableChar)(libevaluation_interop_KChar);
|
68
|
+
libevaluation_interop_kref_kotlin_Boolean (*createNullableBoolean)(libevaluation_interop_KBoolean);
|
69
|
+
libevaluation_interop_kref_kotlin_Unit (*createNullableUnit)(void);
|
70
|
+
|
71
|
+
/* User functions. */
|
72
|
+
struct {
|
73
|
+
struct {
|
74
|
+
const char* (*evaluate)(const char* rules, const char* user);
|
75
|
+
} root;
|
76
|
+
} kotlin;
|
77
|
+
} libevaluation_interop_ExportedSymbols;
|
78
|
+
extern libevaluation_interop_ExportedSymbols* libevaluation_interop_symbols(void);
|
79
|
+
#ifdef __cplusplus
|
80
|
+
} /* extern "C" */
|
81
|
+
#endif
|
82
|
+
#endif /* KONAN_LIBEVALUATION_INTEROP_H */
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef KONAN_LIBEVALUATION_INTEROP_H
|
2
|
+
#define KONAN_LIBEVALUATION_INTEROP_H
|
3
|
+
#ifdef __cplusplus
|
4
|
+
extern "C" {
|
5
|
+
#endif
|
6
|
+
#ifdef __cplusplus
|
7
|
+
typedef bool libevaluation_interop_KBoolean;
|
8
|
+
#else
|
9
|
+
typedef _Bool libevaluation_interop_KBoolean;
|
10
|
+
#endif
|
11
|
+
typedef unsigned short libevaluation_interop_KChar;
|
12
|
+
typedef signed char libevaluation_interop_KByte;
|
13
|
+
typedef short libevaluation_interop_KShort;
|
14
|
+
typedef int libevaluation_interop_KInt;
|
15
|
+
typedef long long libevaluation_interop_KLong;
|
16
|
+
typedef unsigned char libevaluation_interop_KUByte;
|
17
|
+
typedef unsigned short libevaluation_interop_KUShort;
|
18
|
+
typedef unsigned int libevaluation_interop_KUInt;
|
19
|
+
typedef unsigned long long libevaluation_interop_KULong;
|
20
|
+
typedef float libevaluation_interop_KFloat;
|
21
|
+
typedef double libevaluation_interop_KDouble;
|
22
|
+
typedef float __attribute__ ((__vector_size__ (16))) libevaluation_interop_KVector128;
|
23
|
+
typedef void* libevaluation_interop_KNativePtr;
|
24
|
+
struct libevaluation_interop_KType;
|
25
|
+
typedef struct libevaluation_interop_KType libevaluation_interop_KType;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
libevaluation_interop_KNativePtr pinned;
|
29
|
+
} libevaluation_interop_kref_kotlin_Byte;
|
30
|
+
typedef struct {
|
31
|
+
libevaluation_interop_KNativePtr pinned;
|
32
|
+
} libevaluation_interop_kref_kotlin_Short;
|
33
|
+
typedef struct {
|
34
|
+
libevaluation_interop_KNativePtr pinned;
|
35
|
+
} libevaluation_interop_kref_kotlin_Int;
|
36
|
+
typedef struct {
|
37
|
+
libevaluation_interop_KNativePtr pinned;
|
38
|
+
} libevaluation_interop_kref_kotlin_Long;
|
39
|
+
typedef struct {
|
40
|
+
libevaluation_interop_KNativePtr pinned;
|
41
|
+
} libevaluation_interop_kref_kotlin_Float;
|
42
|
+
typedef struct {
|
43
|
+
libevaluation_interop_KNativePtr pinned;
|
44
|
+
} libevaluation_interop_kref_kotlin_Double;
|
45
|
+
typedef struct {
|
46
|
+
libevaluation_interop_KNativePtr pinned;
|
47
|
+
} libevaluation_interop_kref_kotlin_Char;
|
48
|
+
typedef struct {
|
49
|
+
libevaluation_interop_KNativePtr pinned;
|
50
|
+
} libevaluation_interop_kref_kotlin_Boolean;
|
51
|
+
typedef struct {
|
52
|
+
libevaluation_interop_KNativePtr pinned;
|
53
|
+
} libevaluation_interop_kref_kotlin_Unit;
|
54
|
+
|
55
|
+
|
56
|
+
typedef struct {
|
57
|
+
/* Service functions. */
|
58
|
+
void (*DisposeStablePointer)(libevaluation_interop_KNativePtr ptr);
|
59
|
+
void (*DisposeString)(const char* string);
|
60
|
+
libevaluation_interop_KBoolean (*IsInstance)(libevaluation_interop_KNativePtr ref, const libevaluation_interop_KType* type);
|
61
|
+
libevaluation_interop_kref_kotlin_Byte (*createNullableByte)(libevaluation_interop_KByte);
|
62
|
+
libevaluation_interop_kref_kotlin_Short (*createNullableShort)(libevaluation_interop_KShort);
|
63
|
+
libevaluation_interop_kref_kotlin_Int (*createNullableInt)(libevaluation_interop_KInt);
|
64
|
+
libevaluation_interop_kref_kotlin_Long (*createNullableLong)(libevaluation_interop_KLong);
|
65
|
+
libevaluation_interop_kref_kotlin_Float (*createNullableFloat)(libevaluation_interop_KFloat);
|
66
|
+
libevaluation_interop_kref_kotlin_Double (*createNullableDouble)(libevaluation_interop_KDouble);
|
67
|
+
libevaluation_interop_kref_kotlin_Char (*createNullableChar)(libevaluation_interop_KChar);
|
68
|
+
libevaluation_interop_kref_kotlin_Boolean (*createNullableBoolean)(libevaluation_interop_KBoolean);
|
69
|
+
libevaluation_interop_kref_kotlin_Unit (*createNullableUnit)(void);
|
70
|
+
|
71
|
+
/* User functions. */
|
72
|
+
struct {
|
73
|
+
struct {
|
74
|
+
const char* (*evaluate)(const char* rules, const char* user);
|
75
|
+
} root;
|
76
|
+
} kotlin;
|
77
|
+
} libevaluation_interop_ExportedSymbols;
|
78
|
+
extern libevaluation_interop_ExportedSymbols* libevaluation_interop_symbols(void);
|
79
|
+
#ifdef __cplusplus
|
80
|
+
} /* extern "C" */
|
81
|
+
#endif
|
82
|
+
#endif /* KONAN_LIBEVALUATION_INTEROP_H */
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef KONAN_LIBEVALUATION_INTEROP_H
|
2
|
+
#define KONAN_LIBEVALUATION_INTEROP_H
|
3
|
+
#ifdef __cplusplus
|
4
|
+
extern "C" {
|
5
|
+
#endif
|
6
|
+
#ifdef __cplusplus
|
7
|
+
typedef bool libevaluation_interop_KBoolean;
|
8
|
+
#else
|
9
|
+
typedef _Bool libevaluation_interop_KBoolean;
|
10
|
+
#endif
|
11
|
+
typedef unsigned short libevaluation_interop_KChar;
|
12
|
+
typedef signed char libevaluation_interop_KByte;
|
13
|
+
typedef short libevaluation_interop_KShort;
|
14
|
+
typedef int libevaluation_interop_KInt;
|
15
|
+
typedef long long libevaluation_interop_KLong;
|
16
|
+
typedef unsigned char libevaluation_interop_KUByte;
|
17
|
+
typedef unsigned short libevaluation_interop_KUShort;
|
18
|
+
typedef unsigned int libevaluation_interop_KUInt;
|
19
|
+
typedef unsigned long long libevaluation_interop_KULong;
|
20
|
+
typedef float libevaluation_interop_KFloat;
|
21
|
+
typedef double libevaluation_interop_KDouble;
|
22
|
+
typedef float __attribute__ ((__vector_size__ (16))) libevaluation_interop_KVector128;
|
23
|
+
typedef void* libevaluation_interop_KNativePtr;
|
24
|
+
struct libevaluation_interop_KType;
|
25
|
+
typedef struct libevaluation_interop_KType libevaluation_interop_KType;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
libevaluation_interop_KNativePtr pinned;
|
29
|
+
} libevaluation_interop_kref_kotlin_Byte;
|
30
|
+
typedef struct {
|
31
|
+
libevaluation_interop_KNativePtr pinned;
|
32
|
+
} libevaluation_interop_kref_kotlin_Short;
|
33
|
+
typedef struct {
|
34
|
+
libevaluation_interop_KNativePtr pinned;
|
35
|
+
} libevaluation_interop_kref_kotlin_Int;
|
36
|
+
typedef struct {
|
37
|
+
libevaluation_interop_KNativePtr pinned;
|
38
|
+
} libevaluation_interop_kref_kotlin_Long;
|
39
|
+
typedef struct {
|
40
|
+
libevaluation_interop_KNativePtr pinned;
|
41
|
+
} libevaluation_interop_kref_kotlin_Float;
|
42
|
+
typedef struct {
|
43
|
+
libevaluation_interop_KNativePtr pinned;
|
44
|
+
} libevaluation_interop_kref_kotlin_Double;
|
45
|
+
typedef struct {
|
46
|
+
libevaluation_interop_KNativePtr pinned;
|
47
|
+
} libevaluation_interop_kref_kotlin_Char;
|
48
|
+
typedef struct {
|
49
|
+
libevaluation_interop_KNativePtr pinned;
|
50
|
+
} libevaluation_interop_kref_kotlin_Boolean;
|
51
|
+
typedef struct {
|
52
|
+
libevaluation_interop_KNativePtr pinned;
|
53
|
+
} libevaluation_interop_kref_kotlin_Unit;
|
54
|
+
|
55
|
+
|
56
|
+
typedef struct {
|
57
|
+
/* Service functions. */
|
58
|
+
void (*DisposeStablePointer)(libevaluation_interop_KNativePtr ptr);
|
59
|
+
void (*DisposeString)(const char* string);
|
60
|
+
libevaluation_interop_KBoolean (*IsInstance)(libevaluation_interop_KNativePtr ref, const libevaluation_interop_KType* type);
|
61
|
+
libevaluation_interop_kref_kotlin_Byte (*createNullableByte)(libevaluation_interop_KByte);
|
62
|
+
libevaluation_interop_kref_kotlin_Short (*createNullableShort)(libevaluation_interop_KShort);
|
63
|
+
libevaluation_interop_kref_kotlin_Int (*createNullableInt)(libevaluation_interop_KInt);
|
64
|
+
libevaluation_interop_kref_kotlin_Long (*createNullableLong)(libevaluation_interop_KLong);
|
65
|
+
libevaluation_interop_kref_kotlin_Float (*createNullableFloat)(libevaluation_interop_KFloat);
|
66
|
+
libevaluation_interop_kref_kotlin_Double (*createNullableDouble)(libevaluation_interop_KDouble);
|
67
|
+
libevaluation_interop_kref_kotlin_Char (*createNullableChar)(libevaluation_interop_KChar);
|
68
|
+
libevaluation_interop_kref_kotlin_Boolean (*createNullableBoolean)(libevaluation_interop_KBoolean);
|
69
|
+
libevaluation_interop_kref_kotlin_Unit (*createNullableUnit)(void);
|
70
|
+
|
71
|
+
/* User functions. */
|
72
|
+
struct {
|
73
|
+
struct {
|
74
|
+
const char* (*evaluate)(const char* rules, const char* user);
|
75
|
+
} root;
|
76
|
+
} kotlin;
|
77
|
+
} libevaluation_interop_ExportedSymbols;
|
78
|
+
extern libevaluation_interop_ExportedSymbols* libevaluation_interop_symbols(void);
|
79
|
+
#ifdef __cplusplus
|
80
|
+
} /* extern "C" */
|
81
|
+
#endif
|
82
|
+
#endif /* KONAN_LIBEVALUATION_INTEROP_H */
|
@@ -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.code}: #{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.9
|
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-31 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,25 @@ 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/evaluation/lib/linuxArm64/libevaluation_interop.so
|
143
|
+
- lib/experiment/local/evaluation/lib/linuxArm64/libevaluation_interop_api.h
|
144
|
+
- lib/experiment/local/evaluation/lib/linuxX64/libevaluation_interop.so
|
145
|
+
- lib/experiment/local/evaluation/lib/linuxX64/libevaluation_interop_api.h
|
146
|
+
- lib/experiment/local/evaluation/lib/macosArm64/libevaluation_interop.dylib
|
147
|
+
- lib/experiment/local/evaluation/lib/macosArm64/libevaluation_interop_api.h
|
148
|
+
- lib/experiment/local/evaluation/lib/macosX64/libevaluation_interop.dylib
|
149
|
+
- lib/experiment/local/evaluation/lib/macosX64/libevaluation_interop_api.h
|
150
|
+
- lib/experiment/local/fetcher.rb
|
151
|
+
- lib/experiment/local/poller.rb
|
126
152
|
- lib/experiment/persistent_http_client.rb
|
153
|
+
- lib/experiment/remote/client.rb
|
154
|
+
- lib/experiment/remote/config.rb
|
127
155
|
- lib/experiment/user.rb
|
128
156
|
- lib/experiment/variant.rb
|
129
157
|
- lib/experiment/version.rb
|